Merge of itasc branch. Project files, scons and cmake should be working. Makefile...
[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, KX_ACT_SOUND)
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         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, startSound),
300         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, pauseSound),
301         KX_PYMETHODTABLE_NOARGS(KX_SoundActuator, stopSound),
302         {NULL,NULL,NULL,NULL} //Sentinel
303 };
304
305 PyAttributeDef KX_SoundActuator::Attributes[] = {
306         KX_PYATTRIBUTE_RW_FUNCTION("volume", KX_SoundActuator, pyattr_get_gain, pyattr_set_gain),
307         KX_PYATTRIBUTE_RW_FUNCTION("pitch", KX_SoundActuator, pyattr_get_pitch, pyattr_set_pitch),
308         KX_PYATTRIBUTE_RW_FUNCTION("rollOffFactor", KX_SoundActuator, pyattr_get_rollOffFactor, pyattr_set_rollOffFactor),
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         switch(AUD_getStatus(m_handle))
319         {
320         case AUD_STATUS_PLAYING:
321                 break;
322         case AUD_STATUS_PAUSED:
323                 AUD_resume(m_handle);
324                 break;
325         default:
326                 play();
327         }
328         Py_RETURN_NONE;
329 }
330
331 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, pauseSound,
332 "pauseSound()\n"
333 "\tPauses the sound.\n")
334 {
335         AUD_pause(m_handle);
336         Py_RETURN_NONE;
337 }
338
339 KX_PYMETHODDEF_DOC_NOARGS(KX_SoundActuator, stopSound,
340 "stopSound()\n"
341 "\tStops the sound.\n")
342 {
343         AUD_stop(m_handle);
344         Py_RETURN_NONE;
345 }
346
347 /* Atribute setting and getting -------------------------------------------- */
348
349 PyObject* KX_SoundActuator::pyattr_get_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
350 {
351         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
352         float gain = actuator->m_volume;
353
354         PyObject* result = PyFloat_FromDouble(gain);
355
356         return result;
357 }
358
359 PyObject* KX_SoundActuator::pyattr_get_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
360 {
361         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
362         float pitch = actuator->m_pitch;
363
364         PyObject* result = PyFloat_FromDouble(pitch);
365
366         return result;
367 }
368
369 PyObject* KX_SoundActuator::pyattr_get_rollOffFactor(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
370 {
371         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
372         float rollofffactor = actuator->m_3d.rolloff_factor;
373         PyObject* result = PyFloat_FromDouble(rollofffactor);
374
375         return result;
376 }
377
378 int KX_SoundActuator::pyattr_set_gain(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
379 {
380         float gain = 1.0;
381         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
382         if (!PyArg_Parse(value, "f", &gain))
383                 return PY_SET_ATTR_FAIL;
384
385         actuator->m_volume = gain;
386         if(actuator->m_handle)
387                 AUD_setSoundVolume(actuator->m_handle, gain);
388
389         return PY_SET_ATTR_SUCCESS;
390 }
391
392 int KX_SoundActuator::pyattr_set_pitch(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
393 {
394         float pitch = 1.0;
395         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
396         if (!PyArg_Parse(value, "f", &pitch))
397                 return PY_SET_ATTR_FAIL;
398
399         actuator->m_pitch = pitch;
400         if(actuator->m_handle)
401                 AUD_setSoundPitch(actuator->m_handle, pitch);
402
403         return PY_SET_ATTR_SUCCESS;
404 }
405
406 int KX_SoundActuator::pyattr_set_rollOffFactor(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
407 {
408         KX_SoundActuator * actuator = static_cast<KX_SoundActuator *> (self);
409         float rollofffactor = 1.0;
410         if (!PyArg_Parse(value, "f", &rollofffactor))
411                 return PY_SET_ATTR_FAIL;
412
413         actuator->m_3d.rolloff_factor = rollofffactor;
414         if(actuator->m_handle)
415                 AUD_set3DSourceSetting(actuator->m_handle, AUD_3DSS_ROLLOFF_FACTOR, rollofffactor);
416
417         return PY_SET_ATTR_SUCCESS;
418 }