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