📄 audioemitter.cc
字号:
//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "game/audioEmitter.h"
#include "game/ambientAudioManager.h"
#include "console/consoleTypes.h"
#include "editor/editor.h"
#include "dgl/dgl.h"
#include "sceneGraph/sceneState.h"
#include "game/gameConnection.h"
//------------------------------------------------------------------------------
static MRandomLCG sgRandom(0xdeadbeef);
#define UPDATE_BUMP_MS 50
extern ALuint alxGetWaveLen(ALuint buffer);
extern ALuint alxFindSource(AUDIOHANDLE handle);
IMPLEMENT_CO_NETOBJECT_V1(AudioEmitter);
//------------------------------------------------------------------------------
class AudioEmitterLoopEvent : public SimEvent
{
public:
void process(SimObject * object) {
((AudioEmitter*)object)->processLoopEvent();
}
};
//------------------------------------------------------------------------------
AudioEmitter::AudioEmitter(bool client)
{
// ::packData depends on these values... make sure it is updated with changes
mTypeMask |= MarkerObjectType;
if(client)
mNetFlags = 0;
else
mNetFlags.set(Ghostable|ScopeAlways);
mAudioHandle = NULL_AUDIOHANDLE;
mAudioProfileId = 0;
mAudioDescriptionId = 0;
mLoopCount = smDefaultDescription.mLoopCount;
mEventID = 0;
mOwnedByClient = false;
// field defaults
mAudioProfile = 0;
mAudioDescription = 0;
mFilename = 0;
mUseProfileDescription = false;
mOutsideAmbient = true;
mDescription = smDefaultDescription;
mEnableVisualFeedback = true;
mAnimRotAngle=0.f;
}
Audio::Description AudioEmitter::smDefaultDescription;
//------------------------------------------------------------------------------
void AudioEmitter::processLoopEvent()
{
if(mLoopCount == 0)
return;
else if(mLoopCount > 0)
mLoopCount--;
if(mUseProfileDescription && mAudioProfileId)
return;
if(!mDescription.mIsLooping || ((mDescription.mLoopCount <= 0) && !mDescription.mMaxLoopGap))
return;
// check if still playing...
U32 source = alxFindSource(mAudioHandle);
if(source != -1)
{
ALint state = AL_STOPPED;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if(state == AL_PLAYING)
{
mEventID = Sim::postEvent(this, new AudioEmitterLoopEvent, Sim::getCurrentTime() + UPDATE_BUMP_MS);
return;
}
}
mEventID = 0;
mDirty.set(SourceMask);
update();
}
//------------------------------------------------------------------------------
bool AudioEmitter::update()
{
// object does not know it is really a client object if created through event
AssertFatal(isClientObject() || mOwnedByClient, "AudioEmitter::update: only clients can update!");
if(mDirty.test(LoopCount))
mLoopCount = mDescription.mLoopCount;
// updating source?
if(mDirty.test(SourceMask|UseProfileDescription) || (!mUseProfileDescription &&
((mDescription.mIsLooping && mDirty.test(LoopingMask)) || mDirty.test(IsLooping|Is3D|AudioType))))
{
if(mAudioHandle != NULL_AUDIOHANDLE)
{
alxStop(mAudioHandle);
mAudioHandle = NULL_AUDIOHANDLE;
}
// profile:
if(mDirty.test(Profile))
{
if(!mAudioProfileId)
mAudioProfile = 0;
else
mAudioProfile = dynamic_cast<AudioProfile*>(Sim::findObject(mAudioProfileId));
}
// description:
if(mDirty.test(Description))
{
if(!mAudioDescriptionId)
mAudioDescription = 0;
else
mAudioDescription = dynamic_cast<AudioDescription*>(Sim::findObject(mAudioDescriptionId));
}
MatrixF transform = getTransform();
// use the profile?
if(mUseProfileDescription && mAudioProfileId)
mAudioHandle = alxCreateSource(mAudioProfile, &transform);
else
{
// grab the filename
const char * fileName = 0;
if(mFilename && mFilename[0])
fileName = mFilename;
else if(mAudioProfile)
fileName = mAudioProfile->mFilename;
if(!fileName)
{
Con::errorf(ConsoleLogEntry::General, "AudioEmitter::update: invalid audio filename!");
return(false);
}
// use a description?
if(mAudioDescription)
mAudioHandle = alxCreateSource(mAudioDescription, fileName, &transform);
else
{
if(mDescription.mIsLooping)
{
S32 minGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
S32 maxGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
// controlling looping?
if((mDescription.mLoopCount != -1) || maxGap)
{
mDescription.mIsLooping = false;
mAudioHandle = alxCreateSource(&mDescription, fileName, &transform);
mDescription.mIsLooping = true;
// handle may come back as null.. (0 volume, no handles, ...), still
// want to try again (assume good buffer) since this is a looper
Resource<AudioBuffer> buffer = AudioBuffer::find(fileName);
S32 waveLen = 0;
if(bool(buffer))
{
ALuint alBuffer = buffer->getALBuffer();
waveLen = alxGetWaveLen(alBuffer);
}
if(waveLen)
{
S32 offset = waveLen + minGap + sgRandom.randI(minGap, maxGap);
if(mEventID)
Sim::cancelEvent(mEventID);
mEventID = Sim::postEvent(this, new AudioEmitterLoopEvent, Sim::getCurrentTime() + offset);
if(mAudioHandle == NULL_AUDIOHANDLE)
return(true);
}
}
else
mAudioHandle = alxCreateSource(&mDescription, fileName, &transform);
}
else
mAudioHandle = alxCreateSource(&mDescription, fileName, &transform);
}
}
if(mAudioHandle == NULL_AUDIOHANDLE)
{
Con::errorf(ConsoleLogEntry::General, "AudioEmitter::update: failed to create source!");
return(false);
}
alxPlay(mAudioHandle);
}
else
{
// don't clear dirty flags until there is a source
if(mAudioHandle == NULL_AUDIOHANDLE)
return(true);
if(mDirty.test(Transform))
{
// Update the position.
Point3F pos;
mObjToWorld.getColumn(3, &pos);
alxSource3f(mAudioHandle, AL_POSITION, pos.x, pos.y, pos.z);
//Make sure we update the direction also.
mObjToWorld.getColumn(1, &pos);
alSource3f(mAudioHandle, AL_DIRECTION, pos.x, pos.y, pos.z);
}
// grab the description
const Audio::Description * desc = 0;
if(mUseProfileDescription && mAudioProfile)
desc = mAudioProfile->getDescription();
else if(mAudioDescription)
desc = mAudioDescription->getDescription();
else
desc = &mDescription;
if(!desc)
return(true);
if(mDirty.test(Volume))
alxSourcef(mAudioHandle, AL_GAIN_LINEAR, desc->mVolume);
if(mDirty.test(ReferenceDistance|MaxDistance))
{
alxSourcef(mAudioHandle, AL_REFERENCE_DISTANCE, desc->mReferenceDistance);
alxSourcef(mAudioHandle, AL_MAX_DISTANCE, desc->mMaxDistance);
}
if(mDirty.test(ConeInsideAngle))
alxSourcei(mAudioHandle, AL_CONE_INNER_ANGLE, desc->mConeInsideAngle);
if(mDirty.test(ConeOutsideAngle))
alxSourcei(mAudioHandle, AL_CONE_OUTER_ANGLE, desc->mConeOutsideAngle);
if(mDirty.test(ConeOutsideVolume))
alxSourcef(mAudioHandle, AL_CONE_OUTER_GAIN, desc->mConeOutsideVolume);
}
mDirty.clear();
return(true);
}
//------------------------------------------------------------------------------
bool AudioEmitter::onAdd()
{
if(!Parent::onAdd())
return(false);
// object does not know it is really a client object if created through event
if(isServerObject() && !mOwnedByClient)
{
// validate the object data
mDescription.mVolume = mClampF(mDescription.mVolume, 0.0f, 1.0f);
mDescription.mLoopCount = mClamp(mDescription.mLoopCount, -1, mDescription.mLoopCount);
mDescription.mMaxLoopGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
mDescription.mMinLoopGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
if(mDescription.mIs3D)
{
mDescription.mReferenceDistance = mClampF(mDescription.mReferenceDistance, 0.f, mDescription.mReferenceDistance);
mDescription.mMaxDistance = (mDescription.mMaxDistance > mDescription.mReferenceDistance) ? mDescription.mMaxDistance : (mDescription.mReferenceDistance+0.01f);
mDescription.mConeInsideAngle = mClamp(mDescription.mConeInsideAngle, 0, 360);
mDescription.mConeOutsideAngle = mClamp(mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, 360);
mDescription.mConeOutsideVolume = mClampF(mDescription.mConeOutsideVolume, 0.0f, 1.0f);
mDescription.mConeVector.normalize();
}
#ifndef TGE_RPGCLIENT2 /// TGE_RPGIsServerObject
}
else
{
#endif
if(!update())
{
Con::errorf(ConsoleLogEntry::General, "AudioEmitter::onAdd: client failed initial update!");
if(mOwnedByClient)
return(false);
}
gAmbientAudioManager.addEmitter(this);
}
//
mObjBox.max = mObjScale;
mObjBox.min = mObjScale;
mObjBox.min.neg();
resetWorldBox();
addToScene();
return(true);
}
void AudioEmitter::onRemove()
{
if(isClientObject())
{
gAmbientAudioManager.removeEmitter(this);
if(mAudioHandle != NULL_AUDIOHANDLE)
alxStop(mAudioHandle);
}
removeFromScene();
Parent::onRemove();
}
void AudioEmitter::setTransform(const MatrixF & mat)
{
// Set the transform directly from the matrix created by inspector
// Also, be sure to strip out the pointing vector and place it in the cone vector
Point3F pointing;
mat.getColumn(1, &pointing);
mDescription.mConeVector = pointing;
Parent::setTransform(mat);
setMaskBits(TransformUpdateMask);
}
void AudioEmitter::setScale(const VectorF &)
{
}
//------------------------------------------------------------------------------
namespace {
static Point3F cubePoints[8] = {
Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1),
Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1)
};
static U32 cubeFaces[6][4] = {
{ 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
{ 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
};
static Point2F textureCoords[4] = {
Point2F(0, 0), Point2F(1, 0), Point2F(1, 1), Point2F(0, 1)
};
}
bool AudioEmitter::prepRenderImage(SceneState * state, const U32 stateKey, const U32, const bool)
{
if(!gEditingMission || isLastState(state, stateKey))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -