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