611467d7167a91565f850d7fda6d0829f070c1e3
[blender-staging.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 #  include "AUD_C-API.h"
41 #endif
42
43 #include "KX_GameObject.h"
44 #include "KX_PyMath.h" // needed for PyObjectFrom()
45 #include "KX_PythonInit.h"
46 #include "KX_Camera.h"
47 #include <iostream>
48
49 /* ------------------------------------------------------------------------- */
50 /* Native functions                                                          */
51 /* ------------------------------------------------------------------------- */
52 KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
53                                                                    AUD_Sound* sound,
54                                                                    float volume,
55                                                                    float pitch,
56                                                                    bool is3d,
57                                                                    KX_3DSoundSettings settings,
58                                                                    KX_SOUNDACT_TYPE type)//,
59                                                                    : SCA_IActuator(gameobj, KX_ACT_SOUND)
60 {
61         m_sound = AUD_copy(sound);
62         m_handle = NULL;
63         m_volume = volume;
64         m_pitch = pitch;
65         m_is3d = is3d;
66         m_3d = settings;
67         m_type = type;
68         m_isplaying = false;
69 }
70
71
72
73 KX_SoundActuator::~KX_SoundActuator()
74 {
75         if(m_handle)
76         {
77                 AUD_stop(m_handle);
78         }
79
80         if(m_sound)
81         {
82                 AUD_unload(m_sound);
83         }
84 }
85
86 void KX_SoundActuator::play()
87 {
88         if(m_handle)
89         {
90                 AUD_stop(m_handle);
91                 m_handle = NULL;
92         }
93
94         if (!m_sound)
95                 return;
96
97         // this is the sound that will be played and not deleted afterwards
98         AUD_Sound* sound = m_sound;
99
100         bool loop = false;
101
102         switch (m_type)
103         {
104         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
105         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
106                 sound = AUD_pingpongSound(sound);
107                 // fall through
108         case KX_SOUNDACT_LOOPEND:
109         case KX_SOUNDACT_LOOPSTOP:
110                 loop = true;
111                 break;
112         case KX_SOUNDACT_PLAYSTOP:
113         case KX_SOUNDACT_PLAYEND:
114         default:
115                 break;
116         }
117
118         m_handle = AUD_play(sound, false);
119
120         // in case of pingpong, we have to free the sound
121         if(sound != m_sound)
122                 AUD_unload(sound);
123
124         if (m_handle != NULL)
125         {
126                 if (m_is3d)
127                 {
128                         AUD_setRelative(m_handle, true);
129                         AUD_setVolumeMaximum(m_handle, m_3d.max_gain);
130                         AUD_setVolumeMinimum(m_handle, m_3d.min_gain);
131                         AUD_setDistanceReference(m_handle, m_3d.reference_distance);
132                         AUD_setDistanceMaximum(m_handle, m_3d.max_distance);
133                         AUD_setAttenuation(m_handle, m_3d.rolloff_factor);
134                         AUD_setConeAngleInner(m_handle, m_3d.cone_inner_angle);
135                         AUD_setConeAngleOuter(m_handle, m_3d.cone_outer_angle);
136                         AUD_setConeVolumeOuter(m_handle, m_3d.cone_outer_gain);
137                 }
138
139                 if (loop)
140                         AUD_setLoop(m_handle, -1);
141                 AUD_setSoundPitch(m_handle, m_pitch);
142                 AUD_setSoundVolume(m_handle, m_volume);
143         }
144
145         m_isplaying = true;
146 }
147
148 CValue* KX_SoundActuator::GetReplica()
149 {
150         KX_SoundActuator* replica = new KX_SoundActuator(*this);
151         replica->ProcessReplica();
152         return replica;
153 }
154
155 void KX_SoundActuator::ProcessReplica()
156 {
157         SCA_IActuator::ProcessReplica();
158         m_handle = NULL;
159         m_sound = AUD_copy(m_sound);
160 }
161
162 bool KX_SoundActuator::Update(double curtime, bool frame)
163 {
164         if (!frame)
165                 return true;
166         bool result = false;
167
168         // do nothing on negative events, otherwise sounds are played twice!
169         bool bNegativeEvent = IsNegativeEvent();
170         bool bPositiveEvent = m_posevent;
171         
172         RemoveAllEvents();
173
174         if (!m_sound)
175                 return false;
176
177         // actual audio device playing state
178         bool isplaying = m_handle ? (AUD_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
179
180         if (bNegativeEvent)
181         {
182                 // here must be a check if it is still playing
183                 if (m_isplaying && isplaying)
184                 {
185                         switch (m_type)
186                         {
187                         case KX_SOUNDACT_PLAYSTOP:
188                         case KX_SOUNDACT_LOOPSTOP:
189                         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
190                                 {
191                                         // stop immediately
192                                         if (m_handle)
193                                         {
194                                                 AUD_stop(m_handle);
195                                                 m_handle = NULL;
196                                         }
197                                         break;
198                                 }
199                         case KX_SOUNDACT_PLAYEND:
200                                 {
201                                         // do nothing, sound will stop anyway when it's finished
202                                         break;
203                                 }
204                         case KX_SOUNDACT_LOOPEND:
205                         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
206                                 {
207                                         // stop the looping so that the sound stops when it finished
208                                         if (m_handle)
209                                                 AUD_setLoop(m_handle, 0);
210                                         break;
211                                 }
212                         default:
213                                 // implement me !!
214                                 break;
215                         }
216                 }
217                 // remember that we tried to stop the actuator
218                 m_isplaying = false;
219         }
220         
221 #if 1
222         // Warning: when de-activating the actuator, after a single negative event this runs again with...
223         // m_posevent==false && m_posevent==false, in this case IsNegativeEvent() returns false 
224         // and assumes this is a positive event.
225         // check that we actually have a positive event so as not to play sounds when being disabled.
226         else if (bPositiveEvent)  /* <- added since 2.49 */
227 #else
228         else    // <- works in most cases except a loop-end sound will never stop unless
229                         // the negative pulse is done continuesly
230 #endif
231         {
232                 if (!m_isplaying)
233                         play();
234         }
235         // verify that the sound is still playing
236         isplaying = m_handle ? (AUD_getStatus(m_handle) == AUD_STATUS_PLAYING) : false;
237
238         if (isplaying)
239         {
240                 if (m_is3d)
241                 {
242                         KX_Camera* cam = KX_GetActiveScene()->GetActiveCamera();
243                         if (cam)
244                         {
245                                 KX_GameObject* obj = (KX_GameObject*)this->GetParent();
246                                 MT_Point3 p;
247                                 MT_Matrix3x3 Mo;
248                                 float data[4];
249
250                                 Mo = cam->NodeGetWorldOrientation().inverse();
251                                 p = (obj->NodeGetWorldPosition() - cam->NodeGetWorldPosition());
252                                 p = Mo * p;
253                                 p.getValue(data);
254                                 AUD_setSourceLocation(m_handle, data);
255                                 p = (obj->GetLinearVelocity() - cam->GetLinearVelocity());
256                                 p = Mo * p;
257                                 p.getValue(data);
258                                 AUD_setSourceVelocity(m_handle, data);
259                                 (Mo * obj->NodeGetWorldOrientation()).getRotation().getValue(data);
260                                 AUD_setSourceOrientation(m_handle, data);
261                         }
262                 }
263                 result = true;
264         }
265         else
266         {
267                 m_isplaying = false;
268                 result = false;
269         }
270         return result;
271 }
272
273 #ifdef WITH_PYTHON
274
275 /* ------------------------------------------------------------------------- */
276 /* Python functions                                                          */
277 /* ------------------------------------------------------------------------- */
278
279
280
281 /* Integration hooks ------------------------------------------------------- */
282 PyTypeObject KX_SoundActuator::Type = {
283         PyVarObject_HEAD_INIT(NULL, 0)
284         "KX_SoundActuator",
285         sizeof(PyObjectPlus_Proxy),
286         0,
287         py_base_dealloc,
288         0,
289         0,
290         0,
291         0,
292         py_base_repr,
293         0,0,0,0,0,0,0,0,0,
294         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
295         0,0,0,0,0,0,0,
296         Methods,
297         0,
298         0,
299         &SCA_IActuator::Type,
300         0,0,0,0,0,0,
301         py_base_new
302 };
303
304 PyMethodDef KX_SoundActuator::Methods[] = {
305         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
306         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
307         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
308         {NULL, NULL} //Sentinel
309 };
310
311 PyAttributeDef KX_SoundActuator::Attributes[] = {
312         KX_PYATTRIBUTE_BOOL_RO("is3D", KX_SoundActuator, m_is3d),
313         KX_PYATTRIBUTE_RW_FUNCTION("volume_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
314         KX_PYATTRIBUTE_RW_FUNCTION("volume_minimum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
315         KX_PYATTRIBUTE_RW_FUNCTION("distance_reference", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
316         KX_PYATTRIBUTE_RW_FUNCTION("distance_maximum", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
317         KX_PYATTRIBUTE_RW_FUNCTION("attenuation", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
318         KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_inner", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
319         KX_PYATTRIBUTE_RW_FUNCTION("cone_angle_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
320         KX_PYATTRIBUTE_RW_FUNCTION("cone_volume_outer", KX_SoundActuator, pyattr_get_3d_property, pyattr_set_3d_property),
321         KX_PYATTRIBUTE_RW_FUNCTION("sound", KX_SoundActuator, pyattr_get_sound, pyattr_set_sound),
322
323         KX_PYATTRIBUTE_RW_FUNCTION("time", KX_SoundActuator, pyattr_get_audposition, pyattr_set_audposition),
324         KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
325         KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
326         KX_PYATTRIBUTE_ENUM_RW("mode",KX_SoundActuator::KX_SOUNDACT_NODEF+1,KX_SoundActuator::KX_SOUNDACT_MAX-1,false,KX_SoundActuator,m_type),
327         { NULL }        //Sentinel
328 };
329
330 /* Methods ----------------------------------------------------------------- */
331 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
332 "startSound()\n"
333 "\tStarts the sound.\n")
334 {
335         switch (m_handle ? AUD_getStatus(m_handle) : AUD_STATUS_INVALID) {
336                 case AUD_STATUS_PLAYING:
337                         break;
338                 case AUD_STATUS_PAUSED:
339                         AUD_resume(m_handle);
340                         break;
341                 default:
342                         play();
343         }
344         Py_RETURN_NONE;
345 }
346
347 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
348 "pauseSound()\n"
349 "\tPauses the sound.\n")
350 {
351         if (m_handle)
352                 AUD_pause(m_handle);
353         Py_RETURN_NONE;
354 }
355
356 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
357 "stopSound()\n"
358 "\tStops the sound.\n")
359 {
360         if (m_handle)
361         {
362                 AUD_stop(m_handle);
363                 m_handle = NULL;
364         }
365         Py_RETURN_NONE;
366 }
367
368 /* Atribute setting and getting -------------------------------------------- */
369 PyObject *KX_SoundActuator::pyattr_get_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
370 {
371         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
372         const char* prop = attrdef->m_name;
373         float result_value = 0.0;
374
375         if (!strcmp(prop, "volume_maximum")) {
376                 result_value = actuator->m_3d.max_gain;
377
378         } else if (!strcmp(prop, "volume_minimum")) {
379                 result_value = actuator->m_3d.min_gain;
380
381         } else if (!strcmp(prop, "distance_reference")) {
382                 result_value = actuator->m_3d.reference_distance;
383
384         } else if (!strcmp(prop, "distance_maximum")) {
385                 result_value = actuator->m_3d.max_distance;
386
387         } else if (!strcmp(prop, "attenuation")) {
388                 result_value = actuator->m_3d.rolloff_factor;
389
390         } else if (!strcmp(prop, "cone_angle_inner")) {
391                 result_value = actuator->m_3d.cone_inner_angle;
392
393         } else if (!strcmp(prop, "cone_angle_outer")) {
394                 result_value = actuator->m_3d.cone_outer_angle;
395
396         } else if (!strcmp(prop, "cone_volume_outer")) {
397                 result_value = actuator->m_3d.cone_outer_gain;
398
399         } else {
400                 Py_RETURN_NONE;
401         }
402
403         PyObject *result = PyFloat_FromDouble(result_value);
404         return result;
405 }
406
407 PyObject *KX_SoundActuator::pyattr_get_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
408 {
409         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
410         float position = 0.0;
411
412         if (actuator->m_handle)
413                 position = AUD_getPosition(actuator->m_handle);
414
415         PyObject *result = PyFloat_FromDouble(position);
416
417         return result;
418 }
419
420 PyObject *KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
421 {
422         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
423         float gain = actuator->m_volume;
424
425         PyObject *result = PyFloat_FromDouble(gain);
426
427         return result;
428 }
429
430 PyObject *KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
431 {
432         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
433         float pitch = actuator->m_pitch;
434
435         PyObject *result = PyFloat_FromDouble(pitch);
436
437         return result;
438 }
439
440 PyObject *KX_SoundActuator::pyattr_get_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
441 {
442         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
443         if (actuator->m_sound)
444                 return (PyObject *)AUD_getPythonSound(actuator->m_sound);
445         else
446                 Py_RETURN_NONE;
447 }
448
449 int KX_SoundActuator::pyattr_set_3d_property(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
450 {
451         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
452         const char* prop = attrdef->m_name;
453         float prop_value = 0.0;
454
455         if (!PyArg_Parse(value, "f", &prop_value))
456                 return PY_SET_ATTR_FAIL;
457
458         // if sound is working and 3D, set the new setting
459         if (!actuator->m_is3d)
460                 return PY_SET_ATTR_FAIL;
461
462         if (!strcmp(prop, "volume_maximum")) {
463                 actuator->m_3d.max_gain = prop_value;
464                 if (actuator->m_handle)
465                         AUD_setVolumeMaximum(actuator->m_handle, prop_value);
466
467         } else if (!strcmp(prop, "volume_minimum")) {
468                 actuator->m_3d.min_gain = prop_value;
469                 if (actuator->m_handle)
470                         AUD_setVolumeMinimum(actuator->m_handle, prop_value);
471
472         } else if (!strcmp(prop, "distance_reference")) {
473                 actuator->m_3d.reference_distance = prop_value;
474                 if (actuator->m_handle)
475                         AUD_setDistanceReference(actuator->m_handle, prop_value);
476
477         } else if (!strcmp(prop, "distance_maximum")) {
478                 actuator->m_3d.max_distance = prop_value;
479                 if (actuator->m_handle)
480                         AUD_setDistanceMaximum(actuator->m_handle, prop_value);
481
482         } else if (!strcmp(prop, "attenuation")) {
483                 actuator->m_3d.rolloff_factor = prop_value;
484                 if (actuator->m_handle)
485                         AUD_setAttenuation(actuator->m_handle, prop_value);
486
487         } else if (!!strcmp(prop, "cone_angle_inner")) {
488                 actuator->m_3d.cone_inner_angle = prop_value;
489                 if (actuator->m_handle)
490                         AUD_setConeAngleInner(actuator->m_handle, prop_value);
491
492         } else if (!strcmp(prop, "cone_angle_outer")) {
493                 actuator->m_3d.cone_outer_angle = prop_value;
494                 if (actuator->m_handle)
495                         AUD_setConeAngleOuter(actuator->m_handle, prop_value);
496
497         } else if (!strcmp(prop, "cone_volume_outer")) {
498                 actuator->m_3d.cone_outer_gain = prop_value;
499                 if (actuator->m_handle)
500                         AUD_setConeVolumeOuter(actuator->m_handle, prop_value);
501
502         } else {
503                 return PY_SET_ATTR_FAIL;
504         }
505         
506         return PY_SET_ATTR_SUCCESS;
507 }
508
509 int KX_SoundActuator::pyattr_set_audposition(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
510 {
511         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
512
513         float position = 1.0;
514         if (!PyArg_Parse(value, "f", &position))
515                 return PY_SET_ATTR_FAIL;
516
517         if (actuator->m_handle)
518                 AUD_seek(actuator->m_handle, position);
519         return PY_SET_ATTR_SUCCESS;
520 }
521
522 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
523 {
524         float gain = 1.0;
525         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
526         if (!PyArg_Parse(value, "f", &gain))
527                 return PY_SET_ATTR_FAIL;
528
529         actuator->m_volume = gain;
530         if (actuator->m_handle)
531                 AUD_setSoundVolume(actuator->m_handle, gain);
532
533         return PY_SET_ATTR_SUCCESS;
534 }
535
536 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
537 {
538         float pitch = 1.0;
539         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
540         if (!PyArg_Parse(value, "f", &pitch))
541                 return PY_SET_ATTR_FAIL;
542
543         actuator->m_pitch = pitch;
544         if (actuator->m_handle)
545                 AUD_setSoundPitch(actuator->m_handle, pitch);
546
547         return PY_SET_ATTR_SUCCESS;
548 }
549
550 int KX_SoundActuator::pyattr_set_sound(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
551 {
552         PyObject *sound = NULL;
553         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
554         if (!PyArg_Parse(value, "O", &sound))
555                 return PY_SET_ATTR_FAIL;
556
557         AUD_Sound *snd = AUD_getSoundFromPython((void *)sound);
558
559         if (snd)
560         {
561                 AUD_unload(actuator->m_sound);
562                 actuator->m_sound = snd;
563                 return PY_SET_ATTR_SUCCESS;
564         }
565
566         return PY_SET_ATTR_FAIL;
567 }
568
569 #endif // WITH_PYTHON