BGE fix: ignore sounds that cannot be opened instead of crashing. ;-)
[blender.git] / source / gameengine / Ketsji / KX_SoundActuator.cpp
1 /*
2  * KX_SoundActuator.cpp
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  *
31  */
32
33 /** \file gameengine/Ketsji/KX_SoundActuator.cpp
34  *  \ingroup ketsji
35  */
36
37
38 #include "KX_SoundActuator.h"
39
40 #ifdef WITH_AUDASPACE
41 #  include "AUD_C-API.h"
42 #  include "AUD_PingPongFactory.h"
43 #  include "AUD_IDevice.h"
44 #  include "AUD_I3DHandle.h"
45 #endif
46
47 #include "KX_GameObject.h"
48 #include "KX_PyMath.h" // needed for PyObjectFrom()
49 #include "KX_PythonInit.h"
50 #include "KX_Camera.h"
51 #include <iostream>
52
53 /* ------------------------------------------------------------------------- */
54 /* Native functions                                                          */
55 /* ------------------------------------------------------------------------- */
56 KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
57                                                                    AUD_Reference<AUD_IFactory> sound,
58                                                                    float volume,
59                                                                    float pitch,
60                                                                    bool is3d,
61                                                                    KX_3DSoundSettings settings,
62                                                                    KX_SOUNDACT_TYPE type)//,
63                                                                    : SCA_IActuator(gameobj, KX_ACT_SOUND)
64 {
65         m_sound = sound;
66         m_volume = volume;
67         m_pitch = pitch;
68         m_is3d = is3d;
69         m_3d = settings;
70         m_type = type;
71         m_isplaying = false;
72 }
73
74
75
76 KX_SoundActuator::~KX_SoundActuator()
77 {
78         if(!m_handle.isNull())
79                 m_handle->stop();
80 }
81
82 void KX_SoundActuator::play()
83 {
84         if(!m_handle.isNull())
85                 m_handle->stop();
86
87         if(m_sound.isNull())
88                 return;
89
90         // this is the sound that will be played and not deleted afterwards
91         AUD_Reference<AUD_IFactory> sound = m_sound;
92
93         bool loop = false;
94
95         switch (m_type)
96         {
97         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
98         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
99                 sound =  new AUD_PingPongFactory(sound);
100                 // fall through
101         case KX_SOUNDACT_LOOPEND:
102         case KX_SOUNDACT_LOOPSTOP:
103                 loop = true;
104                 break;
105         case KX_SOUNDACT_PLAYSTOP:
106         case KX_SOUNDACT_PLAYEND:
107         default:
108                 break;
109         }
110
111         try
112         {
113                 m_handle = AUD_getDevice()->play(sound, 0);
114         }
115         catch(AUD_Exception&)
116         {
117                 // cannot play back, ignore
118                 return;
119         }
120
121         AUD_Reference<AUD_I3DHandle> handle3d = AUD_Reference<AUD_I3DHandle>(m_handle);
122
123         if(m_is3d && !handle3d.isNull())
124         {
125                 handle3d->setRelative(true);
126                 handle3d->setVolumeMaximum(m_3d.max_gain);
127                 handle3d->setVolumeMinimum(m_3d.min_gain);
128                 handle3d->setDistanceReference(m_3d.reference_distance);
129                 handle3d->setDistanceMaximum(m_3d.max_distance);
130                 handle3d->setAttenuation(m_3d.rolloff_factor);
131                 handle3d->setConeAngleInner(m_3d.cone_inner_angle);
132                 handle3d->setConeAngleOuter(m_3d.cone_outer_angle);
133                 handle3d->setConeVolumeOuter(m_3d.cone_outer_gain);
134         }
135
136         if(loop)
137                 m_handle->setLoopCount(-1);
138         m_handle->setPitch(m_pitch);
139         m_handle->setVolume(m_volume);
140         m_isplaying = true;
141 }
142
143 CValue* KX_SoundActuator::GetReplica()
144 {
145         KX_SoundActuator* replica = new KX_SoundActuator(*this);
146         replica->ProcessReplica();
147         return replica;
148 };
149
150 void KX_SoundActuator::ProcessReplica()
151 {
152         SCA_IActuator::ProcessReplica();
153         m_handle = AUD_Reference<AUD_IHandle>();
154 }
155
156 bool KX_SoundActuator::Update(double curtime, bool frame)
157 {
158         if (!frame)
159                 return true;
160         bool result = false;
161
162         // do nothing on negative events, otherwise sounds are played twice!
163         bool bNegativeEvent = IsNegativeEvent();
164         bool bPositiveEvent = m_posevent;
165         
166         RemoveAllEvents();
167
168         if(m_sound.isNull())
169                 return false;
170
171         // actual audio device playing state
172         bool isplaying = m_handle.isNull() ? false : (m_handle->getStatus() == AUD_STATUS_PLAYING);
173
174         if (bNegativeEvent)
175         {
176                 // here must be a check if it is still playing
177                 if (m_isplaying && isplaying)
178                 {
179                         switch (m_type)
180                         {
181                         case KX_SOUNDACT_PLAYSTOP:
182                         case KX_SOUNDACT_LOOPSTOP:
183                         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
184                                 {
185                                         // stop immediately
186                                         if(!m_handle.isNull())
187                                                 m_handle->stop();
188                                         m_handle = AUD_Reference<AUD_IHandle>();
189                                         break;
190                                 }
191                         case KX_SOUNDACT_PLAYEND:
192                                 {
193                                         // do nothing, sound will stop anyway when it's finished
194                                         break;
195                                 }
196                         case KX_SOUNDACT_LOOPEND:
197                         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
198                                 {
199                                         // stop the looping so that the sound stops when it finished
200                                         if(!m_handle.isNull())
201                                                 m_handle->setLoopCount(0);
202                                         break;
203                                 }
204                         default:
205                                 // implement me !!
206                                 break;
207                         }
208                 }
209                 // remember that we tried to stop the actuator
210                 m_isplaying = false;
211         }
212         
213 #if 1
214         // Warning: when de-activating the actuator, after a single negative event this runs again with...
215         // m_posevent==false && m_posevent==false, in this case IsNegativeEvent() returns false 
216         // and assumes this is a positive event.
217         // check that we actually have a positive event so as not to play sounds when being disabled.
218         else if(bPositiveEvent) { // <- added since 2.49
219 #else
220         else {  // <- works in most cases except a loop-end sound will never stop unless
221                         // the negative pulse is done continuesly
222 #endif
223                 if (!m_isplaying)
224                         play();
225         }
226         // verify that the sound is still playing
227         isplaying = m_handle.isNull() ? false : (m_handle->getStatus() == AUD_STATUS_PLAYING);
228
229         if (isplaying)
230         {
231                 AUD_Reference<AUD_I3DHandle> handle3d = AUD_Reference<AUD_I3DHandle>(m_handle);
232
233                 if(m_is3d && !handle3d.isNull())
234                 {
235                         KX_Camera* cam = KX_GetActiveScene()->GetActiveCamera();
236                         if (cam)
237                         {
238                                 KX_GameObject* obj = (KX_GameObject*)this->GetParent();
239                                 MT_Point3 p;
240                                 MT_Matrix3x3 Mo;
241                                 AUD_Vector3 v;
242                                 float q[4];
243
244                                 Mo = cam->NodeGetWorldOrientation().inverse();
245                                 p = (obj->NodeGetWorldPosition() - cam->NodeGetWorldPosition());
246                                 p = Mo * p;
247                                 p.getValue(v.get());
248                                 handle3d->setSourceLocation(v);
249                                 p = (obj->GetLinearVelocity() - cam->GetLinearVelocity());
250                                 p = Mo * p;
251                                 p.getValue(v.get());
252                                 handle3d->setSourceVelocity(v);
253                                 (Mo * obj->NodeGetWorldOrientation()).getRotation().getValue(q);
254                                 handle3d->setSourceOrientation(AUD_Quaternion(q[3], q[0], q[1], q[2]));
255                         }
256                 }
257                 result = true;
258         }
259         else
260         {
261                 m_isplaying = false;
262                 result = false;
263         }
264         return result;
265 }
266
267 #ifdef WITH_PYTHON
268
269 /* ------------------------------------------------------------------------- */
270 /* Python functions                                                          */
271 /* ------------------------------------------------------------------------- */
272
273
274
275 /* Integration hooks ------------------------------------------------------- */
276 PyTypeObject KX_SoundActuator::Type = {
277         PyVarObject_HEAD_INIT(NULL, 0)
278         "KX_SoundActuator",
279         sizeof(PyObjectPlus_Proxy),
280         0,
281         py_base_dealloc,
282         0,
283         0,
284         0,
285         0,
286         py_base_repr,
287         0,0,0,0,0,0,0,0,0,
288         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
289         0,0,0,0,0,0,0,
290         Methods,
291         0,
292         0,
293         &SCA_IActuator::Type,
294         0,0,0,0,0,0,
295         py_base_new
296 };
297
298 PyMethodDef KX_SoundActuator::Methods[] = {
299         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
300         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
301         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
302         {NULL, NULL} //Sentinel
303 };
304
305 PyAttributeDef KX_SoundActuator::Attributes[] = {
306         KX_PYATTRIBUTE_BOOL_RO("is3D", KX_SoundActuator, m_is3d),
307         KX_PYATTRIBUTE_RW_FUNCTION("volume_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
308         KX_PYATTRIBUTE_RW_FUNCTION("volume_minimum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
309         KX_PYATTRIBUTE_RW_FUNCTION("distance_reference", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
310         KX_PYATTRIBUTE_RW_FUNCTION("distance_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
311         KX_PYATTRIBUTE_RW_FUNCTION("attenuation", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
312         KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_inner", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
313         KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
314         KX_PYATTRIBUTE_RW_FUNCTION("cone_volume_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
315         KX_PYATTRIBUTE_RW_FUNCTION("sound", KX_SoundActuator, pyattr_get_sound, pyattr_set_sound),
316
317         KX_PYATTRIBUTE_RW_FUNCTION("time", KX_SoundActuator, pyattr_get_audposition, pyattr_set_audposition),
318         KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
319         KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
320         KX_PYATTRIBUTE_ENUM_RW("mode",KX_SoundActuator::KX_SOUNDACT_NODEF+1,KX_SoundActuator::KX_SOUNDACT_MAX-1,false,KX_SoundActuator,m_type),
321         { NULL }        //Sentinel
322 };
323
324 /* Methods ----------------------------------------------------------------- */
325 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
326 "startSound()\n"
327 "\tStarts the sound.\n")
328 {
329         if(!m_handle.isNull())
330         {
331                 switch(m_handle->getStatus())
332                 {
333                 case AUD_STATUS_PLAYING:
334                         break;
335                 case AUD_STATUS_PAUSED:
336                         m_handle->resume();
337                         break;
338                 default:
339                         play();
340                 }
341         }
342         Py_RETURN_NONE;
343 }
344
345 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
346 "pauseSound()\n"
347 "\tPauses the sound.\n")
348 {
349         if(!m_handle.isNull())
350                 m_handle->pause();
351         Py_RETURN_NONE;
352 }
353
354 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
355 "stopSound()\n"
356 "\tStops the sound.\n")
357 {
358         if(!m_handle.isNull())
359                 m_handle->stop();
360         m_handle = AUD_Reference<AUD_IHandle>();
361         Py_RETURN_NONE;
362 }
363
364 /* Atribute setting and getting -------------------------------------------- */
365 PyObject* KX_SoundActuator::pyattr_get_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
366 {
367         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
368         const char* prop = attrdef->m_name;
369         float result_value = 0.0;
370
371         if(!strcmp(prop, "volume_maximum")) {
372                 result_value = actuator->m_3d.max_gain;
373
374         } else if (!strcmp(prop, "volume_minimum")) {
375                 result_value = actuator->m_3d.min_gain;
376
377         } else if (!strcmp(prop, "distance_reference")) {
378                 result_value = actuator->m_3d.reference_distance;
379
380         } else if (!strcmp(prop, "distance_maximum")) {
381                 result_value = actuator->m_3d.max_distance;
382
383         } else if (!strcmp(prop, "attenuation")) {
384                 result_value = actuator->m_3d.rolloff_factor;
385
386         } else if (!strcmp(prop, "cone_angle_inner")) {
387                 result_value = actuator->m_3d.cone_inner_angle;
388
389         } else if (!strcmp(prop, "cone_angle_outer")) {
390                 result_value = actuator->m_3d.cone_outer_angle;
391
392         } else if (!strcmp(prop, "cone_volume_outer")) {
393                 result_value = actuator->m_3d.cone_outer_gain;
394
395         } else {
396                 Py_RETURN_NONE;
397         }
398
399         PyObject* result = PyFloat_FromDouble(result_value);
400         return result;
401 }
402
403 PyObject* KX_SoundActuator::pyattr_get_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
404 {
405         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
406         float position = 0.0;
407
408         if(!actuator->m_handle.isNull())
409                 position = actuator->m_handle->getPosition();
410
411         PyObject* result = PyFloat_FromDouble(position);
412
413         return result;
414 }
415
416 PyObject* KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
417 {
418         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
419         float gain = actuator->m_volume;
420
421         PyObject* result = PyFloat_FromDouble(gain);
422
423         return result;
424 }
425
426 PyObject* KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
427 {
428         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
429         float pitch = actuator->m_pitch;
430
431         PyObject* result = PyFloat_FromDouble(pitch);
432
433         return result;
434 }
435
436 PyObject* KX_SoundActuator::pyattr_get_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
437 {
438         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
439         if(!actuator->m_sound.isNull())
440                 return AUD_getPythonFactory(&actuator->m_sound);
441         else
442                 Py_RETURN_NONE;
443 }
444
445 int KX_SoundActuator::pyattr_set_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
446 {
447         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
448         const char* prop = attrdef->m_name;
449         float prop_value = 0.0;
450
451         if (!PyArg_Parse(value, "f", &prop_value))
452                 return PY_SET_ATTR_FAIL;
453
454         AUD_Reference<AUD_I3DHandle> handle3d = AUD_Reference<AUD_I3DHandle>(actuator->m_handle);
455         // if sound is working and 3D, set the new setting
456         if(!actuator->m_is3d)
457                 return PY_SET_ATTR_FAIL;
458
459         if(!strcmp(prop, "volume_maximum")) {
460                 actuator->m_3d.max_gain = prop_value;
461                 if(!handle3d.isNull())
462                         handle3d->setVolumeMaximum(prop_value);
463
464         } else if (!strcmp(prop, "volume_minimum")) {
465                 actuator->m_3d.min_gain = prop_value;
466                 if(!handle3d.isNull())
467                         handle3d->setVolumeMinimum(prop_value);
468
469         } else if (!strcmp(prop, "distance_reference")) {
470                 actuator->m_3d.reference_distance = prop_value;
471                 if(!handle3d.isNull())
472                         handle3d->setDistanceReference(prop_value);
473
474         } else if (!strcmp(prop, "distance_maximum")) {
475                 actuator->m_3d.max_distance = prop_value;
476                 if(!handle3d.isNull())
477                         handle3d->setDistanceMaximum(prop_value);
478
479         } else if (!strcmp(prop, "attenuation")) {
480                 actuator->m_3d.rolloff_factor = prop_value;
481                 if(!handle3d.isNull())
482                         handle3d->setAttenuation(prop_value);
483
484         } else if (!!strcmp(prop, "cone_angle_inner")) {
485                 actuator->m_3d.cone_inner_angle = prop_value;
486                 if(!handle3d.isNull())
487                         handle3d->setConeAngleInner(prop_value);
488
489         } else if (!strcmp(prop, "cone_angle_outer")) {
490                 actuator->m_3d.cone_outer_angle = prop_value;
491                 if(!handle3d.isNull())
492                         handle3d->setConeAngleOuter(prop_value);
493
494         } else if (!strcmp(prop, "cone_volume_outer")) {
495                 actuator->m_3d.cone_outer_gain = prop_value;
496                 if(!handle3d.isNull())
497                         handle3d->setConeVolumeOuter(prop_value);
498
499         } else {
500                 return PY_SET_ATTR_FAIL;
501         }
502         
503         return PY_SET_ATTR_SUCCESS;
504 }
505
506 int KX_SoundActuator::pyattr_set_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
507 {
508         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
509
510         float position = 1.0;
511         if (!PyArg_Parse(value, "f", &position))
512                 return PY_SET_ATTR_FAIL;
513
514         if(!actuator->m_handle.isNull())
515                 actuator->m_handle->seek(position);
516         return PY_SET_ATTR_SUCCESS;
517 }
518
519 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
520 {
521         float gain = 1.0;
522         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
523         if (!PyArg_Parse(value, "f", &gain))
524                 return PY_SET_ATTR_FAIL;
525
526         actuator->m_volume = gain;
527         if(!actuator->m_handle.isNull())
528                 actuator->m_handle->setVolume(gain);
529
530         return PY_SET_ATTR_SUCCESS;
531 }
532
533 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
534 {
535         float pitch = 1.0;
536         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
537         if (!PyArg_Parse(value, "f", &pitch))
538                 return PY_SET_ATTR_FAIL;
539
540         actuator->m_pitch = pitch;
541         if(!actuator->m_handle.isNull())
542                 actuator->m_handle->setPitch(pitch);
543
544         return PY_SET_ATTR_SUCCESS;
545 }
546
547 int KX_SoundActuator::pyattr_set_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
548 {
549         PyObject* sound = NULL;
550         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
551         if (!PyArg_Parse(value, "O", &sound))
552                 return PY_SET_ATTR_FAIL;
553
554         AUD_Reference<AUD_IFactory>* snd = reinterpret_cast<AUD_Reference<AUD_IFactory>*>(AUD_getPythonSound(sound));
555         if(snd)
556         {
557                 actuator->m_sound = *snd;
558                 delete snd;
559                 return PY_SET_ATTR_SUCCESS;
560         }
561
562         return PY_SET_ATTR_FAIL;
563 }
564
565 #endif // WITH_PYTHON