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