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..5518bd5c 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 }
};
@@ -828,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 )
@@ -2780,6 +2784,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().c_str(), pSubMsg->GetSpeaker().c_str());
+ 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..6c2d20ed 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), // 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),
+ CLASS_INDEX(plLoadClothingMsg),
+ CLASS_INDEX(plNullPipeline),
+ CLASS_INDEX(plGLPipeline),
+ CLASS_INDEX(plSDLModifierStateMsg),
+ CLASS_INDEX(plConfirmationMsg),
+ 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
-#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..dbc05db8 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 );
@@ -258,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/plAudio/plDSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudio/plDSoundBuffer.cpp
index 82f6d53d..27bc2217 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 / ((float)fBufferDesc->fAvgBytesPerSec / 1000.0f));
}
//// GetBufferBytePos ////////////////////////////////////////////////////////
diff --git a/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp b/Sources/Plasma/PubUtilLib/plAudio/plWin32Sound.cpp
index a81a31b4..2fcbe712 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,19 @@ void plWin32Sound::IFreeBuffers( void )
void plWin32Sound::Update()
{
+ plSoundBuffer* buf = GetDataBuffer();
+ if (plgAudioSys::AreSubtitlesEnabled() && buf != nullptr) {
+ plSrtFileReader* srtReader = buf->GetSrtReader();
+ if (srtReader != nullptr) {
+ 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();
+ }
+ }
+ }
+
plSound::Update();
}
@@ -120,6 +135,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/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;
}
diff --git a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp
index f7068053..177257d8 100644
--- a/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp
+++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSoundBuffer.cpp
@@ -45,9 +45,11 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com
#include "hsStream.h"
#include "hsUtils.h"
+#include
#include "plgDispatch.h"
#include "hsResMgr.h"
+#include "plSrtFileReader.h"
#include "../pnMessage/plRefMsg.h"
#include "../plFile/plFileUtils.h"
#include "../plFile/hsFiles.h"
@@ -117,20 +119,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 && (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
+ {
buffer->SetError();
+ }
}
templist.Unlink(buffer);
@@ -194,6 +211,8 @@ plSoundBuffer::~plSoundBuffer()
ASSERT(!link.IsLinked());
delete [] fFileName;
UnLoad();
+
+ delete fSrtReader;
}
void plSoundBuffer::IInitBuffer()
@@ -206,6 +225,7 @@ void plSoundBuffer::IInitBuffer()
fFlags = 0;
fDataRead = 0;
fReader = nil;
+ fSrtReader = nullptr;
fLoaded = 0;
fLoading = false;
fHeader.fFormatTag = 0;
@@ -458,6 +478,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..2cc63f32
--- /dev/null
+++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.cpp
@@ -0,0 +1,187 @@
+/*==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/pnUtils.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 path[MAX_PATH];
+ StrPrintf(path, arrsize(path), L"sfx\\%S", fAudioFileName);
+ PathSetExtension(path, path, L".sub", arrsize(path));
+
+ if (PathDoesFileExist(path)) {
+ // read sets of SRT data until end of file
+ hsUNIXStream srtFileStream;
+
+ // if file exists and was opened successfully
+ if (srtFileStream.Open(path, L"r")) {
+ plStatusLog::AddLineS("audio.log", "Successfully opened subtitle file %S", path);
+
+ uint32_t subtitleNumber = 0;
+ uint32_t subtitleStartTimeMs = 0;
+ uint32_t subtitleEndTimeMs = 0;
+ char line[4096];
+ 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 '%s' 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 '%s' 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..94604f87
--- /dev/null
+++ b/Sources/Plasma/PubUtilLib/plAudioCore/plSrtFileReader.h
@@ -0,0 +1,107 @@
+/*==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 "../pnUtils/pnUtils.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(StrDup(audioFileName)), fCurrentEntryIndex(0) { }
+ virtual ~plSrtFileReader() { delete[] fAudioFileName; }
+
+ 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