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