blender/source/gameengine/Ketsji/KX_SoundActuator.cpp

547 lines
15 KiB
C++
Raw Normal View History

/*
2002-10-12 11:37:38 +00:00
* KX_SoundActuator.cpp
*
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
2002-10-12 11:37:38 +00:00
*
* 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 2
* of the License, or (at your option) any later version.
2002-10-12 11:37:38 +00:00
*
* 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, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2002-10-12 11:37:38 +00:00
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
2002-10-12 11:37:38 +00:00
*
*/
2011-02-25 13:35:59 +00:00
/** \file gameengine/Ketsji/KX_SoundActuator.cpp
* \ingroup ketsji
*/
2002-10-12 11:37:38 +00:00
#include "KX_SoundActuator.h"
#ifdef WITH_AUDASPACE
# include "AUD_C-API.h"
#endif
2002-10-12 11:37:38 +00:00
#include "KX_GameObject.h"
2009-04-09 20:40:12 +00:00
#include "KX_PyMath.h" // needed for PyObjectFrom()
2002-10-12 11:37:38 +00:00
#include <iostream>
/* ------------------------------------------------------------------------- */
/* Native functions */
/* ------------------------------------------------------------------------- */
KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
AUD_Sound* sound,
float volume,
float pitch,
bool is3d,
KX_3DSoundSettings settings,
KX_SOUNDACT_TYPE type)//,
: SCA_IActuator(gameobj, KX_ACT_SOUND)
2002-10-12 11:37:38 +00:00
{
m_sound = AUD_copy(sound);
m_volume = volume;
m_pitch = pitch;
m_is3d = is3d;
m_3d = settings;
m_handle = NULL;
2002-10-12 11:37:38 +00:00
m_type = type;
m_isplaying = false;
}
KX_SoundActuator::~KX_SoundActuator()
{
if(m_handle)
AUD_stop(m_handle);
AUD_unload(m_sound);
2002-10-12 11:37:38 +00:00
}
void KX_SoundActuator::play()
{
if(m_handle)
{
AUD_stop(m_handle);
m_handle = NULL;
}
if(!m_sound)
return;
// this is the sound that will be played and not deleted afterwards
AUD_Sound* sound = m_sound;
// this sound is for temporary stacked sounds, will be deleted if not NULL
AUD_Sound* sound2 = NULL;
bool loop = false;
2002-10-12 11:37:38 +00:00
switch (m_type)
{
case KX_SOUNDACT_LOOPBIDIRECTIONAL:
case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
sound = sound2 = AUD_pingpongSound(sound);
// fall through
case KX_SOUNDACT_LOOPEND:
case KX_SOUNDACT_LOOPSTOP:
loop = true;
break;
case KX_SOUNDACT_PLAYSTOP:
case KX_SOUNDACT_PLAYEND:
default:
break;
}
m_handle = AUD_play(sound, 0);
if(sound2)
AUD_unload(sound2);
if(!m_handle)
return;
if(m_is3d)
{
AUD_setRelative(m_handle, false);
AUD_setVolumeMaximum(m_handle, m_3d.max_gain);
AUD_setVolumeMinimum(m_handle, m_3d.min_gain);
AUD_setDistanceReference(m_handle, m_3d.reference_distance);
AUD_setDistanceMaximum(m_handle, m_3d.max_distance);
AUD_setAttenuation(m_handle, m_3d.rolloff_factor);
AUD_setConeAngleInner(m_handle, m_3d.cone_inner_angle);
AUD_setConeAngleOuter(m_handle, m_3d.cone_outer_angle);
AUD_setConeVolumeOuter(m_handle, m_3d.cone_outer_gain);
}
if(loop)
AUD_setLoop(m_handle, -1);
AUD_setSoundPitch(m_handle, m_pitch);
AUD_setSoundVolume(m_handle, m_volume);
m_isplaying = true;
}
2002-10-12 11:37:38 +00:00
CValue* KX_SoundActuator::GetReplica()
{
KX_SoundActuator* replica = new KX_SoundActuator(*this);
replica->ProcessReplica();
BGE performance, 4th round: logic This commit extends the technique of dynamic linked list to the logic system to eliminate as much as possible temporaries, map lookup or full scan. The logic engine is now free of memory allocation, which is an important stability factor. The overhead of the logic system is reduced by a factor between 3 and 6 depending on the logic setup. This is the speed-up you can expect on a logic setup using simple bricks. Heavy bricks like python controllers and ray sensors will still take about the same time to execute so the speed up will be less important. The core of the logic engine has been much reworked but the functionality is still the same except for one thing: the priority system on the execution of controllers. The exact same remark applies to actuators but I'll explain for controllers only: Previously, it was possible, with the "executePriority" attribute to set a controller to run before any other controllers in the game. Other than that, the sequential execution of controllers, as defined in Blender was guaranteed by default. With the new system, the sequential execution of controllers is still guaranteed but only within the controllers of one object. the user can no longer set a controller to run before any other controllers in the game. The "executePriority" attribute controls the execution of controllers within one object. The priority is a small number starting from 0 for the first controller and incrementing for each controller. If this missing feature is a must, a special method can be implemented to set a controller to run before all other controllers. Other improvements: - Systematic use of reference in parameter passing to avoid unnecessary data copy - Use pre increment in iterator instead of post increment to avoid temporary allocation - Use const char* instead of STR_String whenever possible to avoid temporary allocation - Fix reference counting bugs (memory leak) - Fix a crash in certain cases of state switching and object deletion - Minor speed up in property sensor - Removal of objects during the game is a lot faster
2009-05-10 20:53:58 +00:00
return replica;
};
void KX_SoundActuator::ProcessReplica()
{
SCA_IActuator::ProcessReplica();
m_handle = 0;
}
2002-10-12 11:37:38 +00:00
bool KX_SoundActuator::Update(double curtime, bool frame)
2002-10-12 11:37:38 +00:00
{
if (!frame)
return true;
2002-10-12 11:37:38 +00:00
bool result = false;
// do nothing on negative events, otherwise sounds are played twice!
bool bNegativeEvent = IsNegativeEvent();
bool bPositiveEvent = m_posevent;
2002-10-12 11:37:38 +00:00
RemoveAllEvents();
if(!m_sound)
return false;
// actual audio device playing state
bool isplaying = m_handle ? (AUD_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
2002-10-12 11:37:38 +00:00
if (bNegativeEvent)
{
2002-10-12 11:37:38 +00:00
// here must be a check if it is still playing
if (m_isplaying && isplaying)
2002-10-12 11:37:38 +00:00
{
switch (m_type)
2002-10-12 11:37:38 +00:00
{
case KX_SOUNDACT_PLAYSTOP:
case KX_SOUNDACT_LOOPSTOP:
case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
{
// stop immediately
if(m_handle)
AUD_stop(m_handle);
m_handle = NULL;
break;
}
case KX_SOUNDACT_PLAYEND:
{
// do nothing, sound will stop anyway when it's finished
break;
}
case KX_SOUNDACT_LOOPEND:
case KX_SOUNDACT_LOOPBIDIRECTIONAL:
{
// stop the looping so that the sound stops when it finished
if(m_handle)
AUD_setLoop(m_handle, 0);
break;
}
default:
// implement me !!
2002-10-12 11:37:38 +00:00
break;
}
}
// remember that we tried to stop the actuator
m_isplaying = false;
2002-10-12 11:37:38 +00:00
}
#if 1
// Warning: when de-activating the actuator, after a single negative event this runs again with...
// m_posevent==false && m_posevent==false, in this case IsNegativeEvent() returns false
// and assumes this is a positive event.
// check that we actually have a positive event so as not to play sounds when being disabled.
else if(bPositiveEvent) { // <- added since 2.49
#else
else { // <- works in most cases except a loop-end sound will never stop unless
// the negative pulse is done continuesly
#endif
if (!m_isplaying)
play();
2002-10-12 11:37:38 +00:00
}
// verify that the sound is still playing
isplaying = m_handle ? (AUD_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
2002-10-12 11:37:38 +00:00
if (isplaying)
2002-10-12 11:37:38 +00:00
{
if(m_is3d)
{
KX_GameObject* obj = (KX_GameObject*)this->GetParent();
float f[4];
obj->NodeGetWorldPosition().getValue(f);
AUD_setSourceLocation(m_handle, f);
obj->GetLinearVelocity().getValue(f);
AUD_setSourceVelocity(m_handle, f);
obj->NodeGetWorldOrientation().getRotation().getValue(f);
AUD_setSourceOrientation(m_handle, f);
}
2002-10-12 11:37:38 +00:00
result = true;
}
else
{
m_isplaying = false;
2002-10-12 11:37:38 +00:00
result = false;
}
return result;
}
#ifdef WITH_PYTHON
2002-10-12 11:37:38 +00:00
/* ------------------------------------------------------------------------- */
/* Python functions */
/* ------------------------------------------------------------------------- */
/* Integration hooks ------------------------------------------------------- */
PyTypeObject KX_SoundActuator::Type = {
PyVarObject_HEAD_INIT(NULL, 0)
2009-08-10 00:07:34 +00:00
"KX_SoundActuator",
sizeof(PyObjectPlus_Proxy),
0,
py_base_dealloc,
0,
0,
0,
0,
py_base_repr,
0,0,0,0,0,0,0,0,0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
0,0,0,0,0,0,0,
Methods,
0,
0,
&SCA_IActuator::Type,
0,0,0,0,0,0,
py_base_new
2002-10-12 11:37:38 +00:00
};
PyMethodDef KX_SoundActuator::Methods[] = {
2009-04-09 20:40:12 +00:00
KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
{NULL, NULL} //Sentinel
2002-10-12 11:37:38 +00:00
};
PyAttributeDef KX_SoundActuator::Attributes[] = {
KX_PYATTRIBUTE_BOOL_RO("is3D", KX_SoundActuator, m_is3d),
KX_PYATTRIBUTE_RW_FUNCTION("volume_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("volume_minimum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("distance_reference", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("distance_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("attenuation", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_inner", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("cone_volume_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
KX_PYATTRIBUTE_RW_FUNCTION("sound", KX_SoundActuator, pyattr_get_sound, pyattr_set_sound),
KX_PYATTRIBUTE_RW_FUNCTION("time", KX_SoundActuator, pyattr_get_audposition, pyattr_set_audposition),
2009-04-09 20:40:12 +00:00
KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
Name attributes added since 2.48a more consistently. BL_ActionActuator::blendin -> blendIn BL_ActionActuator::end -> frameEnd BL_ActionActuator::property -> propName BL_ActionActuator::start -> frameStart BL_ActionActuator::type -> mode BL_ShapeActionActuator::blendin -> blendIn BL_ShapeActionActuator::end -> frameEnd BL_ShapeActionActuator::frameProperty -> framePropName BL_ShapeActionActuator::property -> propName BL_ShapeActionActuator::start -> frameStart BL_ShapeActionActuator::type -> mode KX_CameraActuator::xy -> useXY KX_ConstraintActuator::property -> propName KX_GameActuator::file -> fileName KX_GameObject::localScaling -> localScaling KX_GameObject::worldScaling -> worldScaling KX_IpoActuator::endFrame -> frameEnd KX_IpoActuator::startFrame -> frameStart KX_IpoActuator::type -> mode KX_RaySensor::property -> propName KX_SCA_DynamicActuator::operation -> mode KX_Scene::objects_inactive -> objectsInactive KX_SoundActuator::filename -> fileName KX_SoundActuator::type -> mode KX_TouchSensor::objectHit -> hitObject KX_TouchSensor::objectHitList -> hitObjectList KX_TouchSensor::property -> propName KX_TouchSensor::pulseCollisions -> usePulseCollision KX_VisibilityActuator::occlusion -> useOcclusion KX_VisibilityActuator::recursion -> useRecursion SCA_2DFilterActuator::passNb -> passNumber SCA_PropertyActuator::property -> propName SCA_PropertyActuator::type -> mode SCA_PropertySensor::property -> propName SCA_PropertySensor::type -> mode SCA_RandomActuator::property -> propName
2009-05-15 03:26:53 +00:00
KX_PYATTRIBUTE_ENUM_RW("mode",KX_SoundActuator::KX_SOUNDACT_NODEF+1,KX_SoundActuator::KX_SOUNDACT_MAX-1,false,KX_SoundActuator,m_type),
{ NULL } //Sentinel
};
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
/* Methods ----------------------------------------------------------------- */
KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
2009-04-09 20:40:12 +00:00
"startSound()\n"
"\tStarts the sound.\n")
2002-10-12 11:37:38 +00:00
{
if(m_handle)
{
switch(AUD_getStatus(m_handle))
{
case AUD_STATUS_PLAYING:
break;
case AUD_STATUS_PAUSED:
AUD_resume(m_handle);
break;
default:
play();
}
}
2009-04-09 20:40:12 +00:00
Py_RETURN_NONE;
}
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
"pauseSound()\n"
"\tPauses the sound.\n")
2002-10-12 11:37:38 +00:00
{
if(m_handle)
AUD_pause(m_handle);
2009-04-09 20:40:12 +00:00
Py_RETURN_NONE;
}
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
"stopSound()\n"
"\tStops the sound.\n")
{
if(m_handle)
AUD_stop(m_handle);
m_handle = NULL;
Py_RETURN_NONE;
2002-10-12 11:37:38 +00:00
}
2009-04-09 20:40:12 +00:00
/* Atribute setting and getting -------------------------------------------- */
PyObject* KX_SoundActuator::pyattr_get_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
{
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
const char* prop = attrdef->m_name;
float result_value = 0.0;
if(!strcmp(prop, "volume_maximum")) {
result_value = actuator->m_3d.max_gain;
} else if (!strcmp(prop, "volume_minimum")) {
result_value = actuator->m_3d.min_gain;
} else if (!strcmp(prop, "distance_reference")) {
result_value = actuator->m_3d.reference_distance;
} else if (!strcmp(prop, "distance_maximum")) {
result_value = actuator->m_3d.max_distance;
} else if (!strcmp(prop, "attenuation")) {
result_value = actuator->m_3d.rolloff_factor;
} else if (!strcmp(prop, "cone_angle_inner")) {
result_value = actuator->m_3d.cone_inner_angle;
} else if (!strcmp(prop, "cone_angle_outer")) {
result_value = actuator->m_3d.cone_outer_angle;
} else if (!strcmp(prop, "cone_volume_outer")) {
result_value = actuator->m_3d.cone_outer_gain;
} else {
Py_RETURN_NONE;
}
PyObject* result = PyFloat_FromDouble(result_value);
return result;
}
PyObject* KX_SoundActuator::pyattr_get_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
{
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
float position = 0.0;
if(actuator->m_handle)
position = AUD_getPosition(actuator->m_handle);
PyObject* result = PyFloat_FromDouble(position);
return result;
}
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
PyObject* KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
{
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
float gain = actuator->m_volume;
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
PyObject* result = PyFloat_FromDouble(gain);
2009-04-09 20:40:12 +00:00
return result;
}
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
PyObject* KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
2002-10-12 11:37:38 +00:00
{
2009-04-09 20:40:12 +00:00
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
float pitch = actuator->m_pitch;
2009-04-09 20:40:12 +00:00
PyObject* result = PyFloat_FromDouble(pitch);
2009-04-09 20:40:12 +00:00
return result;
}
PyObject* KX_SoundActuator::pyattr_get_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
{
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
return AUD_getPythonFactory(actuator->m_sound);
}
int KX_SoundActuator::pyattr_set_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
const char* prop = attrdef->m_name;
float prop_value = 0.0;
if (!PyArg_Parse(value, "f", &prop_value))
return PY_SET_ATTR_FAIL;
// if sound is working and 3D, set the new setting
if(!actuator->m_is3d)
return PY_SET_ATTR_FAIL;
if(!strcmp(prop, "volume_maximum")) {
actuator->m_3d.max_gain = prop_value;
if(actuator->m_handle)
AUD_setVolumeMaximum(actuator->m_handle, prop_value);
} else if (!strcmp(prop, "volume_minimum")) {
actuator->m_3d.min_gain = prop_value;
if(actuator->m_handle)
AUD_setVolumeMinimum(actuator->m_handle, prop_value);
} else if (!strcmp(prop, "distance_reference")) {
actuator->m_3d.reference_distance = prop_value;
if(actuator->m_handle)
AUD_setDistanceReference(actuator->m_handle, prop_value);
} else if (!strcmp(prop, "distance_maximum")) {
actuator->m_3d.max_distance = prop_value;
if(actuator->m_handle)
AUD_setDistanceMaximum(actuator->m_handle, prop_value);
} else if (!strcmp(prop, "attenuation")) {
actuator->m_3d.rolloff_factor = prop_value;
if(actuator->m_handle)
AUD_setAttenuation(actuator->m_handle, prop_value);
} else if (!!strcmp(prop, "cone_angle_inner")) {
actuator->m_3d.cone_inner_angle = prop_value;
if(actuator->m_handle)
AUD_setConeAngleInner(actuator->m_handle, prop_value);
} else if (!strcmp(prop, "cone_angle_outer")) {
actuator->m_3d.cone_outer_angle = prop_value;
if(actuator->m_handle)
AUD_setConeAngleOuter(actuator->m_handle, prop_value);
} else if (!strcmp(prop, "cone_volume_outer")) {
actuator->m_3d.cone_outer_gain = prop_value;
if(actuator->m_handle)
AUD_setConeVolumeOuter(actuator->m_handle, prop_value);
} else {
return PY_SET_ATTR_FAIL;
}
return PY_SET_ATTR_SUCCESS;
}
int KX_SoundActuator::pyattr_set_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
float position = 1.0;
if (!PyArg_Parse(value, "f", &position))
return PY_SET_ATTR_FAIL;
if(actuator->m_handle)
AUD_seek(actuator->m_handle, position);
return PY_SET_ATTR_SUCCESS;
}
2009-04-09 20:40:12 +00:00
int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
float gain = 1.0;
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
if (!PyArg_Parse(value, "f", &gain))
return PY_SET_ATTR_FAIL;
actuator->m_volume = gain;
if(actuator->m_handle)
AUD_setSoundVolume(actuator->m_handle, gain);
return PY_SET_ATTR_SUCCESS;
}
2002-10-12 11:37:38 +00:00
2009-04-09 20:40:12 +00:00
int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
float pitch = 1.0;
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
if (!PyArg_Parse(value, "f", &pitch))
return PY_SET_ATTR_FAIL;
actuator->m_pitch = pitch;
if(actuator->m_handle)
AUD_setSoundPitch(actuator->m_handle, pitch);
return PY_SET_ATTR_SUCCESS;
}
2009-04-09 20:40:12 +00:00
int KX_SoundActuator::pyattr_set_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
{
PyObject* sound = NULL;
KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
if (!PyArg_Parse(value, "O", &sound))
return PY_SET_ATTR_FAIL;
AUD_Sound* snd = AUD_getPythonSound(sound);
if(snd)
{
AUD_unload(actuator->m_sound);
actuator->m_sound = snd;
return PY_SET_ATTR_SUCCESS;
}
return PY_SET_ATTR_FAIL;
}
#endif // WITH_PYTHON