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