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