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