From 229883654e1ecb9e7368a4b15dd5f3094fc0949f Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Wed, 23 Feb 2022 21:35:03 -0600 Subject: [PATCH 1/9] Initial pass at getting subtitle support in. Doesn't compile for unknown reasons --- .../plAudioCore/plAudioCore.vcxproj | 2 + .../plAudioCore/plAudioCore.vcxproj.filters | 6 + .../PubUtilLib/plMessage/plMessage.vcxproj | 1 + .../plMessage/plMessage.vcxproj.filters | 3 + Sources/Plasma/Apps/plClient/plClient.cpp | 1 + .../pfConsole/pfConsoleCommands.cpp | 5 + .../FeatureLib/pfPython/plPythonFileMod.cpp | 32 +++ .../FeatureLib/pfPython/plPythonFileMod.h | 1 + .../FeatureLib/pfPython/pyAudioControl.cpp | 16 ++ .../FeatureLib/pfPython/pyAudioControl.h | 5 + .../pfPython/pyAudioControlGlue.cpp | 11 + .../Plasma/NucleusLib/inc/plCreatableIndex.h | 80 ++++---- .../NucleusLib/pnModifier/plLogicModBase.h | 2 +- .../PubUtilLib/plAudio/plAudioSystem.cpp | 6 + .../Plasma/PubUtilLib/plAudio/plAudioSystem.h | 2 + .../PubUtilLib/plAudio/plWin32Sound.cpp | 29 ++- .../PubUtilLib/plAudioCore/plSoundBuffer.cpp | 27 ++- .../PubUtilLib/plAudioCore/plSoundBuffer.h | 6 +- .../plAudioCore/plSrtFileReader.cpp | 192 ++++++++++++++++++ .../PubUtilLib/plAudioCore/plSrtFileReader.h | 105 ++++++++++ .../PubUtilLib/plMessage/plMessageCreatable.h | 4 + .../PubUtilLib/plMessage/plSubtitleMsg.h | 82 ++++++++ 22 files changed, 579 insertions(+), 39 deletions(-) create mode 100644 Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp create mode 100644 Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h create mode 100644 Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h diff --git a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj index ad01030c..4ff0c775 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj +++ b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj @@ -244,6 +244,7 @@ %(PreprocessorDefinitions) %(PreprocessorDefinitions) + Disabled Disabled @@ -315,6 +316,7 @@ + diff --git a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters index 88ab2ab0..27a310a8 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters +++ b/Build/VS2010/Plasma/PubUtilLib/plAudioCore/plAudioCore.vcxproj.filters @@ -32,6 +32,9 @@ Source Files + + Source Files + @@ -61,5 +64,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj index 4b64cea6..21785e59 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj +++ b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj @@ -817,6 +817,7 @@ + diff --git a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters index f0e01926..c3423612 100644 --- a/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters +++ b/Build/VS2010/Plasma/PubUtilLib/plMessage/plMessage.vcxproj.filters @@ -361,5 +361,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 07a2ccb4..9d4d5566 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -2313,6 +2313,7 @@ void plClient::IDetectAudioVideoSettings() WriteInt(stream, "Audio.SetChannelVolume Ambience", 1); WriteInt(stream, "Audio.SetChannelVolume NPCVoice", 1); WriteInt(stream, "Audio.EnableVoiceRecording", 1); + WriteInt(stream, "Audio.EnableSubtitles", false); WriteString(stream, "Audio.SetDeviceName", deviceName ); stream->Close(); delete stream; diff --git a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp index 3834f3e0..c2a96a24 100644 --- a/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp +++ b/Sources/Plasma/FeatureLib/pfConsole/pfConsoleCommands.cpp @@ -3391,6 +3391,11 @@ PF_CONSOLE_CMD( Audio, MuteAll, "bool on", "Mute or unmute all sounds") plgAudioSys::SetMuted( (bool)params[ 0 ] ); } +PF_CONSOLE_CMD(Audio, EnableSubtitles, "bool on", "Enable or disable displaying subtitles for audio files containing speech") +{ + plgAudioSys::SetEnableSubtitles((bool)params[0]); +} + PF_CONSOLE_CMD( Audio, SetDistanceModel, "int type", "Sets the distance model for all 3d sounds") { if(plgAudioSys::Sys()) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index ca1ec5fb..cfc2dc90 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -94,6 +94,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "../pfGameMgr/pfGameMgr.h" #include "../plMessage/plAIMsg.h" #include "../plAvatar/plAvBrainCritter.h" +#include "../plMessage/plSubtitleMsg.h" #include "plProfile.h" @@ -189,6 +190,7 @@ char* plPythonFileMod::fFunctionNames[] = { "OnGameMgrMsg" }, // kfunc_OnGameMgrMsg { "OnGameCliMsg" }, // kfunc_OnGameCliMsg { "OnAIMsg" }, // kfunc_OnAIMsg + { "OnSubtitleMsg" }, // kfunc_OnSubtitleMsg { nil } }; @@ -2780,6 +2782,36 @@ hsBool plPythonFileMod::MsgReceive(plMessage* msg) } } + if (fPyFunctionInstances[kfunc_OnSubtitleMsg]) + { + plSubtitleMsg* pSubMsg = plSubtitleMsg::ConvertNoRef(msg); + if (pSubMsg) + { + plProfile_BeginTiming(PythonUpdate); + PyObject* retVal = PyObject_CallMethod( + fPyFunctionInstances[kfunc_OnSubtitleMsg], fFunctionNames[kfunc_OnSubtitleMsg], + "ss", pSubMsg->GetText(), pSubMsg->GetSpeaker()); + if (retVal == nil) + { +#ifndef PLASMA_EXTERNAL_RELEASE + // for some reason this function didn't, remember that and not call it again + fPyFunctionInstances[kfunc_OnSubtitleMsg] = nil; +#endif //PLASMA_EXTERNAL_RELEASE + // if there was an error make sure that the stderr gets flushed so it can be seen + ReportError(); + } + + Py_XDECREF(retVal); + plProfile_EndTiming(PythonUpdate); + + // display any output (NOTE: this would be disabled in production) + DisplayPythonOutput(); + + // we handled this message (I think) + return true; + } + } + return plModifier::MsgReceive(msg); } diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h index fc701a99..b781c717 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.h @@ -202,6 +202,7 @@ public: kfunc_OnGameMgrMsg, kfunc_OnGameCliMsg, kfunc_OnAIMsg, + kfunc_OnSubtitleMsg, kfunc_lastone }; // array of matching Python instance where the functions are, if defined diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp index bddad507..623f0106 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.cpp @@ -256,6 +256,22 @@ hsBool pyAudioControl::IsMuted() return plgAudioSys::IsMuted(); } +// Enable or disable displaying speech subtitles +void pyAudioControl::EnableSubtitles() +{ + plgAudioSys::SetEnableSubtitles(true); +} + +void pyAudioControl::DisableSubtitles() +{ + plgAudioSys::SetEnableSubtitles(false); +} + +bool pyAudioControl::AreSubtitlesEnabled() const +{ + return plgAudioSys::AreSubtitlesEnabled(); +} + hsBool pyAudioControl::SupportEAX(const char *deviceName) { return plgAudioSys::SupportsEAX(deviceName); diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h index 1bd2e435..3e20d460 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControl.h @@ -109,6 +109,11 @@ public: virtual void UnmuteAll(); virtual hsBool IsMuted(); + // Enable or disable displaying speech subtitles + void EnableSubtitles(); + void DisableSubtitles(); + bool AreSubtitlesEnabled() const; + virtual void SetAudioSystemMode(int mode); // sets the current mode virtual int GetAudioSystemMode(); // returns the current mode virtual int GetHighestAudioMode(); // returns the highest mode the card is capable of handling diff --git a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp index 7a886eb7..c0dfc11f 100644 --- a/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/pyAudioControlGlue.cpp @@ -242,6 +242,14 @@ PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, isMuted) PYTHON_RETURN_BOOL(self->fThis->IsMuted()); } +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, enableSubtitles, EnableSubtitles) +PYTHON_BASIC_METHOD_DEFINITION(ptAudioControl, disableSubtitles, DisableSubtitles) + +PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, areSubtitlesEnabled) +{ + PYTHON_RETURN_BOOL(self->fThis->AreSubtitlesEnabled()); +} + PYTHON_METHOD_DEFINITION_NOARGS(ptAudioControl, canSetMicLevel) { PYTHON_RETURN_BOOL(self->fThis->CanSetMicLevel()); @@ -484,6 +492,9 @@ PYTHON_START_METHODS_TABLE(ptAudioControl) PYTHON_BASIC_METHOD(ptAudioControl, muteAll, "Mutes all sounds."), PYTHON_BASIC_METHOD(ptAudioControl, unmuteAll, "Unmutes all sounds."), PYTHON_METHOD_NOARGS(ptAudioControl, isMuted, "Are all sounds muted? Returns 1 if true otherwise returns 0."), + PYTHON_BASIC_METHOD(ptAudioControl, enableSubtitles, "Enables audio subtitles."), + PYTHON_BASIC_METHOD(ptAudioControl, disableSubtitles, "Disables audio subtitles."), + PYTHON_METHOD_NOARGS(ptAudioControl, areSubtitlesEnabled, "Are audio subtitles enabled? Returns 1 if true otherwise returns 0."), PYTHON_METHOD_NOARGS(ptAudioControl, canSetMicLevel, "Can the microphone level be set? Returns 1 if true otherwise returns 0."), PYTHON_METHOD(ptAudioControl, setMicLevel, "Params: level\nSets the microphone recording level (0.0 to 1.0)."), PYTHON_METHOD_NOARGS(ptAudioControl, getMicLevel, "Returns the microphone recording level (0.0 to 1.0)."), diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index 75f0e728..a5c5b248 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -34,9 +34,9 @@ work. You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: - Cyan Worlds, Inc. - 14617 N Newport Hwy - Mead, WA 99021 + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 *==LICENSE==*/ @@ -46,19 +46,19 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plClassIndexMacros.h" // for CLASS_INDEX macro defn CLASS_INDEX_LIST_START - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- // hsKeyedObjects should appear in the first section of the enum list // And everything else in the next section // Otherwise you will get an Assert - //--------------------------------------------------------------------- + //--------------------------------------------------------------------- CLASS_INDEX(plSceneNode), - CLASS_INDEX(plSceneObject), + CLASS_INDEX(plSceneObject), CLASS_INDEX(hsKeyedObject), CLASS_INDEX(plBitmap), CLASS_INDEX(plMipmap), CLASS_INDEX(plCubicEnvironmap), - CLASS_INDEX(plLayer), - CLASS_INDEX(hsGMaterial), + CLASS_INDEX(plLayer), + CLASS_INDEX(hsGMaterial), CLASS_INDEX(plParticleSystem), CLASS_INDEX(plParticleEffect), CLASS_INDEX(plParticleCollisionEffectBeat), @@ -74,7 +74,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plWinAudible), CLASS_INDEX(plCoordinateInterface), CLASS_INDEX(plDrawInterface), - CLASS_INDEX(plDrawable), + CLASS_INDEX(plDrawable), CLASS_INDEX(plDrawableMesh), CLASS_INDEX(plDrawableIce), CLASS_INDEX(plPhysical), @@ -131,7 +131,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(UNUSED_plDrawablePatchSet), CLASS_INDEX(plInputManager), CLASS_INDEX(plLogicModBase), - CLASS_INDEX(plFogEnvironment), + CLASS_INDEX(plFogEnvironment), CLASS_INDEX(plNetApp), CLASS_INDEX(plNetClientMgr), CLASS_INDEX(pl2WayWinAudible), @@ -243,7 +243,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(pfGUIDynDisplayCtrl), CLASS_INDEX(UNUSED_plLayerProject), CLASS_INDEX(plInputInterfaceMgr), - CLASS_INDEX(plRailCameraMod), + CLASS_INDEX(plRailCameraMod), CLASS_INDEX(plMultistageBehMod), CLASS_INDEX(plCameraBrain1_Circle), CLASS_INDEX(plParticleWindEffect), @@ -368,14 +368,15 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plRidingAnimatedPhysicalDetector), CLASS_INDEX(plVolumeSensorConditionalObjectNoArbitration), CLASS_INDEX(plPXSubWorld), -//--------------------------------------------------------- -// Keyed objects above this line, unkeyed (such as messages) below.. -//--------------------------------------------------------- + CLASS_INDEX(pfConfirmationMgr), + //--------------------------------------------------------- + // Keyed objects above this line, unkeyed (such as messages) below.. + //--------------------------------------------------------- CLASS_INDEX_NONKEYED_OBJ_START - CLASS_INDEX(plObjRefMsg), + CLASS_INDEX(plObjRefMsg), CLASS_INDEX(plNodeRefMsg), - CLASS_INDEX(plMessage), + CLASS_INDEX(plMessage), CLASS_INDEX(plRefMsg), CLASS_INDEX(plGenRefMsg), CLASS_INDEX(plTimeMsg), @@ -411,12 +412,12 @@ CLASS_INDEX_LIST_START CLASS_INDEX(hsSfxObjDistFade), CLASS_INDEX(hsSfxObjDistShade), CLASS_INDEX(hsDynamicValue), - CLASS_INDEX(hsDynamicScalar), + CLASS_INDEX(hsDynamicScalar), CLASS_INDEX(hsDynamicColorRGBA), CLASS_INDEX(hsDynamicMatrix33), CLASS_INDEX(hsDynamicMatrix44), CLASS_INDEX(plOmniSqApplicator), - CLASS_INDEX(plPreResourceMsg), + CLASS_INDEX(plPreResourceMsg), CLASS_INDEX(UNUSED_hsDynamicColorRGBA), CLASS_INDEX(UNUSED_hsDynamicMatrix33), CLASS_INDEX(UNUSED_hsDynamicMatrix44), @@ -427,7 +428,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(UNUSED_plPosController), CLASS_INDEX(UNUSED_plScalarController), CLASS_INDEX(UNUSED_plPoint3Controller), - CLASS_INDEX(UNUSED_plScaleValueController), + CLASS_INDEX(UNUSED_plScaleValueController), CLASS_INDEX(UNUSED_plQuatController), CLASS_INDEX(UNUSED_plMatrix33Controller), CLASS_INDEX(UNUSED_plMatrix44Controller), @@ -437,7 +438,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plCompoundRotController), CLASS_INDEX(UNUSED_plSimplePosController), CLASS_INDEX(plCompoundPosController), - CLASS_INDEX(plTMController), + CLASS_INDEX(plTMController), CLASS_INDEX(hsFogControl), CLASS_INDEX(plIntRefMsg), CLASS_INDEX(plCollisionReactor), @@ -577,8 +578,8 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plNetServerMsgPing), CLASS_INDEX(plNetMsgAlive), CLASS_INDEX(plNetMsgTerminated), - CLASS_INDEX(plSDLModifierMsg), - CLASS_INDEX(plNetMsgSDLState), + CLASS_INDEX(plSDLModifierMsg), + CLASS_INDEX(plNetMsgSDLState), CLASS_INDEX(plNetServerMsgSessionReset), CLASS_INDEX(plCCRBanLinkingMsg), CLASS_INDEX(plCCRSilencePlayerMsg), @@ -780,17 +781,17 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plShadowCastMsg), CLASS_INDEX(plBoundsIsect), CLASS_INDEX(plResMgrHelperMsg), - CLASS_INDEX(plNetCommAuthMsg), - CLASS_INDEX(plNetCommFileListMsg), - CLASS_INDEX(plNetCommFileDownloadMsg), - CLASS_INDEX(plNetCommLinkToAgeMsg), - CLASS_INDEX(plNetCommPlayerListMsg), - CLASS_INDEX(plNetCommActivePlayerMsg), - CLASS_INDEX(plNetCommCreatePlayerMsg), - CLASS_INDEX(plNetCommDeletePlayerMsg), - CLASS_INDEX(plNetCommPublicAgeListMsg), - CLASS_INDEX(plNetCommPublicAgeMsg), - CLASS_INDEX(plNetCommRegisterAgeMsg), + CLASS_INDEX(plNetCommAuthMsg), + CLASS_INDEX(plNetCommFileListMsg), + CLASS_INDEX(plNetCommFileDownloadMsg), + CLASS_INDEX(plNetCommLinkToAgeMsg), + CLASS_INDEX(plNetCommPlayerListMsg), + CLASS_INDEX(plNetCommActivePlayerMsg), + CLASS_INDEX(plNetCommCreatePlayerMsg), + CLASS_INDEX(plNetCommDeletePlayerMsg), + CLASS_INDEX(plNetCommPublicAgeListMsg), + CLASS_INDEX(plNetCommPublicAgeMsg), + CLASS_INDEX(plNetCommRegisterAgeMsg), CLASS_INDEX(plVaultAdminInitializationTask), CLASS_INDEX(plMultistageModMsg), CLASS_INDEX(plSoundVolumeApplicator), @@ -948,6 +949,17 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plAngularVelocityMsg), CLASS_INDEX(plRideAnimatedPhysMsg), CLASS_INDEX(plAvBrainRideAnimatedPhysical), + CLASS_INDEX(pfGameScoreMsg), + CLASS_INDEX(pfGameScoreListMsg), + CLASS_INDEX(pfGameScoreTransferMsg), + CLASS_INDEX(pfGameScoreUpdateMsg), + CLASS_INDEX(plLoadClothingMsg), + CLASS_INDEX(plNullPipeline), + CLASS_INDEX(plGLPipeline), + CLASS_INDEX(plSDLModifierStateMsg), + CLASS_INDEX(plConfirmationMsg), + CLASS_INDEX(plLocalizedConfirmationMsg), + CLASS_INDEX(plSubtitleMsg), CLASS_INDEX_LIST_END -#endif // plCreatableIndex_inc +#endif // plCreatableIndex_inc \ No newline at end of file diff --git a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h index 908f5924..dd6f1100 100644 --- a/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h +++ b/Sources/Plasma/NucleusLib/pnModifier/plLogicModBase.h @@ -43,7 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef plLogicModBase_inc #define plLogicModBase_inc -#include +#include #include "plSingleModifier.h" #include "../pnNetCommon/plSynchedValue.h" #include "hsTemplates.h" diff --git a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp index c2da6da0..83a1d5b5 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.cpp @@ -994,6 +994,7 @@ hsBool plgAudioSys::fInit = false; hsBool plgAudioSys::fActive = false; hsBool plgAudioSys::fUseHardware = false; hsBool plgAudioSys::fMuted = true; +bool plgAudioSys::fEnableSubtitles = false; hsBool plgAudioSys::fDelayedActivate = false; hsBool plgAudioSys::fEnableEAX = false; hsWindowHndl plgAudioSys::fWnd = nil; @@ -1047,6 +1048,11 @@ void plgAudioSys::SetMuted( hsBool b ) SetGlobalFadeVolume(1.0); } +void plgAudioSys::SetEnableSubtitles(bool b) +{ + fEnableSubtitles = b; +} + void plgAudioSys::SetUseHardware(hsBool b) { fUseHardware = b; diff --git a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h index a376e9c7..43df875e 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h +++ b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h @@ -193,11 +193,13 @@ public: static void SetUseHardware(hsBool b); static void SetActive(hsBool b); static void SetMuted( hsBool b ); + static void SetEnableSubtitles(bool b); static void EnableEAX( hsBool b ); static hsBool Active() { return fInit; } static void Shutdown(); static void Activate(hsBool b); static hsBool IsMuted( void ) { return fMuted; } + static bool AreSubtitlesEnabled() { return fEnableSubtitles; } static hsWindowHndl hWnd() { return fWnd; } static plAudioSystem* Sys() { return fSys; } static void Restart( void ); diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp index a81a31b4..0eebdfbc 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp @@ -53,10 +53,12 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plWavFile.h" #include "../plAudible/plWinAudible.h" +#include "../plAudioCore/plSrtFileReader.h" +#include "../pnMessage/plEventCallbackMsg.h" +#include "../pnMessage/plSoundMsg.h" +#include "../plMessage/plSubtitleMsg.h" #include "../plNetMessage/plNetMessage.h" #include "../pnNetCommon/plNetApp.h" -#include "../pnMessage/plSoundMsg.h" -#include "../pnMessage/plEventCallbackMsg.h" #include "../plPipeline/plPlates.h" #include "../plStatusLog/plStatusLog.h" @@ -108,6 +110,18 @@ void plWin32Sound::IFreeBuffers( void ) void plWin32Sound::Update() { + plSoundBuffer* buf = GetDataBuffer(); + if (plgAudioSys::AreSubtitlesEnabled() && buf != nullptr) { + plSrtFileReader* srtReader = buf->GetSrtReader(); + if (srtReader != nullptr) { + while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime((uint32_t)(GetActualTimeSec() * 1000.0f))) { + // add a plSubtitleMsg to go... to whoever is listening (probably the KI) + plSubtitleMsg* msg = new plSubtitleMsg(nextEntry->GetSubtitleText(), nextEntry->GetSpeakerName()); + msg->Send(); + } + } + } + plSound::Update(); } @@ -120,6 +134,17 @@ void plWin32Sound::IActuallyPlay( void ) { if (fDSoundBuffer && plgAudioSys::Active() ) { + if (!fReallyPlaying && fSynchedStartTimeSec > 0) { + // advance past any subtitles that would end before the synched start time + // not sure when this actually happens... + plSoundBuffer* buf = GetDataBuffer(); + if (buf != nullptr) { + plSrtFileReader* srtReader = buf->GetSrtReader(); + if (srtReader != nullptr) { + srtReader->AdvanceToTime(fSynchedStartTimeSec * 1000.0); + } + } + } // Sometimes base/derived classes can be annoying IDerivedActuallyPlay(); diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp index f7068053..b7259bc1 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp @@ -48,6 +48,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plgDispatch.h" #include "hsResMgr.h" +#include "plSrtFileReader.h" #include "../pnMessage/plRefMsg.h" #include "../plFile/plFileUtils.h" #include "../plFile/hsFiles.h" @@ -117,20 +118,35 @@ static void LoadCallback(void *) else { plAudioFileReader *reader = nil; + plSrtFileReader* srtReader = nullptr; + while(plSoundBuffer *buffer = templist.Head()) { if(buffer->GetData()) { - reader = CreateReader(true, buffer->GetFileName(), buffer->GetAudioReaderType(), buffer->GetReaderSelect()); + const char* srcFilename = buffer->GetFileName(); + reader = CreateReader(true, srcFilename, buffer->GetAudioReaderType(), buffer->GetReaderSelect()); if( reader ) { unsigned readLen = buffer->GetAsyncLoadLength() ? buffer->GetAsyncLoadLength() : buffer->GetDataLength(); reader->Read( readLen, buffer->GetData() ); buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later + + plSrtFileReader* srtReader = buffer->GetSrtReader(); + if (srtReader != nullptr && srtReader->GetCurrentAudioFileName() == srcFilename) { + // same file we were playing before, so start the SRT feed over instead of deleting and reloading + srtReader->StartOver(); + } else { + std::unique_ptr newSrtFileReader(new plSrtFileReader(srcFilename)); + if (newSrtFileReader->ReadFile()) + buffer->SetSrtReader(newSrtFileReader.release()); + } } else + { buffer->SetError(); + } } templist.Unlink(buffer); @@ -194,6 +210,8 @@ plSoundBuffer::~plSoundBuffer() ASSERT(!link.IsLinked()); delete [] fFileName; UnLoad(); + + delete fSrtReader; } void plSoundBuffer::IInitBuffer() @@ -206,6 +224,7 @@ void plSoundBuffer::IInitBuffer() fFlags = 0; fDataRead = 0; fReader = nil; + fSrtReader = nullptr; fLoaded = 0; fLoading = false; fHeader.fFormatTag = 0; @@ -458,6 +477,12 @@ void plSoundBuffer::SetLoaded(bool loaded) fLoaded = loaded; } +void plSoundBuffer::SetSrtReader(plSrtFileReader* reader) +{ + delete fSrtReader; + fSrtReader = reader; +} + /***************************************************************************** * diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h index 6bb72244..940adf95 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.h @@ -61,6 +61,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com class plUnifiedTime; class plAudioFileReader; +class plSrtFileReader; class plSoundBuffer : public hsKeyedObject { public: @@ -118,6 +119,8 @@ public: plAudioFileReader * GetAudioReader(); // transfers ownership to caller void SetAudioReader(plAudioFileReader *reader); void SetLoaded(bool loaded); + plSrtFileReader* GetSrtReader() const { return fSrtReader; } // does not transfer ownership + void SetSrtReader(plSrtFileReader* reader); plAudioFileReader::StreamType GetAudioReaderType() { return fStreamType; } unsigned GetAsyncLoadLength() { return fAsyncLoadLength ? fAsyncLoadLength : fDataLength; } @@ -148,7 +151,8 @@ protected: bool fLoading; bool fError; - plAudioFileReader * fReader; + plAudioFileReader * fReader; + plSrtFileReader* fSrtReader; UInt8 * fData; plWAVHeader fHeader; UInt32 fDataLength; diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp new file mode 100644 index 00000000..31311d3d --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -0,0 +1,192 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plSrtFileReader.h" + +#include "hsStream.h" + +#include "../plStatusLog/plStatusLog.h" +#include "../pnUtils/Private/pnUtPath.h" +#include "../pnUtils/Private/pnUtStr.h" + +#include + +static const std::regex speakerTextRegex("^((([\\w.]+(\\s\\w+)?):) )?(.*)"); +static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> (\\d{2}):(\\d{2}):(\\d{2}),(\\d{3})$"); + +bool plSrtFileReader::ReadFile() +{ + wchar srtPathUnicode[MAX_PATH]; + StrToUnicode(srtPathUnicode, fAudioFileName, arrsize(srtPathUnicode)); + PathSetExtension(srtPathUnicode, srtPathUnicode, L".sub", arrsize(srtPathUnicode)); + + if (PathDoesFileExist(srtPathUnicode)) { + // read sets of SRT data until end of file + hsUNIXStream srtFileStream; + + // if file exists and was opened successfully + if (srtFileStream.Open(srtPathUnicode, L"r")) { + plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", srtPathUnicode); + + uint32_t subtitleNumber = 0; + uint32_t subtitleStartTimeMs = 0; + uint32_t subtitleEndTimeMs = 0; + char* line; + std::string speakerName; + std::string subtitleText; + std::cmatch matches; + + for (unsigned int lnCounter = 0; !srtFileStream.AtEnd(); lnCounter++) { + if (lnCounter % 4 == 0 && srtFileStream.ReadLn(line)) { + subtitleNumber = std::stoul(line); + } + else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) { + if (std::regex_match(line, matches, timingsRegex)) { + if (matches.size() < 9) { + // add error message and ensure this subtitle line won't be added to the entries + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '{}' are formatted incorrectly.", line); + subtitleNumber = 0; + } + else { + // matches[0] is the entire match, we don't do anything with it + // matches[1] is the first group -- the start hour number + subtitleStartTimeMs += std::stoul(matches[1].str()) * 3600000; + // matches[2] is the second group -- the start minute number + subtitleStartTimeMs += std::stoul(matches[2].str()) * 60000; + // matches[3] is the third group -- the start seconds number + subtitleStartTimeMs += std::stoul(matches[3].str()) * 1000; + // matches[4] is the fourth group -- the start milliseconds number + subtitleStartTimeMs += std::stoul(matches[4].str()); + + // matches[5] is the fifth group -- the end hour number + subtitleEndTimeMs += std::stoul(matches[5].str()) * 3600000; + // matches[6] is the sixth group -- the end minute number + subtitleEndTimeMs += std::stoul(matches[6].str()) * 60000; + // matches[7] is the seventh group -- the end seconds number + subtitleEndTimeMs += std::stoul(matches[7].str()) * 1000; + // matches[8] is the eighth group -- the end milliseconds number + subtitleEndTimeMs += std::stoul(matches[8].str()); + } + } + } + else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) { + if (std::regex_match(line, matches, speakerTextRegex)) { + if (matches.size() < 5) { + // add error message and ensure this subtitle line won't be added to the entries + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '{}' are formatted incorrectly.", line); + subtitleNumber = 0; + } + else { + // matches[0] is the entire match, we don't do anything with it + // matches[2] is the second group (the optional subtitle speaker with colon but no space at the end) + speakerName = matches[2]; + + // matches[5] is the fourth group (the subtitle text) + subtitleText = matches[5]; + } + } + } + else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { + // entry is complete, add to the queue and reset our temp variables + if (!speakerName.empty()) + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName))); + else + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText))); + + subtitleNumber = 0; + subtitleStartTimeMs = 0; + subtitleEndTimeMs = 0; + subtitleText.clear(); + speakerName.clear(); + } + } + + if (subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { + // enqueue the last subtitle from the file if we didn't have an extra blank line at the end + if (!speakerName.empty()) + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName))); + else + fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText))); + } + + return true; + } + } + + return false; +} + +void plSrtFileReader::AdvanceToTime(uint32_t timeMs) +{ + while (fCurrentEntryIndex < fEntries.size() && fEntries.at(fCurrentEntryIndex).GetEndTimeMs() <= timeMs) { + fCurrentEntryIndex++; + } +} + + +plSrtEntry* plSrtFileReader::GetNextEntryStartingBeforeTime(uint32_t timeMs) +{ + if (fCurrentEntryIndex < fEntries.size()) { + plSrtEntry& nextEntry = fEntries.at(fCurrentEntryIndex); + + if (nextEntry.GetStartTimeMs() <= timeMs) { + fCurrentEntryIndex++; + return &nextEntry; + } + } + + return nullptr; +} + +plSrtEntry* plSrtFileReader::GetNextEntryEndingBeforeTime(uint32_t timeMs) +{ + if (fCurrentEntryIndex < fEntries.size()) { + plSrtEntry& nextEntry = fEntries.at(fCurrentEntryIndex); + + if (nextEntry.GetEndTimeMs() <= timeMs) { + fCurrentEntryIndex++; + return &nextEntry; + } + } + + return nullptr; +} \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h new file mode 100644 index 00000000..f9e57210 --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h @@ -0,0 +1,105 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ +////////////////////////////////////////////////////////////////////////////// +// // +// plSrtFileReader - Class for reading an SRT format file and // +// storing the read entries for access later // +// // +////////////////////////////////////////////////////////////////////////////// + +#ifndef _plSrtFileReader_h +#define _plSrtFileReader_h + +#include "hsTypes.h" + +#include +#include + +class plSrtEntry +{ +public: + + plSrtEntry(uint32_t entryNum, uint32_t startTimeMs, uint32_t endTimeMs, std::string subtitleText, std::string speakerName) + : fEntryNum(entryNum), fStartTimeMs(startTimeMs), fEndTimeMs(endTimeMs), fSubtitleText(std::move(subtitleText)), + fSpeakerName(std::move(speakerName)) { } + + plSrtEntry(uint32_t entryNum, uint32_t startTimeMs, uint32_t endTimeMs, std::string subtitleText) + : fEntryNum(entryNum), fStartTimeMs(startTimeMs), fEndTimeMs(endTimeMs), fSubtitleText(std::move(subtitleText)) { } + + std::string GetSubtitleText() const { return fSubtitleText; } + std::string GetSpeakerName() const { return fSpeakerName; } + uint32_t GetStartTimeMs() const { return fStartTimeMs; } + uint32_t GetEndTimeMs() const { return fEndTimeMs; } + +protected: + + uint32_t fEntryNum; + uint32_t fStartTimeMs; + uint32_t fEndTimeMs; + std::string fSubtitleText; + std::string fSpeakerName; + +}; + +class plSrtFileReader +{ +public: + + plSrtFileReader(const char* audioFileName) + : fAudioFileName(std::move(audioFileName)), fEntries(), fCurrentEntryIndex() { } + + bool ReadFile(); + void StartOver() { fCurrentEntryIndex = 0; } + const char* GetCurrentAudioFileName() const { return fAudioFileName; } + void AdvanceToTime(uint32_t timeMs); + plSrtEntry* GetNextEntryStartingBeforeTime(uint32_t timeMs); + plSrtEntry* GetNextEntryEndingBeforeTime(uint32_t timeMs); + +protected: + + const char* fAudioFileName; + std::vector fEntries; + uint32_t fCurrentEntryIndex; + +}; + +#endif //_plSrtFileReader_h \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h index 25dac2e2..cee8bf45 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h @@ -133,6 +133,10 @@ REGISTER_CREATABLE( plSpawnModMsg ); REGISTER_CREATABLE( plSpawnRequestMsg ); +#include "plSubtitleMsg.h" + +REGISTER_CREATABLE(plSubtitleMsg); + #include "plNodeCleanupMsg.h" REGISTER_CREATABLE( plNodeCleanupMsg ); diff --git a/Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h new file mode 100644 index 00000000..e6cdb68d --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plMessage/plSubtitleMsg.h @@ -0,0 +1,82 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plSubtitleMsg_inc +#define plSubtitleMsg_inc + +#include "../pnMessage/plMessage.h" + +/** Show subtitle text for in-game audio */ +class plSubtitleMsg : public plMessage +{ +protected: + std::string fText; + std::string fSpeaker; + +public: + plSubtitleMsg() : plMessage() + { + SetBCastFlag(plMessage::kBCastByExactType); + } + + plSubtitleMsg(std::string msgText) : plMessage(), fText(std::move(msgText)) + { + SetBCastFlag(plMessage::kBCastByExactType); + } + + plSubtitleMsg(std::string msgText, std::string msgSpeaker) : plMessage(), fText(std::move(msgText)), + fSpeaker(std::move(msgSpeaker)) + { + SetBCastFlag(plMessage::kBCastByExactType); + } + + CLASSNAME_REGISTER(plSubtitleMsg); + GETINTERFACE_ANY(plSubtitleMsg, plMessage); + + void Read(hsStream*, hsResMgr*) override { FATAL("no"); } + void Write(hsStream*, hsResMgr*) override { FATAL("no"); } + + std::string GetText() const { return fText; } + std::string GetSpeaker() const { return fSpeaker; } +}; + +#endif // plSubtitleMsg_inc \ No newline at end of file From c50bb08e8db3caffe769d8b1e3907d7aa71333a8 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Wed, 23 Feb 2022 21:46:02 -0600 Subject: [PATCH 2/9] Fix usage and overflow bug causing plWin32StreamingSound::GetActualTimeSec() to return absurd values. Needed fix for subtitle timings. --- .../PubUtilLib/plAudio/plDSoundBuffer.cpp | 4 ++-- .../plAudio/plWin32StreamingSound.cpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp index 82f6d53d..a3a89af6 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp @@ -710,9 +710,9 @@ hsBool plDSoundBuffer::IsEAXAccelerated( void ) const //// BytePosToMSecs ////////////////////////////////////////////////////////// -UInt32 plDSoundBuffer::BytePosToMSecs( UInt32 bytePos ) const +UInt32 plDSoundBuffer::BytePosToMSecs(UInt32 bytePos) const { - return (UInt32)(bytePos * 1000 / (hsScalar)fBufferDesc->fAvgBytesPerSec); + return (UInt32)(bytePos / ((hsScalar)fBufferDesc->fAvgBytesPerSec) / 1000.0f); } //// GetBufferBytePos //////////////////////////////////////////////////////// diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp index e402e771..14beb5e1 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32StreamingSound.cpp @@ -455,21 +455,24 @@ void plWin32StreamingSound::IActuallyStop() unsigned plWin32StreamingSound::GetByteOffset() { - if(fDataStream && fDSoundBuffer) - { + if (fDataStream && fDSoundBuffer) + { + UInt32 totalSize = fDataStream->GetDataSize(); + UInt32 bytesRemaining = fDataStream->NumBytesLeft(); unsigned bytesQueued = fDSoundBuffer->BuffersQueued() * STREAM_BUFFER_SIZE; unsigned offset = fDSoundBuffer->GetByteOffset(); - long byteoffset = ((fDataStream->GetDataSize() - fDataStream->NumBytesLeft()) - bytesQueued) + offset; - - return byteoffset < 0 ? fDataStream->GetDataSize() - abs(byteoffset) : byteoffset; + long byteoffset = ((totalSize - bytesRemaining) - bytesQueued) + offset; + + return byteoffset < 0 ? totalSize - std::abs(byteoffset) : byteoffset; } + return 0; } float plWin32StreamingSound::GetActualTimeSec() { - if(fDataStream && fDSoundBuffer) - return fDSoundBuffer->BytePosToMSecs(fDataStream->NumBytesLeft()) / 1000.0f; + if (fDataStream && fDSoundBuffer) + return fDSoundBuffer->BytePosToMSecs(this->GetByteOffset()) / 1000.0f; return 0.0f; } From 1d65c780979d707c89d81b08aa82dd3f86d60bc1 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 25 Feb 2022 01:06:29 -0600 Subject: [PATCH 3/9] Fix bad include, missing static declaration, SRT path directory, and formatting --- .../Plasma/PubUtilLib/plAudio/plAudioSystem.h | 1 + .../plAudioCore/plSrtFileReader.cpp | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h index 43df875e..dbc05db8 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h +++ b/Sources/Plasma/PubUtilLib/plAudio/plAudioSystem.h @@ -260,6 +260,7 @@ private: static hsBool fInit; static hsBool fActive; static hsBool fMuted; + static bool fEnableSubtitles; static hsWindowHndl fWnd; static hsBool fUseHardware; static hsBool fDelayedActivate; diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 31311d3d..19e5f688 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -45,8 +45,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsStream.h" #include "../plStatusLog/plStatusLog.h" -#include "../pnUtils/Private/pnUtPath.h" -#include "../pnUtils/Private/pnUtStr.h" +#include "../pnUtils/pnUtils.h" #include @@ -55,8 +54,15 @@ static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> ( bool plSrtFileReader::ReadFile() { + char path[MAX_PATH]; + + if (strchr(fAudioFileName, '\\') != nil) + strcpy(path, fAudioFileName); + else + sprintf(path, "sfx\\%s", fAudioFileName); + wchar srtPathUnicode[MAX_PATH]; - StrToUnicode(srtPathUnicode, fAudioFileName, arrsize(srtPathUnicode)); + StrToUnicode(srtPathUnicode, path, arrsize(srtPathUnicode)); PathSetExtension(srtPathUnicode, srtPathUnicode, L".sub", arrsize(srtPathUnicode)); if (PathDoesFileExist(srtPathUnicode)) { @@ -70,7 +76,7 @@ bool plSrtFileReader::ReadFile() uint32_t subtitleNumber = 0; uint32_t subtitleStartTimeMs = 0; uint32_t subtitleEndTimeMs = 0; - char* line; + char line[4096]; std::string speakerName; std::string subtitleText; std::cmatch matches; @@ -78,8 +84,7 @@ bool plSrtFileReader::ReadFile() for (unsigned int lnCounter = 0; !srtFileStream.AtEnd(); lnCounter++) { if (lnCounter % 4 == 0 && srtFileStream.ReadLn(line)) { subtitleNumber = std::stoul(line); - } - else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) { + } else if (lnCounter % 4 == 1 && srtFileStream.ReadLn(line)) { if (std::regex_match(line, matches, timingsRegex)) { if (matches.size() < 9) { // add error message and ensure this subtitle line won't be added to the entries @@ -107,8 +112,7 @@ bool plSrtFileReader::ReadFile() subtitleEndTimeMs += std::stoul(matches[8].str()); } } - } - else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) { + } else if (lnCounter % 4 == 2 && srtFileStream.ReadLn(line)) { if (std::regex_match(line, matches, speakerTextRegex)) { if (matches.size() < 5) { // add error message and ensure this subtitle line won't be added to the entries @@ -124,8 +128,7 @@ bool plSrtFileReader::ReadFile() subtitleText = matches[5]; } } - } - else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { + } else if (lnCounter % 4 == 3 && subtitleNumber > 0 && subtitleStartTimeMs >= 0 && subtitleEndTimeMs >= 0 && !subtitleText.empty()) { // entry is complete, add to the queue and reset our temp variables if (!speakerName.empty()) fEntries.emplace_back(plSrtEntry(subtitleNumber, subtitleStartTimeMs, subtitleEndTimeMs, std::move(subtitleText), std::move(speakerName))); From 1012251d9ab1aabab4dad58bce52ccd517999899 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 25 Feb 2022 01:07:40 -0600 Subject: [PATCH 4/9] Add missing message dispatch registration --- Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index cfc2dc90..f32d64d5 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -830,6 +830,8 @@ void plPythonFileMod::AddTarget(plSceneObject* sobj) // the message that is spammed to anyone who will listen plgDispatch::Dispatch()->RegisterForExactType(plAIBrainCreatedMsg::Index(), GetKey()); } + if (fPyFunctionInstances[kfunc_OnSubtitleMsg]) + plgDispatch::Dispatch()->RegisterForExactType(plSubtitleMsg::Index(), GetKey()); // As the last thing... call the OnInit function if they have one if ( fPyFunctionInstances[kfunc_Init] != nil ) From 9a1b37e7f6662d4b2ff1561a04ae709157b89def Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Fri, 25 Feb 2022 12:14:44 -0600 Subject: [PATCH 5/9] Turn std::strings into char* before doing PyObject_CallMethod with them --- Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp index f32d64d5..5518bd5c 100644 --- a/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/plPythonFileMod.cpp @@ -2792,7 +2792,7 @@ hsBool plPythonFileMod::MsgReceive(plMessage* msg) plProfile_BeginTiming(PythonUpdate); PyObject* retVal = PyObject_CallMethod( fPyFunctionInstances[kfunc_OnSubtitleMsg], fFunctionNames[kfunc_OnSubtitleMsg], - "ss", pSubMsg->GetText(), pSubMsg->GetSpeaker()); + "ss", pSubMsg->GetText().c_str(), pSubMsg->GetSpeaker().c_str()); if (retVal == nil) { #ifndef PLASMA_EXTERNAL_RELEASE From 8a2534dff6967d8077fdde16c08ba40d886db67d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Fri, 25 Feb 2022 14:34:30 -0600 Subject: [PATCH 6/9] Simplify SRT file path construction with suggestion from Hoikas --- .../plAudioCore/plSrtFileReader.cpp | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 19e5f688..01b53088 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -54,24 +54,17 @@ static const std::regex timingsRegex("^(\\d{2}):(\\d{2}):(\\d{2}),(\\d{3}) --> ( bool plSrtFileReader::ReadFile() { - char path[MAX_PATH]; - - if (strchr(fAudioFileName, '\\') != nil) - strcpy(path, fAudioFileName); - else - sprintf(path, "sfx\\%s", fAudioFileName); - - wchar srtPathUnicode[MAX_PATH]; - StrToUnicode(srtPathUnicode, path, arrsize(srtPathUnicode)); - PathSetExtension(srtPathUnicode, srtPathUnicode, L".sub", arrsize(srtPathUnicode)); + wchar path[MAX_PATH]; + StrPrintf(path, arrsize(path), L"sfx\\%S", fAudioFileName); + PathSetExtension(path, path, L".sub", arrsize(path)); - if (PathDoesFileExist(srtPathUnicode)) { + if (PathDoesFileExist(path)) { // read sets of SRT data until end of file hsUNIXStream srtFileStream; // if file exists and was opened successfully - if (srtFileStream.Open(srtPathUnicode, L"r")) { - plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", srtPathUnicode); + if (srtFileStream.Open(path, L"r")) { + plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", path); uint32_t subtitleNumber = 0; uint32_t subtitleStartTimeMs = 0; From 64323c6073323dcf8f9ec633707d3e1072052e6b Mon Sep 17 00:00:00 2001 From: rarified Date: Sat, 26 Feb 2022 11:57:56 -0600 Subject: [PATCH 7/9] Apply code suggestions from rarified --- Sources/Plasma/NucleusLib/inc/plCreatableIndex.h | 4 ++-- .../PubUtilLib/plAudioCore/plSoundBuffer.cpp | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index a5c5b248..6c2d20ed 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -949,7 +949,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plAngularVelocityMsg), CLASS_INDEX(plRideAnimatedPhysMsg), CLASS_INDEX(plAvBrainRideAnimatedPhysical), - CLASS_INDEX(pfGameScoreMsg), + CLASS_INDEX(pfGameScoreMsg), // This index and below are unused currently and placed for indexing plSubtitleMsg consistently with H'uru CLASS_INDEX(pfGameScoreListMsg), CLASS_INDEX(pfGameScoreTransferMsg), CLASS_INDEX(pfGameScoreUpdateMsg), @@ -958,7 +958,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plGLPipeline), CLASS_INDEX(plSDLModifierStateMsg), CLASS_INDEX(plConfirmationMsg), - CLASS_INDEX(plLocalizedConfirmationMsg), + CLASS_INDEX(plLocalizedConfirmationMsg), // This index and above are unused currently and placed for indexing plSubtitleMsg consistently with H'uru CLASS_INDEX(plSubtitleMsg), CLASS_INDEX_LIST_END diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp index b7259bc1..177257d8 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp @@ -45,6 +45,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsStream.h" #include "hsUtils.h" +#include #include "plgDispatch.h" #include "hsResMgr.h" @@ -134,13 +135,13 @@ static void LoadCallback(void *) buffer->SetAudioReader(reader); // give sound buffer reader, since we may need it later plSrtFileReader* srtReader = buffer->GetSrtReader(); - if (srtReader != nullptr && srtReader->GetCurrentAudioFileName() == srcFilename) { - // same file we were playing before, so start the SRT feed over instead of deleting and reloading - srtReader->StartOver(); - } else { - std::unique_ptr newSrtFileReader(new plSrtFileReader(srcFilename)); - if (newSrtFileReader->ReadFile()) - buffer->SetSrtReader(newSrtFileReader.release()); + if (srtReader != nullptr && (strcmp(srtReader->GetCurrentAudioFileName(), srcFilename) == 0)) { + // same file we were playing before, so start the SRT feed over instead of deleting and reloading + srtReader->StartOver(); + } else { + std::unique_ptr newSrtFileReader(new plSrtFileReader(srcFilename)); + if (newSrtFileReader->ReadFile()) + buffer->SetSrtReader(newSrtFileReader.release()); } } else From 126d7511e0cd041241326df8e06cd448fe4eecaf Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Sat, 26 Feb 2022 19:16:00 -0600 Subject: [PATCH 8/9] Fix parentheses mistake for retrieving streaming audio timings and apply code fix suggestions from Adam --- Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp | 2 +- Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp | 3 ++- Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp | 4 ++-- Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h | 4 +++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp index a3a89af6..27bc2217 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp @@ -712,7 +712,7 @@ hsBool plDSoundBuffer::IsEAXAccelerated( void ) const UInt32 plDSoundBuffer::BytePosToMSecs(UInt32 bytePos) const { - return (UInt32)(bytePos / ((hsScalar)fBufferDesc->fAvgBytesPerSec) / 1000.0f); + return (UInt32)(bytePos / ((float)fBufferDesc->fAvgBytesPerSec / 1000.0f)); } //// GetBufferBytePos //////////////////////////////////////////////////////// diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp index 0eebdfbc..2fcbe712 100644 --- a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp +++ b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp @@ -114,7 +114,8 @@ void plWin32Sound::Update() if (plgAudioSys::AreSubtitlesEnabled() && buf != nullptr) { plSrtFileReader* srtReader = buf->GetSrtReader(); if (srtReader != nullptr) { - while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime((uint32_t)(GetActualTimeSec() * 1000.0f))) { + uint32_t currentTimeMs = (uint32_t)(GetActualTimeSec() * 1000.0f); + while (plSrtEntry* nextEntry = srtReader->GetNextEntryStartingBeforeTime(currentTimeMs)) { // add a plSubtitleMsg to go... to whoever is listening (probably the KI) plSubtitleMsg* msg = new plSubtitleMsg(nextEntry->GetSubtitleText(), nextEntry->GetSpeakerName()); msg->Send(); diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 01b53088..3208086c 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -64,7 +64,7 @@ bool plSrtFileReader::ReadFile() // if file exists and was opened successfully if (srtFileStream.Open(path, L"r")) { - plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file {}", path); + plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file %S", path); uint32_t subtitleNumber = 0; uint32_t subtitleStartTimeMs = 0; @@ -81,7 +81,7 @@ bool plSrtFileReader::ReadFile() if (std::regex_match(line, matches, timingsRegex)) { if (matches.size() < 9) { // add error message and ensure this subtitle line won't be added to the entries - plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '{}' are formatted incorrectly.", line); + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle timings '%s' are formatted incorrectly.", line); subtitleNumber = 0; } else { diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h index f9e57210..94604f87 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h @@ -50,6 +50,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #define _plSrtFileReader_h #include "hsTypes.h" +#include "../pnUtils/pnUtils.h" #include #include @@ -85,7 +86,8 @@ class plSrtFileReader public: plSrtFileReader(const char* audioFileName) - : fAudioFileName(std::move(audioFileName)), fEntries(), fCurrentEntryIndex() { } + : fAudioFileName(StrDup(audioFileName)), fCurrentEntryIndex(0) { } + virtual ~plSrtFileReader() { delete[] fAudioFileName; } bool ReadFile(); void StartOver() { fCurrentEntryIndex = 0; } From 9e308cbebe53426e12e99960522068806282aa04 Mon Sep 17 00:00:00 2001 From: ZarothYe Date: Sat, 26 Feb 2022 20:22:41 -0600 Subject: [PATCH 9/9] Fix another log string formatting issue found by Adam. --- Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp index 3208086c..2cc63f32 100644 --- a/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp +++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp @@ -109,10 +109,9 @@ bool plSrtFileReader::ReadFile() if (std::regex_match(line, matches, speakerTextRegex)) { if (matches.size() < 5) { // add error message and ensure this subtitle line won't be added to the entries - plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '{}' are formatted incorrectly.", line); + plStatusLog::AddLineS("audio.log", plStatusLog::kRed, " Subtitle text and/or speaker name '%s' are formatted incorrectly.", line); subtitleNumber = 0; - } - else { + } else { // matches[0] is the entire match, we don't do anything with it // matches[2] is the second group (the optional subtitle speaker with colon but no space at the end) speakerName = matches[2];