Blender  V2.59
KX_SoundActuator.cpp
Go to the documentation of this file.
00001 /*
00002  * KX_SoundActuator.cpp
00003  *
00004  * $Id: KX_SoundActuator.cpp 37750 2011-06-23 09:27:56Z campbellbarton $
00005  *
00006  * ***** BEGIN GPL LICENSE BLOCK *****
00007  *
00008  * This program is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License
00010  * as published by the Free Software Foundation; either version 2
00011  * of the License, or (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software Foundation,
00020  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  *
00022  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00023  * All rights reserved.
00024  *
00025  * The Original Code is: all of this file.
00026  *
00027  * Contributor(s): none yet.
00028  *
00029  * ***** END GPL LICENSE BLOCK *****
00030  *
00031  */
00032 
00038 #include "KX_SoundActuator.h"
00039 
00040 #ifdef WITH_AUDASPACE
00041 #  include "AUD_C-API.h"
00042 #endif
00043 
00044 #include "KX_GameObject.h"
00045 #include "KX_PyMath.h" // needed for PyObjectFrom()
00046 #include <iostream>
00047 
00048 /* ------------------------------------------------------------------------- */
00049 /* Native functions                                                          */
00050 /* ------------------------------------------------------------------------- */
00051 KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
00052                                                                    AUD_Sound* sound,
00053                                                                    float volume,
00054                                                                    float pitch,
00055                                                                    bool is3d,
00056                                                                    KX_3DSoundSettings settings,
00057                                                                    KX_SOUNDACT_TYPE type)//,
00058                                                                    : SCA_IActuator(gameobj, KX_ACT_SOUND)
00059 {
00060         m_sound = sound;
00061         m_volume = volume;
00062         m_pitch = pitch;
00063         m_is3d = is3d;
00064         m_3d = settings;
00065         m_handle = NULL;
00066         m_type = type;
00067         m_isplaying = false;
00068 }
00069 
00070 
00071 
00072 KX_SoundActuator::~KX_SoundActuator()
00073 {
00074         if(m_handle)
00075                 AUD_stop(m_handle);
00076 }
00077 
00078 void KX_SoundActuator::play()
00079 {
00080         if(m_handle)
00081                 AUD_stop(m_handle);
00082 
00083         if(!m_sound)
00084                 return;
00085 
00086         // this is the sound that will be played and not deleted afterwards
00087         AUD_Sound* sound = m_sound;
00088         // this sound is for temporary stacked sounds, will be deleted if not NULL
00089         AUD_Sound* sound2 = NULL;
00090 
00091         bool loop = false;
00092 
00093         switch (m_type)
00094         {
00095         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
00096         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
00097                 sound = sound2 = AUD_pingpongSound(sound);
00098                 // fall through
00099         case KX_SOUNDACT_LOOPEND:
00100         case KX_SOUNDACT_LOOPSTOP:
00101                 loop = true;
00102                 break;
00103         case KX_SOUNDACT_PLAYSTOP:
00104         case KX_SOUNDACT_PLAYEND:
00105         default:
00106                 break;
00107         }
00108 
00109         if(m_is3d)
00110         {
00111                 // sound shall be played 3D
00112                 m_handle = AUD_play(sound, 0);
00113 
00114                 AUD_setRelative(m_handle, false);
00115                 AUD_setVolumeMaximum(m_handle, m_3d.max_gain);
00116                 AUD_setVolumeMinimum(m_handle, m_3d.min_gain);
00117                 AUD_setDistanceReference(m_handle, m_3d.reference_distance);
00118                 AUD_setDistanceMaximum(m_handle, m_3d.max_distance);
00119                 AUD_setAttenuation(m_handle, m_3d.rolloff_factor);
00120                 AUD_setConeAngleInner(m_handle, m_3d.cone_inner_angle);
00121                 AUD_setConeAngleOuter(m_handle, m_3d.cone_outer_angle);
00122                 AUD_setConeVolumeOuter(m_handle, m_3d.cone_outer_gain);
00123         }
00124         else
00125                 m_handle = AUD_play(sound, 0);
00126 
00127         if(loop)
00128                 AUD_setLoop(m_handle, -1);
00129         AUD_setSoundPitch(m_handle, m_pitch);
00130         AUD_setSoundVolume(m_handle, m_volume);
00131         m_isplaying = true;
00132 
00133         if(sound2)
00134                 AUD_unload(sound2);
00135 }
00136 
00137 CValue* KX_SoundActuator::GetReplica()
00138 {
00139         KX_SoundActuator* replica = new KX_SoundActuator(*this);
00140         replica->ProcessReplica();
00141         return replica;
00142 };
00143 
00144 void KX_SoundActuator::ProcessReplica()
00145 {
00146         SCA_IActuator::ProcessReplica();
00147         m_handle = 0;
00148 }
00149 
00150 bool KX_SoundActuator::Update(double curtime, bool frame)
00151 {
00152         if (!frame)
00153                 return true;
00154         bool result = false;
00155 
00156         // do nothing on negative events, otherwise sounds are played twice!
00157         bool bNegativeEvent = IsNegativeEvent();
00158         bool bPositiveEvent = m_posevent;
00159         
00160         RemoveAllEvents();
00161 
00162         if(!m_sound)
00163                 return false;
00164 
00165         // actual audio device playing state
00166         bool isplaying = AUD_getStatus(m_handle) == AUD_STATUS_PLAYING;
00167 
00168         if (bNegativeEvent)
00169         {
00170                 // here must be a check if it is still playing
00171                 if (m_isplaying && isplaying)
00172                 {
00173                         switch (m_type)
00174                         {
00175                         case KX_SOUNDACT_PLAYSTOP:
00176                         case KX_SOUNDACT_LOOPSTOP:
00177                         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
00178                                 {
00179                                         // stop immediately
00180                                         AUD_stop(m_handle);
00181                                         break;
00182                                 }
00183                         case KX_SOUNDACT_PLAYEND:
00184                                 {
00185                                         // do nothing, sound will stop anyway when it's finished
00186                                         break;
00187                                 }
00188                         case KX_SOUNDACT_LOOPEND:
00189                         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
00190                                 {
00191                                         // stop the looping so that the sound stops when it finished
00192                                         AUD_setLoop(m_handle, 0);
00193                                         break;
00194                                 }
00195                         default:
00196                                 // implement me !!
00197                                 break;
00198                         }
00199                 }
00200                 // remember that we tried to stop the actuator
00201                 m_isplaying = false;
00202         }
00203         
00204 #if 1
00205         // Warning: when de-activating the actuator, after a single negative event this runs again with...
00206         // m_posevent==false && m_posevent==false, in this case IsNegativeEvent() returns false 
00207         // and assumes this is a positive event.
00208         // check that we actually have a positive event so as not to play sounds when being disabled.
00209         else if(bPositiveEvent) { // <- added since 2.49
00210 #else
00211         else {  // <- works in most cases except a loop-end sound will never stop unless
00212                         // the negative pulse is done continuesly
00213 #endif
00214                 if (!m_isplaying)
00215                         play();
00216         }
00217         // verify that the sound is still playing
00218         isplaying = AUD_getStatus(m_handle) == AUD_STATUS_PLAYING ? true : false;
00219 
00220         if (isplaying)
00221         {
00222                 if(m_is3d)
00223                 {
00224                         KX_GameObject* obj = (KX_GameObject*)this->GetParent();
00225                         float f[4];
00226 
00227                         obj->NodeGetWorldPosition().getValue(f);
00228                         AUD_setSourceLocation(m_handle, f);
00229                         obj->GetLinearVelocity().getValue(f);
00230                         AUD_setSourceVelocity(m_handle, f);
00231                         obj->NodeGetWorldOrientation().getRotation().getValue(f);
00232                         AUD_setSourceOrientation(m_handle, f);
00233                 }
00234                 result = true;
00235         }
00236         else
00237         {
00238                 m_isplaying = false;
00239                 result = false;
00240         }
00241         return result;
00242 }
00243 
00244 
00245 #ifdef WITH_PYTHON
00246 
00247 /* ------------------------------------------------------------------------- */
00248 /* Python functions                                                          */
00249 /* ------------------------------------------------------------------------- */
00250 
00251 
00252 
00253 /* Integration hooks ------------------------------------------------------- */
00254 PyTypeObject KX_SoundActuator::Type = {
00255         PyVarObject_HEAD_INIT(NULL, 0)
00256         "KX_SoundActuator",
00257         sizeof(PyObjectPlus_Proxy),
00258         0,
00259         py_base_dealloc,
00260         0,
00261         0,
00262         0,
00263         0,
00264         py_base_repr,
00265         0,0,0,0,0,0,0,0,0,
00266         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
00267         0,0,0,0,0,0,0,
00268         Methods,
00269         0,
00270         0,
00271         &SCA_IActuator::Type,
00272         0,0,0,0,0,0,
00273         py_base_new
00274 };
00275 
00276 PyMethodDef KX_SoundActuator::Methods[] = {
00277         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
00278         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
00279         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
00280         {NULL, NULL} //Sentinel
00281 };
00282 
00283 PyAttributeDef KX_SoundActuator::Attributes[] = {
00284         KX_PYATTRIBUTE_BOOL_RO("is3D", KX_SoundActuator, m_is3d),
00285         KX_PYATTRIBUTE_RW_FUNCTION("volume_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00286         KX_PYATTRIBUTE_RW_FUNCTION("volume_minimum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00287         KX_PYATTRIBUTE_RW_FUNCTION("distance_reference", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00288         KX_PYATTRIBUTE_RW_FUNCTION("distance_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00289         KX_PYATTRIBUTE_RW_FUNCTION("attenuation", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00290         KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_inner", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00291         KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00292         KX_PYATTRIBUTE_RW_FUNCTION("cone_volume_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
00293 
00294         KX_PYATTRIBUTE_RW_FUNCTION("time", KX_SoundActuator, pyattr_get_audposition, pyattr_set_audposition),
00295         KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
00296         KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
00297         KX_PYATTRIBUTE_ENUM_RW("mode",KX_SoundActuator::KX_SOUNDACT_NODEF+1,KX_SoundActuator::KX_SOUNDACT_MAX-1,false,KX_SoundActuator,m_type),
00298         { NULL }        //Sentinel
00299 };
00300 
00301 /* Methods ----------------------------------------------------------------- */
00302 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
00303 "startSound()\n"
00304 "\tStarts the sound.\n")
00305 {
00306         switch(AUD_getStatus(m_handle))
00307         {
00308         case AUD_STATUS_PLAYING:
00309                 break;
00310         case AUD_STATUS_PAUSED:
00311                 AUD_resume(m_handle);
00312                 break;
00313         default:
00314                 play();
00315         }
00316         Py_RETURN_NONE;
00317 }
00318 
00319 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
00320 "pauseSound()\n"
00321 "\tPauses the sound.\n")
00322 {
00323         AUD_pause(m_handle);
00324         Py_RETURN_NONE;
00325 }
00326 
00327 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
00328 "stopSound()\n"
00329 "\tStops the sound.\n")
00330 {
00331         AUD_stop(m_handle);
00332         Py_RETURN_NONE;
00333 }
00334 
00335 /* Atribute setting and getting -------------------------------------------- */
00336 PyObject* KX_SoundActuator::pyattr_get_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00337 {
00338         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00339         const char* prop = attrdef->m_name;
00340         float result_value = 0.0;
00341 
00342         if(!strcmp(prop, "volume_maximum")) {
00343                 result_value = actuator->m_3d.max_gain;
00344 
00345         } else if (!strcmp(prop, "volume_minimum")) {
00346                 result_value = actuator->m_3d.min_gain;
00347 
00348         } else if (!strcmp(prop, "distance_reference")) {
00349                 result_value = actuator->m_3d.reference_distance;
00350 
00351         } else if (!strcmp(prop, "distance_maximum")) {
00352                 result_value = actuator->m_3d.max_distance;
00353 
00354         } else if (!strcmp(prop, "attenuation")) {
00355                 result_value = actuator->m_3d.rolloff_factor;
00356 
00357         } else if (!strcmp(prop, "cone_angle_inner")) {
00358                 result_value = actuator->m_3d.cone_inner_angle;
00359 
00360         } else if (!strcmp(prop, "cone_angle_outer")) {
00361                 result_value = actuator->m_3d.cone_outer_angle;
00362 
00363         } else if (!strcmp(prop, "cone_volume_outer")) {
00364                 result_value = actuator->m_3d.cone_outer_gain;
00365 
00366         } else {
00367                 Py_RETURN_NONE;
00368         }
00369 
00370         PyObject* result = PyFloat_FromDouble(result_value);
00371         return result;
00372 }
00373 
00374 PyObject* KX_SoundActuator::pyattr_get_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00375 {
00376         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00377         float position = 0.0;
00378 
00379         if(actuator->m_handle)
00380                 position = AUD_getPosition(actuator->m_handle);
00381 
00382         PyObject* result = PyFloat_FromDouble(position);
00383 
00384         return result;
00385 }
00386 
00387 PyObject* KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00388 {
00389         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00390         float gain = actuator->m_volume;
00391 
00392         PyObject* result = PyFloat_FromDouble(gain);
00393 
00394         return result;
00395 }
00396 
00397 PyObject* KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
00398 {
00399         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00400         float pitch = actuator->m_pitch;
00401 
00402         PyObject* result = PyFloat_FromDouble(pitch);
00403 
00404         return result;
00405 }
00406 
00407 int KX_SoundActuator::pyattr_set_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00408 {
00409         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00410         const char* prop = attrdef->m_name;
00411         float prop_value = 0.0;
00412 
00413         if (!PyArg_Parse(value, "f", &prop_value))
00414                 return PY_SET_ATTR_FAIL;
00415 
00416         // if sound is working and 3D, set the new setting
00417         if(!actuator->m_is3d)
00418                 return PY_SET_ATTR_FAIL;
00419 
00420         if(!strcmp(prop, "volume_maximum")) {
00421                 actuator->m_3d.max_gain = prop_value;
00422                 if(actuator->m_handle)
00423                         AUD_setVolumeMaximum(actuator->m_handle, prop_value);
00424 
00425         } else if (!strcmp(prop, "volume_minimum")) {
00426                 actuator->m_3d.min_gain = prop_value;
00427                 if(actuator->m_handle)
00428                         AUD_setVolumeMinimum(actuator->m_handle, prop_value);
00429 
00430         } else if (!strcmp(prop, "distance_reference")) {
00431                 actuator->m_3d.reference_distance = prop_value;
00432                 if(actuator->m_handle)
00433                         AUD_setDistanceReference(actuator->m_handle, prop_value);
00434 
00435         } else if (!strcmp(prop, "distance_maximum")) {
00436                 actuator->m_3d.max_distance = prop_value;
00437                 if(actuator->m_handle)
00438                         AUD_setDistanceMaximum(actuator->m_handle, prop_value);
00439 
00440         } else if (!strcmp(prop, "attenuation")) {
00441                 actuator->m_3d.rolloff_factor = prop_value;
00442                 if(actuator->m_handle)
00443                         AUD_setAttenuation(actuator->m_handle, prop_value);
00444 
00445         } else if (!!strcmp(prop, "cone_angle_inner")) {
00446                 actuator->m_3d.cone_inner_angle = prop_value;
00447                 if(actuator->m_handle)
00448                         AUD_setConeAngleInner(actuator->m_handle, prop_value);
00449 
00450         } else if (!strcmp(prop, "cone_angle_outer")) {
00451                 actuator->m_3d.cone_outer_angle = prop_value;
00452                 if(actuator->m_handle)
00453                         AUD_setConeAngleOuter(actuator->m_handle, prop_value);
00454 
00455         } else if (!strcmp(prop, "cone_volume_outer")) {
00456                 actuator->m_3d.cone_outer_gain = prop_value;
00457                 if(actuator->m_handle)
00458                         AUD_setConeVolumeOuter(actuator->m_handle, prop_value);
00459 
00460         } else {
00461                 return PY_SET_ATTR_FAIL;
00462         }
00463         
00464         return PY_SET_ATTR_SUCCESS;
00465 }
00466 
00467 int KX_SoundActuator::pyattr_set_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00468 {
00469         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00470 
00471         float position = 1.0;
00472         if (!PyArg_Parse(value, "f", &position))
00473                 return PY_SET_ATTR_FAIL;
00474 
00475         if(actuator->m_handle)
00476                 AUD_seek(actuator->m_handle, position);
00477         return PY_SET_ATTR_SUCCESS;
00478 }
00479 
00480 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00481 {
00482         float gain = 1.0;
00483         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00484         if (!PyArg_Parse(value, "f", &gain))
00485                 return PY_SET_ATTR_FAIL;
00486 
00487         actuator->m_volume = gain;
00488         if(actuator->m_handle)
00489                 AUD_setSoundVolume(actuator->m_handle, gain);
00490 
00491         return PY_SET_ATTR_SUCCESS;
00492 }
00493 
00494 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
00495 {
00496         float pitch = 1.0;
00497         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
00498         if (!PyArg_Parse(value, "f", &pitch))
00499                 return PY_SET_ATTR_FAIL;
00500 
00501         actuator->m_pitch = pitch;
00502         if(actuator->m_handle)
00503                 AUD_setSoundPitch(actuator->m_handle, pitch);
00504 
00505         return PY_SET_ATTR_SUCCESS;
00506 }
00507 
00508 #endif // WITH_PYTHON