/*==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 .
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 "hsTypes.h"
#include "hsGeometry3.h"
#include "plWinAudible.h"
#include "hsMatrix44.h"
#include "hsTimer.h"
#include "plAudio/plSound.h"
#include "plAudio/plWin32Sound.h"
#include "plAudio/plVoiceChat.h"
#include "plAudio/plAudioSystem.h"
#include "plAudio/plWin32StreamingSound.h"
#include "pnMessage/plSoundMsg.h"
#include "pnMessage/plTimeMsg.h"
#include "pnMessage/plAudioSysMsg.h"
#include "plMessage/plLinkToAgeMsg.h"
#include "hsResMgr.h"
#include "pnKeyedObject/plKey.h"
#include "hsQuat.h"
#include "plgDispatch.h"
#include "pnMessage/plNodeRefMsg.h"
#include "pnMessage/plEventCallbackMsg.h"
#include "pnMessage/plCmdIfaceModMsg.h"
#include "pnMessage/plProxyDrawMsg.h"
#include "plMessage/plInputEventMsg.h"
#include "pnInputCore/plControlEventCodes.h"
#include "plModifier/plSoundSDLModifier.h"
#include "pnSceneObject/plSceneObject.h"
#include "plStatusLog/plStatusLog.h"
#define SND_INDEX_CHECK( index, ret ) \
if( index >= fSoundObjs.GetCount() ) \
{ \
hsStatusMessageF( "ERROR: Sound index out of range (index %d, count %d)\n", index, fSoundObjs.GetCount() ); \
return ret; \
}
#define SND_APPLY_LOOP( index, func, ret ) \
if( index != -1 ) \
{ \
SND_INDEX_CHECK( index, ret ); \
if( fSoundObjs[ index ] != nil ) \
fSoundObjs[ index ]->func; \
} \
else \
{ \
for( int i = 0; i < fSoundObjs.Count(); i++ ) \
{ \
if( fSoundObjs[ i ] != nil ) \
fSoundObjs[ i ]->func; \
} \
}
// Visualization
#include "plWinAudibleProxy.h"
plWinAudible::plWinAudible()
: fSceneNode(nil), fSceneObj(nil), fSDLMod(nil)
{
fProxyGen = TRACKED_NEW plWinAudibleProxy;
fProxyGen->Init(this);
fLocalToWorld.Reset();
}
plWinAudible::~plWinAudible()
{
// delete SDL modifier
if (fSceneObj)
{
plSceneObject* so=plSceneObject::ConvertNoRef(fSceneObj->ObjectIsLoaded());
if (so)
so->RemoveModifier(fSDLMod);
}
delete fSDLMod;
delete fProxyGen;
for (int i = 0; i < fSoundObjs.Count(); i++)
delete(fSoundObjs[i]);
fSoundObjs.SetCountAndZero(0);
}
void plWinAudible::SetSceneObject(plKey obj)
{
plKey oldKey = nil;
// remove old SDL mod
if (fSDLMod && fSceneObj && fSceneObj != obj)
{
oldKey = fSceneObj;
plSceneObject* so=plSceneObject::ConvertNoRef(fSceneObj->ObjectIsLoaded());
if (so)
so->RemoveModifier(fSDLMod);
delete fSDLMod;
fSDLMod=nil;
}
fSceneObj = obj;
plSceneObject* so=plSceneObject::ConvertNoRef(obj ? obj->ObjectIsLoaded() : nil);
if (so)
{
so->RemoveModifier(fSDLMod);
delete fSDLMod;
fSDLMod=TRACKED_NEW plSoundSDLModifier;
so->AddModifier(fSDLMod);
}
for( int i = 0; i < fSoundObjs.Count(); i++ )
{
if( fSoundObjs[ i ] != nil && fSoundObjs[ i ]->GetKey() != nil )
{
if( obj != nil )
{
plGenRefMsg *replaceMsg = TRACKED_NEW plGenRefMsg( fSoundObjs[ i ]->GetKey(), plRefMsg::kOnReplace, 0, plSound::kRefParentSceneObject );
hsgResMgr::ResMgr()->AddViaNotify( obj, replaceMsg, plRefFlags::kPassiveRef );
}
else if( oldKey != nil )
fSoundObjs[ i ]->GetKey()->Release( oldKey );
}
}
}
void plWinAudible::SetSceneNode(plKey newNode)
{
plKey oldNode = GetSceneNode();
if( oldNode == newNode )
return;
if( !oldNode )
Activate();
if( newNode )
{
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kAudible);
hsgResMgr::ResMgr()->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef);
}
if( oldNode )
{
oldNode->Release(GetKey());
}
if( !newNode )
{
DeActivate();
}
fSceneNode = newNode;
}
void plWinAudible::GetStatus(plSoundMsg* pMsg)
{
if (pMsg->fIndex == -1)
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
plSoundMsg* pReply = fSoundObjs[i]->GetStatus(pMsg);
pReply->fIndex = i;
plgDispatch::MsgSend(pReply);
}
}
else
if (pMsg->fIndex < fSoundObjs.Count())
{
plSoundMsg* pReply = fSoundObjs[pMsg->fIndex]->GetStatus(pMsg);
pReply->fIndex = pMsg->fIndex;
plgDispatch::MsgSend(pReply);
}
}
hsBool plWinAudible::AddSound( plSound *pSnd, int index, hsBool is3D )
{
hsAssert(pSnd->GetKey() != nil, "Adding a new sound with no key.");
if (plgAudioSys::Active())
{
hsgResMgr::ResMgr()->AddViaNotify( pSnd->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef );
return true;
}
else
{
pSnd->SetProperty( plSound::kPropIs3DSound, is3D );
hsgResMgr::ResMgr()->AddViaNotify( pSnd->GetKey(), TRACKED_NEW plGenRefMsg( GetKey(), plRefMsg::kOnCreate, index, 0 ), plRefFlags::kActiveRef );
return true;
}
}
/* Unused
int plWinAudible::AddSoundFromResource(plSound *pSnd, void* addr, Int32 size, hsBool is3D )
{
//plWin32Sound* pSnd = TRACKED_NEW plWin32Sound;
//IAssignSoundKey( pSnd, GetKey() ? GetKeyName() : "", fSoundObjs.Count() - 1 );
if (plgAudioSys::Active())
{
int ret = pSnd->GetSoundFromMemory(addr, size, is3D );
if (ret)
{
fSoundObjs.Append(pSnd);
return (fSoundObjs.Count() -1 );
}
}
delete pSnd;
return -1;
}
*/
plAudible& plWinAudible::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index)
{
fLocalToWorld = l2w;
plAudible::SetTransform(l2w, w2l);
hsVector3 v = l2w.GetAxis(hsMatrix44::kUp);
v*=-1;
hsPoint3 pos = l2w.GetTranslate();
if (index != -1)
{
SND_INDEX_CHECK( index, (*this) );
if( fSoundObjs[ index ] != nil )
{
fSoundObjs[index]->SetPosition( pos );
fSoundObjs[index]->SetConeOrientation( v.fX,v.fY,v.fZ );
}
}
else
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
if( fSoundObjs[ i ] != nil )
{
fSoundObjs[i]->SetConeOrientation( v.fX,v.fY,v.fZ );
fSoundObjs[i]->SetPosition( pos );
}
}
}
fProxyGen->SetTransform(l2w, w2l);
return (*this);
}
void plWinAudible::SetTime(double t, int index)
{
SND_APPLY_LOOP( index, SetTime( t ), ; );
}
void plWinAudible::SynchedPlay(int index)
{
for(int i = 0; i < fSoundObjs.Count(); ++i)
{
if(fSoundObjs[i]->IsPlaying())
{
fSoundObjs[index]->SynchedPlay(fSoundObjs[i]->GetByteOffset());
return;
}
}
}
void plWinAudible::Play(int index )
{
// hsStatusMessageF( "Playing sound %s, index %d, time=%f\n", GetKeyName(), index, hsTimer::GetSeconds());
SND_APPLY_LOOP( index, Play(), ; );
}
void plWinAudible::FastForwardPlay(int index /* = -1 */)
{
SND_APPLY_LOOP(index, FastForwardPlay(), ; );
}
void plWinAudible::FastForwardToggle(int index /* = -1 */)
{
SND_APPLY_LOOP(index, FastForwardToggle(), ; );
}
void plWinAudible::SetOuterVol(const int v, int index)
{
SND_APPLY_LOOP( index, SetOuterVolume( v ), ; );
}
void plWinAudible::SetConeAngles(int inner, int outer, int index)
{
SND_APPLY_LOOP( index, SetConeAngles( inner, outer ), ; );
}
void plWinAudible::Stop(int index)
{
SND_APPLY_LOOP( index, Stop(), ; );
}
void plWinAudible::SetMin(const hsScalar m,int index)
{
SND_APPLY_LOOP( index, SetMin( (int)m ), ; );
}
void plWinAudible::SetMax(const hsScalar m,int index)
{
SND_APPLY_LOOP( index, SetMax( (int)m ), ; );
}
// Takes a 0-1.f as the volume (platform independent)
void plWinAudible::SetVolume(const float volume,int index )
{
SND_APPLY_LOOP( index, SetVolume( volume ), ; );
}
void plWinAudible::SetMuted( hsBool muted, int index )
{
SND_APPLY_LOOP( index, SetMuted( muted ), ; );
}
void plWinAudible::ToggleMuted( int index )
{
if( index != -1 )
{
SND_INDEX_CHECK( index, ; );
if( fSoundObjs[ index ] != nil )
fSoundObjs[ index ]->SetMuted( !fSoundObjs[ index ]->IsMuted() );
}
else
{
for( int i = 0; i < fSoundObjs.Count(); i++ )
{
if( fSoundObjs[ i ] != nil )
fSoundObjs[ i ]->SetMuted( !fSoundObjs[ i ]->IsMuted() );
}
}
}
hsScalar plWinAudible::GetMin(int index) const
{
return (hsScalar)(fSoundObjs[index]->GetMin());
}
hsScalar plWinAudible::GetMax(int index) const
{
return (hsScalar)(fSoundObjs[index]->GetMax());
}
void plWinAudible::SetVelocity(const hsVector3 vel,int index)
{
SND_APPLY_LOOP( index, SetVelocity( vel ), ; );
}
hsVector3 plWinAudible::GetVelocity(int index) const
{
return(fSoundObjs[index]->GetVelocity());
}
hsPoint3 plWinAudible::GetPosition(int index)
{
return( fSoundObjs[index]->GetPosition() );
}
void plWinAudible::SetLooping(hsBool loop,int index)
{
SND_APPLY_LOOP( index, SetProperty( plSound::kPropLooping, loop ), ; );
}
void plWinAudible::SetFadeIn( const int type, const float length, int index )
{
SND_APPLY_LOOP( index, SetFadeInEffect( (plSound::plFadeParams::Type)type, length ), ; );
}
void plWinAudible::SetFadeOut( const int type, const float length, int index )
{
SND_APPLY_LOOP( index, SetFadeOutEffect( (plSound::plFadeParams::Type)type, length ), ; );
}
void plWinAudible::SetFilename(int index, const char *filename, hsBool isCompressed)
{
if(index < 0 || index >= fSoundObjs.Count())
{
hsStatusMessageF( "ERROR: Sound index out of range (index %d, count %d)\n", index, fSoundObjs.GetCount() );
return;
}
plWin32StreamingSound *pStreamingSound = plWin32StreamingSound::ConvertNoRef(fSoundObjs[ index ]);
if(pStreamingSound)
{
pStreamingSound->SetFilename(filename, isCompressed);
}
else
{
plStatusLog::AddLineS("audio.log", "Cannot set filename of non-streaming sound. %s", fSoundObjs[ index ]->GetKeyName());
}
}
hsBool plWinAudible::IsPlaying(int index)
{
int count = fSoundObjs.Count();
if(index < count)
{
if (index == -1)
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
if( fSoundObjs[ i ] != nil && fSoundObjs[i]->IsPlaying() )
return true;
}
return false;
}
if( fSoundObjs[ index ] == nil )
return false;
return(fSoundObjs[index]->IsPlaying());
} else {
return false;
}
}
void plWinAudible::Read(hsStream* s, hsResMgr* mgr)
{
plAudible::Read(s, mgr);
int n = s->ReadSwap32();
fSoundObjs.SetCountAndZero(n);
for(int i = 0; i < n; i++ )
{
plGenRefMsg* msg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, i, 0);
mgr->ReadKeyNotifyMe(s, msg, plRefFlags::kActiveRef);
//plSound* pSnd = plSound::ConvertNoRef(mgr->ReadCreatable(s));
//IAssignSoundKey( pSnd, GetKey() ? GetKeyName() : "", i );
//if (plWin32LinkSound::ConvertNoRef(pSnd))
// plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), pSnd->GetKey());
//fSoundObjs[i] = pSnd;
}
plKey pSceneKey = mgr->ReadKey(s);
plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(pSceneKey, plRefMsg::kOnCreate, -1, plNodeRefMsg::kAudible);
mgr->AddViaNotify(GetKey(), refMsg, plRefFlags::kPassiveRef);
}
void plWinAudible::IAssignSoundKey( plSound *sound, const char *name, UInt32 i )
{
char keyName[ 256 ];
sprintf( keyName, "%s_%d", name, i );
hsgResMgr::ResMgr()->NewKey( keyName, sound, GetKey() ? GetKey()->GetUoid().GetLocation() : plLocation::kGlobalFixedLoc );
}
void plWinAudible::Write(hsStream* s, hsResMgr* mgr)
{
plAudible::Write(s, mgr);
s->WriteSwap32(fSoundObjs.GetCount());
for(int i = 0; i < fSoundObjs.GetCount(); i++ )
// mgr->WriteCreatable( s, fSoundObjs[i] );
mgr->WriteKey(s, fSoundObjs[i]);
mgr->WriteKey(s, fSceneNode);
}
void plWinAudible::Activate()
{
for (int i = 0; i < fSoundObjs.Count(); i++)
if (fSoundObjs[i] != nil)
fSoundObjs[i]->Activate();
}
void plWinAudible::DeActivate()
{
for (int i = 0; i < fSoundObjs.Count(); i++)
{
if (fSoundObjs[i] != nil)
{
fSoundObjs[i]->DeActivate();
}
}
}
hsBool plWinAudible::MsgReceive(plMessage* msg)
{
plGenRefMsg *refMsg;
if (refMsg = plGenRefMsg::ConvertNoRef(msg))
{
plSound *snd;
if (snd = plSound::ConvertNoRef(refMsg->GetRef()))
{
int index = refMsg->fWhich;
if( refMsg->GetContext() & (plRefMsg::kOnCreate | plRefMsg::kOnRequest) )
{
int i = fSoundObjs.Count();
if (index >= i)
{
fSoundObjs.ExpandAndZero(index + 1);
}
fSoundObjs[index] = plSound::ConvertNoRef(refMsg->GetRef());
if (plgAudioSys::Active())
fSoundObjs[index]->Activate();
}
else if( refMsg->GetContext() & (plRefMsg::kOnRemove | plRefMsg::kOnDestroy) )
{
int i = fSoundObjs.Count();
if (index < i)
{
fSoundObjs[index] = nil;
}
}
return true;
}
}
plAudioSysMsg *sysMsg = plAudioSysMsg::ConvertNoRef( msg );
if( sysMsg )
{
if( sysMsg->GetAudFlag() == plAudioSysMsg::kChannelVolChanged )
{
SND_APPLY_LOOP( -1, RefreshVolume(), false; );
}
}
// proxyDrawMsg handling--just pass it on to the proxy object
plProxyDrawMsg *pdMsg = plProxyDrawMsg::ConvertNoRef( msg );
if( pdMsg != nil )
{
if( fProxyGen )
return fProxyGen->MsgReceive( pdMsg );
return true;
}
return plAudible::MsgReceive(msg);
}
void plWinAudible::RemoveCallbacks(plSoundMsg* pMsg)
{
// sanity check
if (pMsg->fIndex >= fSoundObjs.Count())
return;
if( pMsg->fIndex < 0 )
{
int i;
for( i = 0; i < fSoundObjs.Count(); i++ )
fSoundObjs[i]->RemoveCallbacks(pMsg);
}
else
{
fSoundObjs[pMsg->fIndex]->RemoveCallbacks(pMsg);
}
}
void plWinAudible::AddCallbacks(plSoundMsg* pMsg)
{
// sanity check
if (pMsg->fIndex >= fSoundObjs.Count())
return;
int i;
for( i = 0; i < pMsg->GetNumCallbacks(); i++ )
{
pMsg->GetEventCallback(i)->fIndex = pMsg->fIndex;
pMsg->GetEventCallback(i)->SetSender(GetKey());
}
if( pMsg->fIndex < 0 )
{
for( i = 0; i < fSoundObjs.Count(); i++ )
fSoundObjs[i]->AddCallbacks(pMsg);
}
else
{
fSoundObjs[pMsg->fIndex]->AddCallbacks(pMsg);
}
}
int plWinAudible::GetSoundIndex(const char *keyname) const
{
for( int i = 0; i < fSoundObjs.Count(); i++)
{
if(!fSoundObjs[i]) continue;
if(!strcmp(fSoundObjs[i]->GetKeyName(), keyname ))
{
return i;
}
}
return -1;
}
plSound* plWinAudible::GetSound(int i) const
{
return fSoundObjs[i];
}
// Visualization
plDrawableSpans* plWinAudible::CreateProxy(hsGMaterial* mat, hsTArray& idx, plDrawableSpans* addTo)
{
plDrawableSpans* myDraw = addTo;
int i;
for( i = 0; i < fSoundObjs.Count(); i++ )
myDraw = fSoundObjs[i]->CreateProxy(fLocalToWorld, mat, idx, myDraw);
return myDraw;
}
//
// 2-way win audible (for local players)
//
pl2WayWinAudible::pl2WayWinAudible() :
fVoicePlayer(nil),
fVoiceRecorder(nil),
fActive(false)
{
}
pl2WayWinAudible::~pl2WayWinAudible()
{
DeActivate();
if (fVoicePlayer)
delete fVoicePlayer;
if(fVoiceRecorder)
delete fVoiceRecorder;
}
hsBool pl2WayWinAudible::MsgReceive(plMessage* msg)
{
plEvalMsg* pMsg = plEvalMsg::ConvertNoRef(msg);
if (pMsg && fVoiceRecorder)
{
fVoiceRecorder->Update(pMsg->GetTimeStamp());
// fVoxIO->Update();
return true;
}
plControlEventMsg* pCtrlMsg = plControlEventMsg::ConvertNoRef(msg);
if (pCtrlMsg)
{
if (pCtrlMsg->GetControlCode() == S_PUSH_TO_TALK && fVoiceRecorder)
fVoiceRecorder->SetMikeOpen(pCtrlMsg->ControlActivated());
return true;
}
return plWinAudible::MsgReceive(msg);
}
void pl2WayWinAudible::Init(hsBool isLocal)
{
if (!fVoicePlayer)
{
if(!isLocal)
fVoicePlayer = TRACKED_NEW plVoicePlayer;
}
if(!fVoiceRecorder)
{
if(isLocal)
{
fVoiceRecorder = TRACKED_NEW plVoiceRecorder;
}
}
Activate();
}
void pl2WayWinAudible::Activate()
{
if(fActive)
return;
plWinAudible::Activate();
if(fVoicePlayer)
{
fVoicePlayer->GetSoundPtr()->Activate();
fActive = true;
}
if (fVoiceRecorder)
{
plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg;
pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
pModMsg->SetSender(GetKey());
pModMsg->SetCmd(plCmdIfaceModMsg::kAdd);
plgDispatch::MsgSend(pModMsg);
fActive = true;
}
}
void pl2WayWinAudible::DeActivate()
{
if(!fActive)
return;
plWinAudible::DeActivate();
if(fVoicePlayer)
{
fVoicePlayer->GetSoundPtr()->DeActivate();
fActive = false;
}
if (fVoiceRecorder)
{
plCmdIfaceModMsg* pModMsg = TRACKED_NEW plCmdIfaceModMsg;
pModMsg->SetBCastFlag(plMessage::kBCastByExactType);
pModMsg->SetSender(GetKey());
pModMsg->SetCmd(plCmdIfaceModMsg::kRemove);
plgDispatch::MsgSend(pModMsg);
fActive = false;
}
}
void pl2WayWinAudible::Read(hsStream* s, hsResMgr* mgr)
{
plWinAudible::Read(s, mgr);
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
}
void pl2WayWinAudible::PlayNetworkedSpeech(const char* addr, Int32 size, int numFrames, BYTE flags)
{
if (fVoicePlayer)
{
if (!(flags & VOICE_ENCODED))
fVoicePlayer->PlaybackUncompressedVoiceMessage((BYTE*)addr, size);
else
fVoicePlayer->PlaybackVoiceMessage((BYTE*)addr, size, numFrames);
}
}
plAudible& pl2WayWinAudible::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l, int index)
{
plWinAudible::SetTransform(l2w, w2l, index);
if (fVoicePlayer)
{
hsPoint3 pt = l2w.GetTranslate();
// adjust to head level
pt.fZ += 6.33f;
fVoicePlayer->SetPosition( pt );
hsPoint3 v(l2w.GetAxis(hsMatrix44::kView));
fVoicePlayer->SetOrientation(v);
}
return (*this);
}
void pl2WayWinAudible::SetVelocity(const hsVector3 vel,int index)
{
plWinAudible::SetVelocity(vel, index);
if (fVoicePlayer)
fVoicePlayer->SetVelocity( vel );
}
void pl2WayWinAudible::SetTalkIcon(int index, UInt32 str)
{
if (fVoicePlayer)
fVoicePlayer->SetTalkIcon(index,str);
}
void pl2WayWinAudible::ClearTalkIcon()
{
if (fVoicePlayer)
fVoicePlayer->ClearTalkIcon();
}