5 * ***** BEGIN GPL LICENSE BLOCK *****
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
22 * All rights reserved.
24 * The Original Code is: all of this file.
26 * Contributor(s): none yet.
28 * ***** END GPL LICENSE BLOCK *****
32 /** \file gameengine/Ketsji/KX_SoundActuator.cpp
37 #include "KX_SoundActuator.h"
40 # ifdef WITH_SYSTEM_AUDASPACE
41 typedef float sample_t;
42 # include AUD_PYTHON_H
45 # include AUD_SPECIAL_H
46 # include AUD_DEVICE_H
47 # include AUD_HANDLE_H
50 #include "KX_GameObject.h"
51 #include "KX_PyMath.h" // needed for PyObjectFrom()
52 #include "KX_PythonInit.h"
53 #include "KX_Camera.h"
56 /* ------------------------------------------------------------------------- */
57 /* Native functions */
58 /* ------------------------------------------------------------------------- */
59 KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
64 KX_3DSoundSettings settings,
65 KX_SOUNDACT_TYPE type)//,
66 : SCA_IActuator(gameobj, KX_ACT_SOUND)
68 m_sound = sound ? AUD_Sound_copy(sound) : NULL;
80 KX_SoundActuator::~KX_SoundActuator()
84 AUD_Handle_stop(m_handle);
89 AUD_Sound_free(m_sound);
93 void KX_SoundActuator::play()
97 AUD_Handle_stop(m_handle);
104 // this is the sound that will be played and not deleted afterwards
105 AUD_Sound* sound = m_sound;
111 case KX_SOUNDACT_LOOPBIDIRECTIONAL:
112 case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
113 sound = AUD_Sound_pingpong(sound);
115 case KX_SOUNDACT_LOOPEND:
116 case KX_SOUNDACT_LOOPSTOP:
119 case KX_SOUNDACT_PLAYSTOP:
120 case KX_SOUNDACT_PLAYEND:
125 AUD_Device* device = AUD_Device_getCurrent();
126 m_handle = AUD_Device_play(device, sound, false);
127 AUD_Device_free(device);
129 // in case of pingpong, we have to free the sound
131 AUD_Sound_free(sound);
133 if (m_handle != NULL)
137 AUD_Handle_setRelative(m_handle, true);
138 AUD_Handle_setVolumeMaximum(m_handle, m_3d.max_gain);
139 AUD_Handle_setVolumeMinimum(m_handle, m_3d.min_gain);
140 AUD_Handle_setDistanceReference(m_handle, m_3d.reference_distance);
141 AUD_Handle_setDistanceMaximum(m_handle, m_3d.max_distance);
142 AUD_Handle_setAttenuation(m_handle, m_3d.rolloff_factor);
143 AUD_Handle_setConeAngleInner(m_handle, m_3d.cone_inner_angle);
144 AUD_Handle_setConeAngleOuter(m_handle, m_3d.cone_outer_angle);
145 AUD_Handle_setConeVolumeOuter(m_handle, m_3d.cone_outer_gain);
149 AUD_Handle_setLoopCount(m_handle, -1);
150 AUD_Handle_setPitch(m_handle, m_pitch);
151 AUD_Handle_setVolume(m_handle, m_volume);
157 CValue* KX_SoundActuator::GetReplica()
159 KX_SoundActuator* replica = new KX_SoundActuator(*this);
160 replica->ProcessReplica();
164 void KX_SoundActuator::ProcessReplica()
166 SCA_IActuator::ProcessReplica();
168 m_sound = AUD_Sound_copy(m_sound);
171 bool KX_SoundActuator::Update(double curtime, bool frame)
177 // do nothing on negative events, otherwise sounds are played twice!
178 bool bNegativeEvent = IsNegativeEvent();
179 bool bPositiveEvent = m_posevent;
186 // actual audio device playing state
187 bool isplaying = m_handle ? (AUD_Handle_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
191 // here must be a check if it is still playing
192 if (m_isplaying && isplaying)
196 case KX_SOUNDACT_PLAYSTOP:
197 case KX_SOUNDACT_LOOPSTOP:
198 case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
203 AUD_Handle_stop(m_handle);
208 case KX_SOUNDACT_PLAYEND:
210 // do nothing, sound will stop anyway when it's finished
213 case KX_SOUNDACT_LOOPEND:
214 case KX_SOUNDACT_LOOPBIDIRECTIONAL:
216 // stop the looping so that the sound stops when it finished
218 AUD_Handle_setLoopCount(m_handle, 0);
226 // remember that we tried to stop the actuator
231 // Warning: when de-activating the actuator, after a single negative event this runs again with...
232 // m_posevent==false && m_posevent==false, in this case IsNegativeEvent() returns false
233 // and assumes this is a positive event.
234 // check that we actually have a positive event so as not to play sounds when being disabled.
235 else if (bPositiveEvent) /* <- added since 2.49 */
237 else // <- works in most cases except a loop-end sound will never stop unless
238 // the negative pulse is done continuesly
244 // verify that the sound is still playing
245 isplaying = m_handle ? (AUD_Handle_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
251 KX_Camera* cam = KX_GetActiveScene()->GetActiveCamera();
254 KX_GameObject* obj = (KX_GameObject*)this->GetParent();
259 Mo = cam->NodeGetWorldOrientation().inverse();
260 p = (obj->NodeGetWorldPosition() - cam->NodeGetWorldPosition());
263 AUD_Handle_setLocation(m_handle, data);
264 p = (obj->GetLinearVelocity() - cam->GetLinearVelocity());
267 AUD_Handle_setVelocity(m_handle, data);
268 (Mo * obj->NodeGetWorldOrientation()).getRotation().getValue(data);
269 AUD_Handle_setOrientation(m_handle, data);
284 /* ------------------------------------------------------------------------- */
285 /* Python functions */
286 /* ------------------------------------------------------------------------- */
290 /* Integration hooks ------------------------------------------------------- */
291 PyTypeObject KX_SoundActuator::Type = {
292 PyVarObject_HEAD_INIT(NULL, 0)
294 sizeof(PyObjectPlus_Proxy),
303 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
308 &SCA_IActuator::Type,
313 PyMethodDef KX_SoundActuator::Methods[] = {
314 KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
315 KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
316 KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
317 {NULL, NULL} //Sentinel
320 PyAttributeDef KX_SoundActuator::Attributes[] = {
321 KX_PYATTRIBUTE_BOOL_RO("is3D", KX_SoundActuator, m_is3d),
322 KX_PYATTRIBUTE_RW_FUNCTION("volume_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
323 KX_PYATTRIBUTE_RW_FUNCTION("volume_minimum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
324 KX_PYATTRIBUTE_RW_FUNCTION("distance_reference", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
325 KX_PYATTRIBUTE_RW_FUNCTION("distance_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
326 KX_PYATTRIBUTE_RW_FUNCTION("attenuation", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
327 KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_inner", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
328 KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
329 KX_PYATTRIBUTE_RW_FUNCTION("cone_volume_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
330 KX_PYATTRIBUTE_RW_FUNCTION("sound", KX_SoundActuator, pyattr_get_sound, pyattr_set_sound),
332 KX_PYATTRIBUTE_RW_FUNCTION("time", KX_SoundActuator, pyattr_get_audposition, pyattr_set_audposition),
333 KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
334 KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
335 KX_PYATTRIBUTE_ENUM_RW("mode",KX_SoundActuator::KX_SOUNDACT_NODEF+1,KX_SoundActuator::KX_SOUNDACT_MAX-1,false,KX_SoundActuator,m_type),
339 /* Methods ----------------------------------------------------------------- */
340 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
342 "\tStarts the sound.\n")
344 switch (m_handle ? AUD_Handle_getStatus(m_handle) : AUD_STATUS_INVALID) {
345 case AUD_STATUS_PLAYING:
347 case AUD_STATUS_PAUSED:
348 AUD_Handle_resume(m_handle);
356 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
358 "\tPauses the sound.\n")
361 AUD_Handle_pause(m_handle);
365 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
367 "\tStops the sound.\n")
371 AUD_Handle_stop(m_handle);
377 /* Atribute setting and getting -------------------------------------------- */
378 PyObject *KX_SoundActuator::pyattr_get_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
380 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
381 const char* prop = attrdef->m_name;
382 float result_value = 0.0;
384 if (!strcmp(prop, "volume_maximum")) {
385 result_value = actuator->m_3d.max_gain;
387 } else if (!strcmp(prop, "volume_minimum")) {
388 result_value = actuator->m_3d.min_gain;
390 } else if (!strcmp(prop, "distance_reference")) {
391 result_value = actuator->m_3d.reference_distance;
393 } else if (!strcmp(prop, "distance_maximum")) {
394 result_value = actuator->m_3d.max_distance;
396 } else if (!strcmp(prop, "attenuation")) {
397 result_value = actuator->m_3d.rolloff_factor;
399 } else if (!strcmp(prop, "cone_angle_inner")) {
400 result_value = actuator->m_3d.cone_inner_angle;
402 } else if (!strcmp(prop, "cone_angle_outer")) {
403 result_value = actuator->m_3d.cone_outer_angle;
405 } else if (!strcmp(prop, "cone_volume_outer")) {
406 result_value = actuator->m_3d.cone_outer_gain;
412 PyObject *result = PyFloat_FromDouble(result_value);
416 PyObject *KX_SoundActuator::pyattr_get_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
418 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
419 float position = 0.0;
421 if (actuator->m_handle)
422 position = AUD_Handle_getPosition(actuator->m_handle);
424 PyObject *result = PyFloat_FromDouble(position);
429 PyObject *KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
431 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
432 float gain = actuator->m_volume;
434 PyObject *result = PyFloat_FromDouble(gain);
439 PyObject *KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
441 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
442 float pitch = actuator->m_pitch;
444 PyObject *result = PyFloat_FromDouble(pitch);
449 PyObject *KX_SoundActuator::pyattr_get_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
451 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
452 if (actuator->m_sound)
453 return (PyObject *)AUD_getPythonSound(actuator->m_sound);
458 int KX_SoundActuator::pyattr_set_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
460 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
461 const char* prop = attrdef->m_name;
462 float prop_value = 0.0;
464 if (!PyArg_Parse(value, "f", &prop_value))
465 return PY_SET_ATTR_FAIL;
467 // if sound is working and 3D, set the new setting
468 if (!actuator->m_is3d)
469 return PY_SET_ATTR_FAIL;
471 if (!strcmp(prop, "volume_maximum")) {
472 actuator->m_3d.max_gain = prop_value;
473 if (actuator->m_handle)
474 AUD_Handle_setVolumeMaximum(actuator->m_handle, prop_value);
476 } else if (!strcmp(prop, "volume_minimum")) {
477 actuator->m_3d.min_gain = prop_value;
478 if (actuator->m_handle)
479 AUD_Handle_setVolumeMinimum(actuator->m_handle, prop_value);
481 } else if (!strcmp(prop, "distance_reference")) {
482 actuator->m_3d.reference_distance = prop_value;
483 if (actuator->m_handle)
484 AUD_Handle_setDistanceReference(actuator->m_handle, prop_value);
486 } else if (!strcmp(prop, "distance_maximum")) {
487 actuator->m_3d.max_distance = prop_value;
488 if (actuator->m_handle)
489 AUD_Handle_setDistanceMaximum(actuator->m_handle, prop_value);
491 } else if (!strcmp(prop, "attenuation")) {
492 actuator->m_3d.rolloff_factor = prop_value;
493 if (actuator->m_handle)
494 AUD_Handle_setAttenuation(actuator->m_handle, prop_value);
496 } else if (!!strcmp(prop, "cone_angle_inner")) {
497 actuator->m_3d.cone_inner_angle = prop_value;
498 if (actuator->m_handle)
499 AUD_Handle_setConeAngleInner(actuator->m_handle, prop_value);
501 } else if (!strcmp(prop, "cone_angle_outer")) {
502 actuator->m_3d.cone_outer_angle = prop_value;
503 if (actuator->m_handle)
504 AUD_Handle_setConeAngleOuter(actuator->m_handle, prop_value);
506 } else if (!strcmp(prop, "cone_volume_outer")) {
507 actuator->m_3d.cone_outer_gain = prop_value;
508 if (actuator->m_handle)
509 AUD_Handle_setConeVolumeOuter(actuator->m_handle, prop_value);
512 return PY_SET_ATTR_FAIL;
515 return PY_SET_ATTR_SUCCESS;
518 int KX_SoundActuator::pyattr_set_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
520 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
522 float position = 1.0;
523 if (!PyArg_Parse(value, "f", &position))
524 return PY_SET_ATTR_FAIL;
526 if (actuator->m_handle)
527 AUD_Handle_setPosition(actuator->m_handle, position);
528 return PY_SET_ATTR_SUCCESS;
531 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
534 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
535 if (!PyArg_Parse(value, "f", &gain))
536 return PY_SET_ATTR_FAIL;
538 actuator->m_volume = gain;
539 if (actuator->m_handle)
540 AUD_Handle_setVolume(actuator->m_handle, gain);
542 return PY_SET_ATTR_SUCCESS;
545 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
548 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
549 if (!PyArg_Parse(value, "f", &pitch))
550 return PY_SET_ATTR_FAIL;
552 actuator->m_pitch = pitch;
553 if (actuator->m_handle)
554 AUD_Handle_setPitch(actuator->m_handle, pitch);
556 return PY_SET_ATTR_SUCCESS;
559 int KX_SoundActuator::pyattr_set_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
561 PyObject *sound = NULL;
562 KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
563 if (!PyArg_Parse(value, "O", &sound))
564 return PY_SET_ATTR_FAIL;
566 AUD_Sound *snd = AUD_getSoundFromPython(sound);
570 AUD_Sound_free(actuator->m_sound);
571 actuator->m_sound = snd;
572 return PY_SET_ATTR_SUCCESS;
575 return PY_SET_ATTR_FAIL;
578 #endif // WITH_PYTHON