Fix T46458: BGE Crash on load
[blender.git] / source / gameengine / Ketsji / KX_SoundActuator.cpp
1 /*
2  * KX_SoundActuator.cpp
3  *
4  *
5  * ***** BEGIN GPL LICENSE BLOCK *****
6  *
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.
11  *
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.
16  *
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.
20  *
21  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
22  * All rights reserved.
23  *
24  * The Original Code is: all of this file.
25  *
26  * Contributor(s): none yet.
27  *
28  * ***** END GPL LICENSE BLOCK *****
29  *
30  */
31
32 /** \file gameengine/Ketsji/KX_SoundActuator.cpp
33  *  \ingroup ketsji
34  */
35
36
37 #include "KX_SoundActuator.h"
38
39 #ifdef WITH_AUDASPACE
40 #  ifdef WITH_SYSTEM_AUDASPACE
41 typedef float sample_t;
42 #    include AUD_PYTHON_H
43 #  endif
44 #  include AUD_SOUND_H
45 #  include AUD_SPECIAL_H
46 #  include AUD_DEVICE_H
47 #  include AUD_HANDLE_H
48 #endif
49
50 #include "KX_GameObject.h"
51 #include "KX_PyMath.h" // needed for PyObjectFrom()
52 #include "KX_PythonInit.h"
53 #include "KX_Camera.h"
54 #include <iostream>
55
56 /* ------------------------------------------------------------------------- */
57 /* Native functions                                                          */
58 /* ------------------------------------------------------------------------- */
59 KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
60                                                                    AUD_Sound* sound,
61                                                                    float volume,
62                                                                    float pitch,
63                                                                    bool is3d,
64                                                                    KX_3DSoundSettings settings,
65                                                                    KX_SOUNDACT_TYPE type)//,
66                                                                    : SCA_IActuator(gameobj, KX_ACT_SOUND)
67 {
68         m_sound = sound ? AUD_Sound_copy(sound) : NULL;
69         m_handle = NULL;
70         m_volume = volume;
71         m_pitch = pitch;
72         m_is3d = is3d;
73         m_3d = settings;
74         m_type = type;
75         m_isplaying = false;
76 }
77
78
79
80 KX_SoundActuator::~KX_SoundActuator()
81 {
82         if(m_handle)
83         {
84                 AUD_Handle_stop(m_handle);
85         }
86
87         if(m_sound)
88         {
89                 AUD_Sound_free(m_sound);
90         }
91 }
92
93 void KX_SoundActuator::play()
94 {
95         if(m_handle)
96         {
97                 AUD_Handle_stop(m_handle);
98                 m_handle = NULL;
99         }
100
101         if (!m_sound)
102                 return;
103
104         // this is the sound that will be played and not deleted afterwards
105         AUD_Sound* sound = m_sound;
106
107         bool loop = false;
108
109         switch (m_type)
110         {
111         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
112         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
113                 sound = AUD_Sound_pingpong(sound);
114                 // fall through
115         case KX_SOUNDACT_LOOPEND:
116         case KX_SOUNDACT_LOOPSTOP:
117                 loop = true;
118                 break;
119         case KX_SOUNDACT_PLAYSTOP:
120         case KX_SOUNDACT_PLAYEND:
121         default:
122                 break;
123         }
124
125         AUD_Device* device = AUD_Device_getCurrent();
126         m_handle = AUD_Device_play(device, sound, false);
127         AUD_Device_free(device);
128
129         // in case of pingpong, we have to free the sound
130         if(sound != m_sound)
131                 AUD_Sound_free(sound);
132
133         if (m_handle != NULL)
134         {
135                 if (m_is3d)
136                 {
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);
146                 }
147
148                 if (loop)
149                         AUD_Handle_setLoopCount(m_handle, -1);
150                 AUD_Handle_setPitch(m_handle, m_pitch);
151                 AUD_Handle_setVolume(m_handle, m_volume);
152         }
153
154         m_isplaying = true;
155 }
156
157 CValue* KX_SoundActuator::GetReplica()
158 {
159         KX_SoundActuator* replica = new KX_SoundActuator(*this);
160         replica->ProcessReplica();
161         return replica;
162 }
163
164 void KX_SoundActuator::ProcessReplica()
165 {
166         SCA_IActuator::ProcessReplica();
167         m_handle = NULL;
168         m_sound = AUD_Sound_copy(m_sound);
169 }
170
171 bool KX_SoundActuator::Update(double curtime, bool frame)
172 {
173         if (!frame)
174                 return true;
175         bool result = false;
176
177         // do nothing on negative events, otherwise sounds are played twice!
178         bool bNegativeEvent = IsNegativeEvent();
179         bool bPositiveEvent = m_posevent;
180         
181         RemoveAllEvents();
182
183         if (!m_sound)
184                 return false;
185
186         // actual audio device playing state
187         bool isplaying = m_handle ? (AUD_Handle_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
188
189         if (bNegativeEvent)
190         {
191                 // here must be a check if it is still playing
192                 if (m_isplaying && isplaying)
193                 {
194                         switch (m_type)
195                         {
196                         case KX_SOUNDACT_PLAYSTOP:
197                         case KX_SOUNDACT_LOOPSTOP:
198                         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
199                                 {
200                                         // stop immediately
201                                         if (m_handle)
202                                         {
203                                                 AUD_Handle_stop(m_handle);
204                                                 m_handle = NULL;
205                                         }
206                                         break;
207                                 }
208                         case KX_SOUNDACT_PLAYEND:
209                                 {
210                                         // do nothing, sound will stop anyway when it's finished
211                                         break;
212                                 }
213                         case KX_SOUNDACT_LOOPEND:
214                         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
215                                 {
216                                         // stop the looping so that the sound stops when it finished
217                                         if (m_handle)
218                                                 AUD_Handle_setLoopCount(m_handle, 0);
219                                         break;
220                                 }
221                         default:
222                                 // implement me !!
223                                 break;
224                         }
225                 }
226                 // remember that we tried to stop the actuator
227                 m_isplaying = false;
228         }
229         
230 #if 1
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 */
236 #else
237         else    // <- works in most cases except a loop-end sound will never stop unless
238                         // the negative pulse is done continuesly
239 #endif
240         {
241                 if (!m_isplaying)
242                         play();
243         }
244         // verify that the sound is still playing
245         isplaying = m_handle ? (AUD_Handle_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
246
247         if (isplaying)
248         {
249                 if (m_is3d)
250                 {
251                         KX_Camera* cam = KX_GetActiveScene()->GetActiveCamera();
252                         if (cam)
253                         {
254                                 KX_GameObject* obj = (KX_GameObject*)this->GetParent();
255                                 MT_Point3 p;
256                                 MT_Matrix3x3 Mo;
257                                 float data[4];
258
259                                 Mo = cam->NodeGetWorldOrientation().inverse();
260                                 p = (obj->NodeGetWorldPosition() - cam->NodeGetWorldPosition());
261                                 p = Mo * p;
262                                 p.getValue(data);
263                                 AUD_Handle_setLocation(m_handle, data);
264                                 p = (obj->GetLinearVelocity() - cam->GetLinearVelocity());
265                                 p = Mo * p;
266                                 p.getValue(data);
267                                 AUD_Handle_setVelocity(m_handle, data);
268                                 (Mo * obj->NodeGetWorldOrientation()).getRotation().getValue(data);
269                                 AUD_Handle_setOrientation(m_handle, data);
270                         }
271                 }
272                 result = true;
273         }
274         else
275         {
276                 m_isplaying = false;
277                 result = false;
278         }
279         return result;
280 }
281
282 #ifdef WITH_PYTHON
283
284 /* ------------------------------------------------------------------------- */
285 /* Python functions                                                          */
286 /* ------------------------------------------------------------------------- */
287
288
289
290 /* Integration hooks ------------------------------------------------------- */
291 PyTypeObject KX_SoundActuator::Type = {
292         PyVarObject_HEAD_INIT(NULL, 0)
293         "KX_SoundActuator",
294         sizeof(PyObjectPlus_Proxy),
295         0,
296         py_base_dealloc,
297         0,
298         0,
299         0,
300         0,
301         py_base_repr,
302         0,0,0,0,0,0,0,0,0,
303         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
304         0,0,0,0,0,0,0,
305         Methods,
306         0,
307         0,
308         &SCA_IActuator::Type,
309         0,0,0,0,0,0,
310         py_base_new
311 };
312
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
318 };
319
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),
331
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),
336         { NULL }        //Sentinel
337 };
338
339 /* Methods ----------------------------------------------------------------- */
340 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
341 "startSound()\n"
342 "\tStarts the sound.\n")
343 {
344         switch (m_handle ? AUD_Handle_getStatus(m_handle) : AUD_STATUS_INVALID) {
345                 case AUD_STATUS_PLAYING:
346                         break;
347                 case AUD_STATUS_PAUSED:
348                         AUD_Handle_resume(m_handle);
349                         break;
350                 default:
351                         play();
352         }
353         Py_RETURN_NONE;
354 }
355
356 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
357 "pauseSound()\n"
358 "\tPauses the sound.\n")
359 {
360         if (m_handle)
361                 AUD_Handle_pause(m_handle);
362         Py_RETURN_NONE;
363 }
364
365 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
366 "stopSound()\n"
367 "\tStops the sound.\n")
368 {
369         if (m_handle)
370         {
371                 AUD_Handle_stop(m_handle);
372                 m_handle = NULL;
373         }
374         Py_RETURN_NONE;
375 }
376
377 /* Atribute setting and getting -------------------------------------------- */
378 PyObject *KX_SoundActuator::pyattr_get_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
379 {
380         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
381         const char* prop = attrdef->m_name;
382         float result_value = 0.0;
383
384         if (!strcmp(prop, "volume_maximum")) {
385                 result_value = actuator->m_3d.max_gain;
386
387         } else if (!strcmp(prop, "volume_minimum")) {
388                 result_value = actuator->m_3d.min_gain;
389
390         } else if (!strcmp(prop, "distance_reference")) {
391                 result_value = actuator->m_3d.reference_distance;
392
393         } else if (!strcmp(prop, "distance_maximum")) {
394                 result_value = actuator->m_3d.max_distance;
395
396         } else if (!strcmp(prop, "attenuation")) {
397                 result_value = actuator->m_3d.rolloff_factor;
398
399         } else if (!strcmp(prop, "cone_angle_inner")) {
400                 result_value = actuator->m_3d.cone_inner_angle;
401
402         } else if (!strcmp(prop, "cone_angle_outer")) {
403                 result_value = actuator->m_3d.cone_outer_angle;
404
405         } else if (!strcmp(prop, "cone_volume_outer")) {
406                 result_value = actuator->m_3d.cone_outer_gain;
407
408         } else {
409                 Py_RETURN_NONE;
410         }
411
412         PyObject *result = PyFloat_FromDouble(result_value);
413         return result;
414 }
415
416 PyObject *KX_SoundActuator::pyattr_get_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
417 {
418         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
419         float position = 0.0;
420
421         if (actuator->m_handle)
422                 position = AUD_Handle_getPosition(actuator->m_handle);
423
424         PyObject *result = PyFloat_FromDouble(position);
425
426         return result;
427 }
428
429 PyObject *KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
430 {
431         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
432         float gain = actuator->m_volume;
433
434         PyObject *result = PyFloat_FromDouble(gain);
435
436         return result;
437 }
438
439 PyObject *KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
440 {
441         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
442         float pitch = actuator->m_pitch;
443
444         PyObject *result = PyFloat_FromDouble(pitch);
445
446         return result;
447 }
448
449 PyObject *KX_SoundActuator::pyattr_get_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
450 {
451         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
452         if (actuator->m_sound)
453                 return (PyObject *)AUD_getPythonSound(actuator->m_sound);
454         else
455                 Py_RETURN_NONE;
456 }
457
458 int KX_SoundActuator::pyattr_set_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
459 {
460         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
461         const char* prop = attrdef->m_name;
462         float prop_value = 0.0;
463
464         if (!PyArg_Parse(value, "f", &prop_value))
465                 return PY_SET_ATTR_FAIL;
466
467         // if sound is working and 3D, set the new setting
468         if (!actuator->m_is3d)
469                 return PY_SET_ATTR_FAIL;
470
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);
475
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);
480
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);
485
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);
490
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);
495
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);
500
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);
505
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);
510
511         } else {
512                 return PY_SET_ATTR_FAIL;
513         }
514         
515         return PY_SET_ATTR_SUCCESS;
516 }
517
518 int KX_SoundActuator::pyattr_set_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
519 {
520         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
521
522         float position = 1.0;
523         if (!PyArg_Parse(value, "f", &position))
524                 return PY_SET_ATTR_FAIL;
525
526         if (actuator->m_handle)
527                 AUD_Handle_setPosition(actuator->m_handle, position);
528         return PY_SET_ATTR_SUCCESS;
529 }
530
531 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
532 {
533         float gain = 1.0;
534         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
535         if (!PyArg_Parse(value, "f", &gain))
536                 return PY_SET_ATTR_FAIL;
537
538         actuator->m_volume = gain;
539         if (actuator->m_handle)
540                 AUD_Handle_setVolume(actuator->m_handle, gain);
541
542         return PY_SET_ATTR_SUCCESS;
543 }
544
545 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
546 {
547         float pitch = 1.0;
548         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
549         if (!PyArg_Parse(value, "f", &pitch))
550                 return PY_SET_ATTR_FAIL;
551
552         actuator->m_pitch = pitch;
553         if (actuator->m_handle)
554                 AUD_Handle_setPitch(actuator->m_handle, pitch);
555
556         return PY_SET_ATTR_SUCCESS;
557 }
558
559 int KX_SoundActuator::pyattr_set_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
560 {
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;
565
566         AUD_Sound *snd = AUD_getSoundFromPython(sound);
567
568         if (snd)
569         {
570                 AUD_Sound_free(actuator->m_sound);
571                 actuator->m_sound = snd;
572                 return PY_SET_ATTR_SUCCESS;
573         }
574
575         return PY_SET_ATTR_FAIL;
576 }
577
578 #endif // WITH_PYTHON