024c51cabc1b780fe48589e261d07945fdd0b8e6
[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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, 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 "KX_GameObject.h"
35 #include "KX_PyMath.h" // needed for PyObjectFrom()
36 #include <iostream>
37
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41
42 /* ------------------------------------------------------------------------- */
43 /* Native functions                                                          */
44 /* ------------------------------------------------------------------------- */
45 KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
46                                                                    AUD_Sound* sound,
47                                                                    float volume,
48                                                                    float pitch,
49                                                                    bool is3d,
50                                                                    KX_3DSoundSettings settings,
51                                                                    KX_SOUNDACT_TYPE type)//,
52                                                                    : SCA_IActuator(gameobj)
53 {
54         m_sound = sound;
55         m_volume = volume;
56         m_pitch = pitch;
57         m_is3d = is3d;
58         m_3d = settings;
59         m_handle = NULL;
60         m_type = type;
61         m_isplaying = false;
62 }
63
64
65
66 KX_SoundActuator::~KX_SoundActuator()
67 {
68         if(m_handle)
69                 AUD_stop(m_handle);
70 }
71
72 void KX_SoundActuator::play()
73 {
74         if(m_handle)
75                 AUD_stop(m_handle);
76
77         if(!m_sound)
78                 return;
79
80         // this is the sound that will be played and not deleted afterwards
81         AUD_Sound* sound = m_sound;
82         // this sounds are for temporary stacked sounds, will be deleted if not NULL
83         AUD_Sound* sound2 = NULL;
84         AUD_Sound* sound3 = NULL;
85
86         switch (m_type)
87         {
88         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
89         case KX_SOUNDACT_LOOPBIDIRECTIONAL_STOP:
90                 // create a ping pong sound on sound2 stacked on the orignal sound
91                 sound2 = AUD_pingpongSound(sound);
92                 // create a loop sound on sound3 stacked on the pingpong sound and let that one play (save it to sound)
93                 sound = sound3 = AUD_loopSound(sound2);
94                 break;
95         case KX_SOUNDACT_LOOPEND:
96         case KX_SOUNDACT_LOOPSTOP:
97                 // create a loop sound on sound2 stacked on the pingpong sound and let that one play (save it to sound)
98                 sound = sound2 = AUD_loopSound(sound);
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_play3D(sound, 0);
110
111                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_MAX_GAIN, m_3d.max_gain);
112                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_MIN_GAIN, m_3d.min_gain);
113                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_REFERENCE_DISTANCE, m_3d.reference_distance);
114                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_MAX_DISTANCE, m_3d.max_distance);
115                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_ROLLOFF_FACTOR, m_3d.rolloff_factor);
116                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_CONE_INNER_ANGLE, m_3d.cone_inner_angle);
117                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_CONE_OUTER_ANGLE, m_3d.cone_outer_angle);
118                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_CONE_OUTER_GAIN, m_3d.cone_outer_gain);
119         }
120         else
121                 m_handle = AUD_play(sound, 0);
122
123         AUD_setSoundPitch(m_handle, m_pitch);
124         AUD_setSoundVolume(m_handle, m_volume);
125         m_isplaying = true;
126
127         // now we unload the pingpong and loop sounds, as we don't need them anymore
128         // the started sound will continue playing like it was created, don't worry!
129         if(sound3)
130                 AUD_unload(sound3);
131         if(sound2)
132                 AUD_unload(sound2);
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 = 0;
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)
161                 return false;
162
163         // actual audio device playing state
164         bool isplaying = AUD_getStatus(m_handle) == 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                                         AUD_stop(m_handle);
179                                         break;
180                                 }
181                         case KX_SOUNDACT_PLAYEND:
182                                 {
183                                         // do nothing, sound will stop anyway when it's finished
184                                         break;
185                                 }
186                         case KX_SOUNDACT_LOOPEND:
187                         case KX_SOUNDACT_LOOPBIDIRECTIONAL:
188                                 {
189                                         // stop the looping so that the sound stops when it finished
190                                         AUD_stopLoop(m_handle);
191                                         break;
192                                 }
193                         default:
194                                 // implement me !!
195                                 break;
196                         }
197                 }
198                 // remember that we tried to stop the actuator
199                 m_isplaying = false;
200         }
201         
202 #if 1
203         // Warning: when de-activating the actuator, after a single negative event this runs again with...
204         // m_posevent==false && m_posevent==false, in this case IsNegativeEvent() returns false 
205         // and assumes this is a positive event.
206         // check that we actually have a positive event so as not to play sounds when being disabled.
207         else if(bPositiveEvent) { // <- added since 2.49
208 #else
209         else {  // <- works in most cases except a loop-end sound will never stop unless
210                         // the negative pulse is done continuesly
211 #endif
212                 if (!m_isplaying)
213                         play();
214         }
215         // verify that the sound is still playing
216         isplaying = AUD_getStatus(m_handle) == AUD_STATUS_PLAYING ? true : false;
217
218         if (isplaying)
219         {
220                 if(m_is3d)
221                 {
222                         AUD_3DData data;
223                         float f;
224                         ((KX_GameObject*)this->GetParent())->NodeGetWorldPosition().getValue(data.position);
225                         ((KX_GameObject*)this->GetParent())->GetLinearVelocity().getValue(data.velocity);
226                         ((KX_GameObject*)this->GetParent())->NodeGetWorldOrientation().getValue3x3(data.orientation);
227
228                         /*
229                          * The 3D data from blender has to be transformed for OpenAL:
230                          *  - In blender z is up and y is forwards
231                          *  - In OpenAL y is up and z is backwards
232                          * We have to do that for all 5 vectors.
233                          */
234                         f = data.position[1];
235                         data.position[1] = data.position[2];
236                         data.position[2] = -f;
237
238                         f = data.velocity[1];
239                         data.velocity[1] = data.velocity[2];
240                         data.velocity[2] = -f;
241
242                         f = data.orientation[1];
243                         data.orientation[1] = data.orientation[2];
244                         data.orientation[2] = -f;
245
246                         f = data.orientation[4];
247                         data.orientation[4] = data.orientation[5];
248                         data.orientation[5] = -f;
249
250                         f = data.orientation[7];
251                         data.orientation[7] = data.orientation[8];
252                         data.orientation[8] = -f;
253
254                         AUD_update3DSource(m_handle, &data);
255                 }
256                 result = true;
257         }
258         else
259         {
260                 m_isplaying = false;
261                 result = false;
262         }
263         return result;
264 }
265
266
267
268
269 /* ------------------------------------------------------------------------- */
270 /* Python functions                                                          */
271 /* ------------------------------------------------------------------------- */
272
273
274
275 /* Integration hooks ------------------------------------------------------- */
276 PyTypeObject KX_SoundActuator::Type = {
277         PyVarObject_HEAD_INIT(NULL, 0)
278         "KX_SoundActuator",
279         sizeof(PyObjectPlus_Proxy),
280         0,
281         py_base_dealloc,
282         0,
283         0,
284         0,
285         0,
286         py_base_repr,
287         0,0,0,0,0,0,0,0,0,
288         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
289         0,0,0,0,0,0,0,
290         Methods,
291         0,
292         0,
293         &SCA_IActuator::Type,
294         0,0,0,0,0,0,
295         py_base_new
296 };
297
298 PyMethodDef KX_SoundActuator::Methods[] = {
299         // Deprecated ----->
300         {"setGain",(PyCFunction) KX_SoundActuator::sPySetGain,METH_VARARGS,NULL},
301         {"getGain",(PyCFunction) KX_SoundActuator::sPyGetGain,METH_NOARGS,NULL},
302         {"setPitch",(PyCFunction) KX_SoundActuator::sPySetPitch,METH_VARARGS,NULL},
303         {"getPitch",(PyCFunction) KX_SoundActuator::sPyGetPitch,METH_NOARGS,NULL},
304         {"setRollOffFactor",(PyCFunction) KX_SoundActuator::sPySetRollOffFactor,METH_VARARGS,NULL},
305         {"getRollOffFactor",(PyCFunction) KX_SoundActuator::sPyGetRollOffFactor,METH_NOARGS,NULL},
306         {"setType",(PyCFunction) KX_SoundActuator::sPySetType,METH_VARARGS,NULL},
307         {"getType",(PyCFunction) KX_SoundActuator::sPyGetType,METH_NOARGS,NULL},
308         // <-----
309
310         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
311         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
312         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
313         {NULL,NULL,NULL,NULL} //Sentinel
314 };
315
316 PyAttributeDef KX_SoundActuator::Attributes[] = {
317         KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
318         KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
319         KX_PYATTRIBUTE_RW_FUNCTION("rollOffFactor", KX_SoundActuator, pyattr_get_rollOffFactor, pyattr_set_rollOffFactor),
320         KX_PYATTRIBUTE_ENUM_RW("mode",KX_SoundActuator::KX_SOUNDACT_NODEF+1,KX_SoundActuator::KX_SOUNDACT_MAX-1,false,KX_SoundActuator,m_type),
321         { NULL }        //Sentinel
322 };
323
324 /* Methods ----------------------------------------------------------------- */
325 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, startSound,
326 "startSound()\n"
327 "\tStarts the sound.\n")
328 {
329         switch(AUD_getStatus(m_handle))
330         {
331         case AUD_STATUS_PLAYING:
332                 break;
333         case AUD_STATUS_PAUSED:
334                 AUD_resume(m_handle);
335                 break;
336         default:
337                 play();
338         }
339         Py_RETURN_NONE;
340 }
341
342 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
343 "pauseSound()\n"
344 "\tPauses the sound.\n")
345 {
346         AUD_pause(m_handle);
347         Py_RETURN_NONE;
348 }
349
350 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
351 "stopSound()\n"
352 "\tStops the sound.\n")
353 {
354         AUD_stop(m_handle);
355         Py_RETURN_NONE;
356 }
357
358 /* Atribute setting and getting -------------------------------------------- */
359
360 PyObject* KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
361 {
362         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
363         float gain = actuator->m_volume;
364
365         PyObject* result = PyFloat_FromDouble(gain);
366
367         return result;
368 }
369
370 PyObject* KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
371 {
372         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
373         float pitch = actuator->m_pitch;
374
375         PyObject* result = PyFloat_FromDouble(pitch);
376
377         return result;
378 }
379
380 PyObject* KX_SoundActuator::pyattr_get_rollOffFactor(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
381 {
382         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
383         float rollofffactor = actuator->m_3d.rolloff_factor;
384         PyObject* result = PyFloat_FromDouble(rollofffactor);
385
386         return result;
387 }
388
389 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
390 {
391         float gain = 1.0;
392         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
393         if (!PyArg_Parse(value, "f", &gain))
394                 return PY_SET_ATTR_FAIL;
395
396         actuator->m_volume = gain;
397         if(actuator->m_handle)
398                 AUD_setSoundVolume(actuator->m_handle, gain);
399
400         return PY_SET_ATTR_SUCCESS;
401 }
402
403 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
404 {
405         float pitch = 1.0;
406         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
407         if (!PyArg_Parse(value, "f", &pitch))
408                 return PY_SET_ATTR_FAIL;
409
410         actuator->m_pitch = pitch;
411         if(actuator->m_handle)
412                 AUD_setSoundPitch(actuator->m_handle, pitch);
413
414         return PY_SET_ATTR_SUCCESS;
415 }
416
417 int KX_SoundActuator::pyattr_set_rollOffFactor(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
418 {
419         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
420         float rollofffactor = 1.0;
421         if (!PyArg_Parse(value, "f", &rollofffactor))
422                 return PY_SET_ATTR_FAIL;
423
424         actuator->m_3d.rolloff_factor = rollofffactor;
425         if(actuator->m_handle)
426                 AUD_set3DSourceSetting(actuator->m_handle, AUD_3DSS_ROLLOFF_FACTOR, rollofffactor);
427
428         return PY_SET_ATTR_SUCCESS;
429 }
430
431 PyObject* KX_SoundActuator::PySetGain(PyObject* args)
432 {
433         ShowDeprecationWarning("setGain()", "the volume property");
434         float gain = 1.0;
435         if (!PyArg_ParseTuple(args, "f:setGain", &gain))
436                 return NULL;
437
438         m_volume = gain;
439         if(m_handle)
440                 AUD_setSoundVolume(m_handle, gain);
441
442         Py_RETURN_NONE;
443 }
444
445
446
447 PyObject* KX_SoundActuator::PyGetGain()
448 {
449         ShowDeprecationWarning("getGain()", "the volume property");
450         float gain = m_volume;
451         PyObject* result = PyFloat_FromDouble(gain);
452
453         return result;
454 }
455
456
457
458 PyObject* KX_SoundActuator::PySetPitch(PyObject* args)
459 {
460         ShowDeprecationWarning("setPitch()", "the pitch property");
461         float pitch = 1.0;
462         if (!PyArg_ParseTuple(args, "f:setPitch", &pitch))
463                 return NULL;
464
465         m_pitch = pitch;
466         if(m_handle)
467                 AUD_setSoundPitch(m_handle, pitch);
468
469         Py_RETURN_NONE;
470 }
471
472
473
474 PyObject* KX_SoundActuator::PyGetPitch()
475 {
476         ShowDeprecationWarning("getPitch()", "the pitch property");
477         float pitch = m_pitch;
478         PyObject* result = PyFloat_FromDouble(pitch);
479
480         return result;
481 }
482
483
484
485 PyObject* KX_SoundActuator::PySetRollOffFactor(PyObject* args)
486 {
487         ShowDeprecationWarning("setRollOffFactor()", "the rollOffFactor property");
488         float rollofffactor = 1.0;
489         if (!PyArg_ParseTuple(args, "f:setRollOffFactor", &rollofffactor))
490                 return NULL;
491
492         m_3d.rolloff_factor = rollofffactor;
493         if(m_handle)
494                 AUD_set3DSourceSetting(m_handle, AUD_3DSS_ROLLOFF_FACTOR, rollofffactor);
495
496         Py_RETURN_NONE;
497 }
498
499
500
501 PyObject* KX_SoundActuator::PyGetRollOffFactor()
502 {
503         ShowDeprecationWarning("getRollOffFactor()", "the rollOffFactor property");
504         float rollofffactor = m_3d.rolloff_factor;
505         PyObject* result = PyFloat_FromDouble(rollofffactor);
506
507         return result;
508 }
509
510
511
512 PyObject* KX_SoundActuator::PySetType(PyObject* args)
513 {
514         int typeArg;
515         ShowDeprecationWarning("setType()", "the mode property");
516
517         if (!PyArg_ParseTuple(args, "i:setType", &typeArg)) {
518                 return NULL;
519         }
520
521         if ( (typeArg > KX_SOUNDACT_NODEF)
522           && (typeArg < KX_SOUNDACT_MAX) ) {
523                 m_type = (KX_SOUNDACT_TYPE) typeArg;
524         }
525
526         Py_RETURN_NONE;
527 }
528
529 PyObject* KX_SoundActuator::PyGetType()
530 {
531         ShowDeprecationWarning("getType()", "the mode property");
532         return PyLong_FromSsize_t(m_type);
533 }
534 // <-----
535