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