FreeWRL / FreeX3D 4.3.0
Component_Sound.c
1/*
2
3
4X3D Sound Component
5
6*/
7
8/****************************************************************************
9 This file is part of the FreeWRL/FreeX3D Distribution.
10
11 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
12
13 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Lesser Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
25****************************************************************************/
26
27
28
29#include <config.h>
30#include <system.h>
31#include <display.h>
32#include <internal.h>
33
34#include <libFreeWRL.h>
35
36#include "../vrml_parser/Structs.h"
37#include "../vrml_parser/CRoutes.h"
38#include "../main/headers.h"
39#include "../opengl/OpenGL_Utils.h"
40
41#include "LinearAlgebra.h"
42#include "sounds.h"
43
44#ifdef HAVE_OPENAL
45//#include <AL/alhelpers.c>
46/* InitAL opens the default device and sets up a context using default
47 * attributes, making the program ready to call OpenAL functions. */
48void* fwInitAL(void)
49{
50 ALCdevice *device;
51 ALCcontext *ctx;
52
53 /* Open and initialize a device with default settings */
54 device = alcOpenDevice(NULL);
55 if(!device)
56 {
57 fprintf(stderr, "Could not open a device!\n");
58 return NULL;
59 }
60
61 ctx = alcCreateContext(device, NULL);
62 if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)
63 {
64 if(ctx != NULL)
65 alcDestroyContext(ctx);
66 alcCloseDevice(device);
67 fprintf(stderr, "Could not set a context!\n");
68 return NULL;
69 }
70
71 printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
72 return ctx;
73}
74
75/* CloseAL closes the device belonging to the current context, and destroys the
76 * context. */
77void fwCloseAL(void *alctx)
78{
79 ALCdevice *device;
80 ALCcontext *ctx;
81
82 //ctx = alcGetCurrentContext();
83 ctx = alctx;
84 if(ctx == NULL)
85 return;
86
87 device = alcGetContextsDevice(ctx);
88
89 alcMakeContextCurrent(NULL);
90 alcDestroyContext(ctx);
91 alcCloseDevice(device);
92}
93
94#endif
95
96
97typedef struct pComponent_Sound{
98 /* for printing warnings about Sound node problems - only print once per invocation */
99 int soundWarned;// = FALSE;
100 int SoundSourceNumber;
101 void *alContext;
102/* this is used to return the duration of an audioclip to the perl
103 side of things. works, but need to figure out all
104 references, etc. to bypass this fudge JAS */
105 float AC_LastDuration[50];
107void *Component_Sound_constructor(){
108 void *v = MALLOCV(sizeof(struct pComponent_Sound));
109 memset(v,0,sizeof(struct pComponent_Sound));
110 return v;
111}
112void Component_Sound_init(struct tComponent_Sound *t){
113 //public
114 /* Sounds can come from AudioClip nodes, or from MovieTexture nodes. Different
115 structures on these */
116 t->sound_from_audioclip= 0;
117
118 /* is the sound engine started yet? */
119 t->SoundEngineStarted = FALSE;
120 //private
121 t->prv = Component_Sound_constructor();
122 {
124 /* for printing warnings about Sound node problems - only print once per invocation */
125 p->soundWarned = FALSE;
126 p->SoundSourceNumber = 0;
127 p->alContext = NULL;
128 /* this is used to return the duration of an audioclip to the perl
129 side of things. works, but need to figure out all
130 references, etc. to bypass this fudge JAS */
131 {
132 int i;
133 for(i=0;i<50;i++)
134 p->AC_LastDuration[i] = -1.0f;
135 }
136 }
137}
138//ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
139void Sound_toserver(char *message)
140{}
141
142// Position of the listener.
143float ListenerPos[] = { 0.0, 0.0, 0.0 };
144// Velocity of the listener.
145float ListenerVel[] = { 0.0, 0.0, 0.0 };
146// Orientation of the listener. (first 3 elements are "at", second 3 are "up")
147float ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
148
149int SoundEngineInit(void)
150{
151 int retval = FALSE;
152#ifdef HAVE_OPENAL
153 {
154 void *alctx;
155 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
156 retval = TRUE;
157 /* Initialize OpenAL with the default device, and check for EFX support. */
158 alctx = fwInitAL();
159 if(!alctx ){
160 ConsoleMessage("initAL failed\n");
161 retval = FALSE;
162 }
163 p->alContext = alctx;
164#ifdef HAVE_ALUT
165 if(!alutInitWithoutContext(NULL,NULL)) //this does not create an AL context (simple)
166 {
167 ALenum error = alutGetError ();
168 ConsoleMessage("%s\n", alutGetErrorString (error));
169 retval = FALSE;
170 }
171#endif //HAVE_ALUT
172
173 //listener is avatar,
174 //we could move both listener and sources in world coordinates
175 //instead we'll work in avatar/local coordinates and
176 //freeze listener at 0,0,0 and update the position of the sound sources
177 //relative to listener, on each frame
178 alListenerfv(AL_POSITION, ListenerPos);
179 alListenerfv(AL_VELOCITY, ListenerVel);
180 alListenerfv(AL_ORIENTATION, ListenerOri);
181 if(1){
182 //ALenum error;
183 if(FALSE) //meters)
184 alSpeedOfSound(345.0f); //alDopplerVelocity(34.0f); //m/s
185 else //feet
186 alSpeedOfSound(1132.0f); //alDopplerVelocity(1132.0f); // using feet/second – change propagation velocity
187 alDopplerFactor(1.0f); // exaggerate pitch shift by 20%
188 //if ((error = alGetError()) != AL_NO_ERROR) DisplayALError("alDopplerX : ", error);
189 }
190 alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); //here's what I think web3d wants
191 //alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); //seems a bit faint
192 }
193#endif //HAVE_OPENAL
194 gglobal()->Component_Sound.SoundEngineStarted = retval;
195 return retval;
196}
197
198void waitformessage(void)
199{}
200
201void SoundEngineDestroy(void)
202{}
203
204int SoundSourceRegistered(int num)
205{
206 if(num > -1) return TRUE;
207 return FALSE;
208}
209
210float SoundSourceInit(int num, int loop, double pitch, double start_time, double stop_time, char *url)
211{return 0.0f;}
212
213void SetAudioActive(int num, int stat)
214{}
215
216
217int haveSoundEngine(){
218 ttglobal tg = gglobal();
219
220 if (!tg->Component_Sound.SoundEngineStarted) {
221 #ifdef SEVERBOSE
222 printf ("SetAudioActive: initializing SoundEngine\n");
223 #endif
224 tg->Component_Sound.SoundEngineStarted = SoundEngineInit();
225 }
226 return tg->Component_Sound.SoundEngineStarted;
227}
228
229#ifdef OLDCODE
230OLDCODEvoid render_AudioControl (struct X3D_AudioControl *node) {
231OLDCODE GLDOUBLE mod[16];
232OLDCODE GLDOUBLE proj[16];
233OLDCODE struct point_XYZ vec, direction, location;
234OLDCODE double len;
235OLDCODE double angle;
236OLDCODE float midmin, midmax;
237OLDCODE
238OLDCODE /* do the sound registering first, and tell us if this is an audioclip*/
239OLDCODE /* or movietexture.*/
240OLDCODE
241OLDCODE
242OLDCODE /* if not enabled, do nothing */
243OLDCODE if (!node) return;
244OLDCODE if (node->__oldEnabled != node->enabled) {
245OLDCODE node->__oldEnabled = node->enabled;
246OLDCODE MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_AudioControl, enabled));
247OLDCODE }
248OLDCODE if (!node->enabled) return;
249OLDCODE
250OLDCODE direction.x = node->direction.c[0];
251OLDCODE direction.y = node->direction.c[1];
252OLDCODE direction.z = node->direction.c[2];
253OLDCODE
254OLDCODE location.x = node->location.c[0];
255OLDCODE location.y = node->location.c[1];
256OLDCODE location.z = node->location.c[2];
257OLDCODE
258OLDCODE midmin = (node->minFront - node->minBack) / (float) 2.0;
259OLDCODE midmax = (node->maxFront - node->maxBack) / (float) 2.0;
260OLDCODE
261OLDCODE
262OLDCODE FW_GL_PUSH_MATRIX();
263OLDCODE
264OLDCODE /*
265OLDCODE first, find whether or not we are within the maximum circle.
266OLDCODE
267OLDCODE translate to the location, and move the centre point, depending
268OLDCODE on whether we have a direction and differential maxFront and MaxBack
269OLDCODE directions.
270OLDCODE */
271OLDCODE
272OLDCODE FW_GL_TRANSLATE_D (location.x + midmax*direction.x,
273OLDCODE location.y + midmax*direction.y,
274OLDCODE location.z + midmax * direction.z);
275OLDCODE
276OLDCODE /* make the ellipse a circle by scaling...
277OLDCODE FW_GL_SCALE_F (direction.x*2.0 + 0.5, direction.y*2.0 + 0.5, direction.z*2.0 + 0.5);
278OLDCODE - scaling needs work - we need direction information, and parameter work. */
279OLDCODE
280OLDCODE if ((fabs(node->minFront - node->minBack) > 0.5) ||
281OLDCODE (fabs(node->maxFront - node->maxBack) > 0.5)) {
282OLDCODE if (!soundWarned) {
283OLDCODE printf ("FreeWRL:Sound: Warning - minBack and maxBack ignored in this version\n");
284OLDCODE soundWarned = TRUE;
285OLDCODE }
286OLDCODE }
287OLDCODE
288OLDCODE
289OLDCODE
290OLDCODE FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
291OLDCODE FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
292OLDCODE FW_GLU_UNPROJECT(viewport[2]/2,viewport[3]/2,0.0,
293OLDCODE mod,proj,viewport, &vec.x,&vec.y,&vec.z);
294OLDCODE /* printf ("mod %lf %lf %lf proj %lf %lf %lf\n",*/
295OLDCODE /* mod[12],mod[13],mod[14],proj[12],proj[13],proj[14]);*/
296OLDCODE
297OLDCODE len = sqrt(VECSQ(vec));
298OLDCODE /* printf ("len %f\n",len); */
299OLDCODE /* printf("Sound: len %f mB %f mF %f angles (%f %f %f)\n",len,*/
300OLDCODE /* -node->maxBack, node->maxFront,vec.x,vec.y,vec.z);*/
301OLDCODE
302OLDCODE
303OLDCODE /* pan left/right. full left = 0; full right = 1.*/
304OLDCODE if (len < 0.001) angle = 0;
305OLDCODE else {
306OLDCODE if (APPROX (mod[12],0)) {
307OLDCODE /* printf ("mod12 approaches zero\n");*/
308OLDCODE mod[12] = 0.001;
309OLDCODE }
310OLDCODE angle = fabs(atan2(mod[14],mod[12])) - (PI/2.0);
311OLDCODE angle = angle/(PI/2.0);
312OLDCODE
313OLDCODE /* Now, scale this angle to make it between -0.5*/
314OLDCODE /* and +0.5; if we divide it by 2.0, we will get*/
315OLDCODE /* this range, but if we divide it by less, then*/
316OLDCODE /* the sound goes "hard over" to left or right for*/
317OLDCODE /* a bit.*/
318OLDCODE angle = angle / 1.5;
319OLDCODE
320OLDCODE /* now scale to 0 to 1*/
321OLDCODE angle = angle + 0.5;
322OLDCODE
323OLDCODE /* and, "reverse" the value, so that left is left, and right is right */
324OLDCODE angle = 1.0 - angle;
325OLDCODE
326OLDCODE /* bounds check...*/
327OLDCODE if (angle > 1.0) angle = 1.0;
328OLDCODE if (angle < 0.0) angle = 0.0;
329OLDCODE
330OLDCODE #ifdef SOUNDVERBOSE
331OLDCODE printf ("angle: %f\n",angle);
332OLDCODE #endif
333OLDCODE }
334OLDCODE
335OLDCODE /* convert to a MIDI control value */
336OLDCODE node->panFloatVal = (float) angle;
337OLDCODE node->panInt32Val = (int) (angle * 128);
338OLDCODE if (node->panInt32Val < 0) node->panInt32Val = 0; if (node->panInt32Val > 127) node->panInt32Val = 127;
339OLDCODE
340OLDCODE
341OLDCODE node->volumeFloatVal = (float) 0.0;
342OLDCODE /* is this within the maxFront maxBack? */
343OLDCODE
344OLDCODE /* this code needs rework JAS */
345OLDCODE if (len < node->maxFront) {
346OLDCODE /* did this node become active? */
347OLDCODE if (!node->isActive) {
348OLDCODE node->isActive = TRUE;
349OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, isActive));
350OLDCODE #ifdef SOUNDVERBOSE
351OLDCODE printf ("AudioControl node is now ACTIVE\n");
352OLDCODE #endif
353OLDCODE
354OLDCODE
355OLDCODE /* record the length for doppler shift comparisons */
356OLDCODE node->__oldLen = len;
357OLDCODE }
358OLDCODE
359OLDCODE /* note: using vecs, length is always positive - need to work in direction
360OLDCODE vector */
361OLDCODE if (len < 0.0) {
362OLDCODE if (len < node->minBack) {node->volumeFloatVal = (float) 1.0;}
363OLDCODE else { node->volumeFloatVal = ((float) len - node->maxBack) / (node->maxBack - node->minBack); }
364OLDCODE } else {
365OLDCODE if (len < node->minFront) {node->volumeFloatVal = (float) 1.0;}
366OLDCODE else { node->volumeFloatVal = (node->maxFront - (float) len) / (node->maxFront - node->minFront); }
367OLDCODE }
368OLDCODE
369OLDCODE /* work out the delta for len */
370OLDCODE if (APPROX(node->maxDelta, 0.0)) {
371OLDCODE printf ("AudioControl: maxDelta approaches zero!\n");
372OLDCODE node->deltaFloatVal = (float) 0.0;
373OLDCODE } else {
374OLDCODE #ifdef SOUNDVERBOSE
375OLDCODE printf ("maxM/S %f \n",(node->__oldLen - len)/ (TickTime()- lastTime));
376OLDCODE #endif
377OLDCODE
378OLDCODE /* calculate change as Metres/second */
379OLDCODE
380OLDCODE /* compute node->deltaFloatVal, and clamp to range of -1.0 to 1.0 */
381OLDCODE node->deltaFloatVal = (float) ((node->__oldLen - len)/(TickTime()-lastTime()))/node->maxDelta;
382OLDCODE if (node->deltaFloatVal < (float) -1.0) node->deltaFloatVal = (float) -1.0; if (node->deltaFloatVal > (float) 1.0) node->deltaFloatVal = (float) 1.0;
383OLDCODE node->__oldLen = len;
384OLDCODE }
385OLDCODE
386OLDCODE /* Now, fit in the intensity. Send along command, with
387OLDCODE source number, amplitude, balance, and the current Framerate */
388OLDCODE node->volumeFloatVal = node->volumeFloatVal*node->intensity;
389OLDCODE node->volumeInt32Val = (int) (node->volumeFloatVal * 128.0);
390OLDCODE if (node->volumeInt32Val < 0) node->volumeInt32Val = 0; if (node->volumeInt32Val > 127) node->volumeInt32Val = 127;
391OLDCODE
392OLDCODE node->deltaInt32Val = (int) (node->deltaFloatVal * 64.0) + 64;
393OLDCODE if (node->deltaInt32Val < 0) node->deltaInt32Val = 0; if (node->deltaInt32Val > 127) node->deltaInt32Val = 127;
394OLDCODE
395OLDCODE #ifdef SOUNDVERBOSE
396OLDCODE printf ("AudioControl: amp: %f (%d) angle: %f (%d) delta: %f (%d)\n",node->volumeFloatVal,node->volumeInt32Val,
397OLDCODE node->panFloatVal, node->panInt32Val ,node->deltaFloatVal,node->deltaInt32Val);
398OLDCODE #endif
399OLDCODE
400OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, volumeInt32Val));
401OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, volumeFloatVal));
402OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, panInt32Val));
403OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, panFloatVal));
404OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, deltaInt32Val));
405OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, deltaFloatVal));
406OLDCODE
407OLDCODE } else {
408OLDCODE /* node just became inActive */
409OLDCODE if (node->isActive) {
410OLDCODE node->isActive = FALSE;
411OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, isActive));
412OLDCODE #ifdef SOUNDVERBOSE
413OLDCODE printf ("AudioControl node is now INACTIVE\n");
414OLDCODE #endif
415OLDCODE }
416OLDCODE }
417OLDCODE
418OLDCODE FW_GL_POP_MATRIX();
419OLDCODE}
420#endif // OLDCODE
421
422#define LOAD_INITIAL_STATE 0
423#define LOAD_REQUEST_RESOURCE 1
424#define LOAD_FETCHING_RESOURCE 2
425//#define LOAD_PARSING 3
426#define LOAD_STABLE 10
427
428void locateAudioSource (struct X3D_AudioClip *node) {
429 resource_item_t *res;
430 //resource_item_t *parentPath;
431 //ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
432
433 switch (node->__loadstatus) {
434 case LOAD_INITIAL_STATE: /* nothing happened yet */
435
436 if (node->url.n == 0) {
437 node->__loadstatus = LOAD_STABLE; /* a "do-nothing" approach */
438 break;
439 } else {
440 res = resource_create_multi(&(node->url));
441 if(node->_nodeType == NODE_MovieTexture)
442 res->media_type = resm_movie;
443 else //if(node->_nodeType == NODE_AudioClip)
444 res->media_type = resm_audio;
445 node->__loadstatus = LOAD_REQUEST_RESOURCE;
446 node->__loadResource = res;
447 }
448 //printf("1");
449 break;
450
451 case LOAD_REQUEST_RESOURCE:
452 res = node->__loadResource;
453 resource_identify(node->_parentResource, res);
454 res->actions = resa_download | resa_load; //not resa_parse which we do below
455 res->ectx = (void*)node->_executionContext;
456 res->whereToPlaceData = X3D_NODE(node);
457 //res->offsetFromWhereToPlaceData = offsetof (struct X3D_AudioClip, __FILEBLOB);
458 resitem_enqueue(ml_new(res));
459 node->__loadstatus = LOAD_FETCHING_RESOURCE;
460 //printf("2");
461 break;
462
463 case LOAD_FETCHING_RESOURCE:
464 res = node->__loadResource;
465 /* printf ("load_Inline, we have type %s status %s\n",
466 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
467 if(res->complete){
468 if (res->status == ress_loaded) {
469 res->actions = resa_process;
470 res->complete = FALSE;
471 resitem_enqueue(ml_new(res));
472 } else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
473 //no hope left
474 printf ("resource failed to load\n");
475 node->__loadstatus = LOAD_STABLE; // a "do-nothing" approach
476 node->__sourceNumber = BADAUDIOSOURCE;
477 } else if (res->status == ress_parsed) {
478 node->__loadstatus = LOAD_STABLE;
479 } //if (res->status == ress_parsed)
480 } //if(res->complete)
481 //end case LOAD_FETCHING_RESOURCE
482 //printf("3");
483 break;
484
485 case LOAD_STABLE:
486 //printf("4");
487 break;
488 }
489}
490int loadstatus_AudioClip(struct X3D_AudioClip *node){
491 int istate = 0;
492 if(node){
493 if(node->__loadstatus > LOAD_INITIAL_STATE && node->__loadstatus < LOAD_STABLE)
494 istate = 1;
495 if(node->__loadstatus == LOAD_STABLE)
496 istate = 2;
497 }
498 return istate;
499}
500void render_AudioClip (struct X3D_AudioClip *node) {
501/* audio clip is a flat sound -no 3D- and a sound node (3D) refers to it
502 specs: if an audioclip can't be reached in the scenegraph, then it doesn't play
503*/
504
505 /* is this audio wavelet initialized yet? */
506 if (node->__loadstatus != LOAD_STABLE) {
507 locateAudioSource (node);
508 }
509 if(node->__loadstatus != LOAD_STABLE) return;
510 /* is this audio ok? if so, the sourceNumber will range
511 * between 0 and infinity; if it is BADAUDIOSOURCE, bad source.
512 * check out locateAudioSource to find out reasons */
513 if (node->__sourceNumber == BADAUDIOSOURCE) return;
514
515
516
517#ifdef HAVE_OLDSOUND //MUST_RE_IMPLEMENT_SOUND_WITH_OPENAL
518 /* register an audioclip*/
519 float pitch,stime, sttime;
520 int loop;
521 int sound_from_audioclip;
522 unsigned char *filename = (unsigned char *)node->__localFileName;
523 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
524
525 /* tell Sound that this is an audioclip */
526 sound_from_audioclip = TRUE;
527
528 /* printf ("_change %d _ichange %d\n",node->_change, node->_ichange); */
529
530 if(!haveSoundEngine()) return;
531
532#ifndef JOHNSOUND
533 if (node->isActive == 0) return; /* not active, so just bow out*/
534#endif
535
536 if (!SoundSourceRegistered(node->__sourceNumber)) {
537
538 /* printf ("AudioClip: registering clip %d loop %d p %f s %f st %f url %s\n",
539 node->__sourceNumber, node->loop, node->pitch,node->startTime, node->stopTime,
540 filename); */
541
542 pitch = node->pitch;
543 stime = node->startTime;
544 sttime = node->stopTime;
545 loop = node->loop;
546
547 p->AC_LastDuration[node->__sourceNumber] =
548 SoundSourceInit (node->__sourceNumber, node->loop,
549 (double) pitch,(double) stime, (double) sttime, filename);
550 /* printf ("globalDuration source %d %f\n",
551 node->__sourceNumber,AC_LastDuration[node->__sourceNumber]); */
552 }
553#endif /* MUST_RE_IMPLEMENT_SOUND_WITH_OPENAL */
554}
555
556
557
558void render_Sound (struct X3D_Sound *node) {
559/* updates the position and velocity vector of the sound source relative to the listener/avatar
560 so 3D sound effects can be rendered: distance attenuation, stereo left/right volume balance,
561 and doppler (pitch) effect
562 - refers to sound source ie audioclip or movie
563 - an audioclip may be DEFed and USEd in multiple Sounds, but will be playing the same tune at the same time
564*/
565 int sound_from_audioclip;
566
567 struct X3D_AudioClip *acp = NULL;
568 struct X3D_MovieTexture *mcp = NULL;
569 struct X3D_Node *tmpN = NULL;
570 //ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
571
572 /* why bother doing this if there is no source? */
573 if (node->source == NULL)
574 return;
575
576 /* ok, is the source a valid node?? */
577
578 /* might be a PROTO expansion, as in what Adam Nash does... */
579 POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->source,tmpN)
580
581 /* did not find a valid source node, even after really looking at a PROTO def */
582 if (tmpN == NULL) return;
583
584 sound_from_audioclip = FALSE;
585 if (tmpN->_nodeType == NODE_AudioClip) {
586 acp = (struct X3D_AudioClip *) tmpN;
587 sound_from_audioclip = TRUE;
588 }else if (tmpN->_nodeType == NODE_MovieTexture){
589 //mcp = (struct X3D_MovieTexture *) tmpN;
590 //july 2016 ordered fields in MovieTexture to mach AudioClip, can up-caste
591 acp = (struct X3D_AudioClip *) tmpN;
592 } else {
593 ConsoleMessage ("Sound node- source type of %s invalid",stringNodeType(tmpN->_nodeType));
594 node->source = NULL; /* stop messages from scrolling forever */
595 return;
596 }
597
598#ifdef HAVE_OPENAL
599 /* 4 sources of openAL explanations and examples:
600 - http://open-activewrl.sourceforge.net/data/OpenAL_PGuide.pdf
601 - http://forum.devmaster.net/t and type 'openal' in the search box to get several lessons on openal
602 - http://kcat.strangesoft.net/openal.html example code (win32 desktop is using this openal-soft implementation of openal)
603 - http://en.wikipedia.org/wiki/OpenAL links
604 - <al.h> comments
605 */
606 if(acp){
607 if(haveSoundEngine()){
608 if( acp->__sourceNumber < 0){
609 render_AudioClip(acp);
610 }
611 if( acp->__sourceNumber > -1 ){
612 //have a buffer loaded
613 int i;
614 GLDOUBLE modelMatrix[16];
615 GLDOUBLE SourcePosd[3] = { 0.0f, 0.0f, 0.0f };
616 ALfloat SourcePos[3];
617
618 //transform source local coordinate 0,0,0 location into avatar/listener space
619 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelMatrix);
620 transformAFFINEd(SourcePosd,SourcePosd,modelMatrix);
621 for(i=0;i<3;i++) SourcePos[i] = (ALfloat)SourcePosd[i];
622
623 if( node->__sourceNumber < 0){
624 //convert buffer to openAL sound source
625 ALint source;
626 source = 0;
627 alGenSources(1, (ALuint *)&source);
628 alSourcei(source, AL_BUFFER, acp->__sourceNumber);
629 alSourcef (source, AL_PITCH, acp->pitch);
630 alSourcef (source, AL_GAIN, node->intensity );
631 alSourcei (source, AL_LOOPING, acp->loop);
632 alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE); //we'll treat the avatar/listener as fixed, and the sources moving relative
633 //openAL will automatically mix multiple sources for one listener, there's no need for .priority hint
634 alSourcef (source, AL_MAX_DISTANCE, node->maxFront);
635 //no attempt is made to implement minBack, maxBack ellipsoidal as in web3d specs
636 //- just a spherical sound, and with spatialize attempt at a cone
637 node->__lasttime = TickTime();
638 veccopy3f(node->__lastlocation.c,SourcePos);
639
640 node->__sourceNumber = source;
641 //assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
642 if(alGetError()!=AL_NO_ERROR) {
643 static int once = 0;
644 if(!once){
645 ConsoleMessage("Failed to setup sound source\n");
646 once = 1;
647 }
648 node->__sourceNumber = BADAUDIOSOURCE;
649 }
650 }
651 if( node->__sourceNumber > -1){
652 int istate;
653 ALfloat SourceVel[3] = { 0.0f, 0.0f, 0.0f };
654 float travelled[3];
655 double traveltime;
656
657 //update position
658 alSourcefv(node->__sourceNumber, AL_POSITION, SourcePos);
659
660 //update velocity for doppler effect
661 vecdif3f(travelled,node->__lastlocation.c,SourcePos);
662 traveltime = TickTime() - node->__lasttime;
663 if(traveltime > 0.0)
664 vecscale3f(SourceVel,travelled,1.0f/(float)traveltime);
665 alSourcefv(node->__sourceNumber, AL_VELOCITY, SourceVel);
666
667 node->__lasttime = TickTime();
668 veccopy3f(node->__lastlocation.c,SourcePos);
669
670 //directional sound - I don't hear directional effects with openAL-Soft
671 //AL_CONE_OUTER_GAIN f the gain when outside the oriented cone
672 //AL_CONE_INNER_ANGLE f, i the gain when inside the oriented cone
673 //AL_CONE_OUTER_ANGLE f, i outer angle of the sound cone, in degrees default is 360
674 if(node->spatialize){
675 double dird[3];
676 ALfloat dirf[3];
677 //transform source direction into avatar/listener space
678 for(i=0;i<3;i++) dird[i] = node->direction.c[i];
679 transformAFFINEd(dird,dird,modelMatrix);
680 for(i=0;i<3;i++) dirf[i] = (float)dird[i];
681 if (1)
682 alSourcefv(node->__sourceNumber, AL_DIRECTION, dirf);
683 else
684 alSource3f(node->__sourceNumber, AL_DIRECTION, dirf[0], dirf[1], dirf[2]);
685 alSourcef(node->__sourceNumber, AL_CONE_OUTER_GAIN, .5f);
686 alSourcef(node->__sourceNumber,AL_CONE_INNER_ANGLE,90.0f);
687 alSourcef(node->__sourceNumber,AL_CONE_OUTER_ANGLE,135.0f);
688 }
689
690 // for routed values going to audioclip, update values
691 alSourcef (node->__sourceNumber, AL_PITCH, acp->pitch);
692 alSourcef (node->__sourceNumber, AL_GAIN, node->intensity );
693 alSourcei (node->__sourceNumber, AL_LOOPING, acp->loop);
694 // update to audioclip start,stop,pause,resume is done in do_AudioTick()
695 if(acp->isPaused) alSourcePause(node->__sourceNumber);
696 //execute audioclip state
697 alGetSourcei(node->__sourceNumber, AL_SOURCE_STATE,&istate);
698 if(acp->isActive ){
699 if(istate != AL_PLAYING && !acp->isPaused){
700 alSourcePlay(node->__sourceNumber);
701 //printf(".play.");
702 }
703 }else{
704 if(istate != AL_STOPPED)
705 alSourceStop(node->__sourceNumber);
706 }
707 }
708 }
709 }
710 }
711
712#endif
713#ifdef HAVE_OLDSOUND //MUST_RE_IMPLEMENT_SOUND_WITH_OPENAL
714
715
716 /* printf ("sound, node %d, acp %d source %d\n",node, acp, acp->__sourceNumber); */
717 /* MovieTextures NOT handled yet*/
718 /* first - is there a node (any node!) attached here?*/
719 if (acp) {
720 GLDOUBLE mod[16];
721 GLDOUBLE proj[16];
722 struct point_XYZ vec, direction, location;
723 double len;
724 double angle;
725 float midmin, midmax;
726 float amp;
727 char mystring[256];
728 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
729
730 /* do the sound registering first, and tell us if this is an audioclip*/
731 /* or movietexture.*/
732
733 render_node(X3D_NODE(acp));
734
735 /* if the attached node is not active, just return*/
736 /* printf ("in Sound, checking AudioClip isactive %d\n", acp->isActive); */
737 if (acp->isActive == 0) return;
738
739 direction.x = node->direction.c[0];
740 direction.y = node->direction.c[1];
741 direction.z = node->direction.c[2];
742
743 location.x = node->location.c[0];
744 location.y = node->location.c[1];
745 location.z = node->location.c[2];
746
747 midmin = (node->minFront - node->minBack) / 2.0;
748 midmax = (node->maxFront - node->maxBack) / 2.0;
749
750
751 FW_GL_PUSH_MATRIX();
752
753 /*
754 first, find whether or not we are within the maximum circle.
755
756 translate to the location, and move the centre point, depending
757 on whether we have a direction and differential maxFront and MaxBack
758 directions.
759 */
760
761 FW_GL_TRANSLATE_F (location.x + midmax*direction.x,
762 location.y + midmax*direction.y,
763 location.z + midmax * direction.z);
764
765 /* make the ellipse a circle by scaling...
766 FW_GL_SCALE_F (direction.x*2.0 + 0.5, direction.y*2.0 + 0.5, direction.z*2.0 + 0.5);
767 - scaling needs work - we need direction information, and parameter work. */
768
769 if ((fabs(node->minFront - node->minBack) > 0.5) ||
770 (fabs(node->maxFront - node->maxBack) > 0.5)) {
771 if (!p->soundWarned) {
772 printf ("FreeWRL:Sound: Warning - minBack and maxBack ignored in this version\n");
773 p->soundWarned = TRUE;
774 }
775 }
776
777
778
779 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
780 FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
781 FW_GLU_UNPROJECT(viewport[2]/2,viewport[3]/2,0.0,
782 mod,proj,viewport, &vec.x,&vec.y,&vec.z);
783 /* printf ("mod %lf %lf %lf proj %lf %lf %lf\n",*/
784 /* mod[12],mod[13],mod[14],proj[12],proj[13],proj[14]);*/
785
786 len = sqrt(VECSQ(vec));
787 /* printf ("len %f\n",len); */
788 /* printf("Sound: len %f mB %f mF %f angles (%f %f %f)\n",len,*/
789 /* -node->maxBack, node->maxFront,vec.x,vec.y,vec.z);*/
790
791
792 /* pan left/right. full left = 0; full right = 1.*/
793 if (len < 0.001) angle = 0;
794 else {
795 if (APPROX (mod[12],0)) {
796 /* printf ("mod12 approaches zero\n");*/
797 mod[12] = 0.001;
798 }
799 angle = fabs(atan2(mod[14],mod[12])) - (PI/2.0);
800 angle = angle/(PI/2.0);
801
802 /* Now, scale this angle to make it between -0.5*/
803 /* and +0.5; if we divide it by 2.0, we will get*/
804 /* this range, but if we divide it by less, then*/
805 /* the sound goes "hard over" to left or right for*/
806 /* a bit.*/
807 angle = angle / 1.5;
808
809 /* now scale to 0 to 1*/
810 angle = angle + 0.5;
811
812 /* bounds check...*/
813 if (angle > 1.0) angle = 1.0;
814 if (angle < 0.0) angle = 0.0;
815 /* printf ("angle: %f\n",angle); */
816 }
817
818
819 amp = 0.0;
820 /* is this within the maxFront maxBack? */
821
822 /* printf ("sound %d len %f maxFront %f\n",acp->__sourceNumber, len, node->maxFront); */
823 /* this code needs rework JAS */
824 if (len < node->maxFront) {
825
826 /* note: using vecs, length is always positive - need to work in direction
827 vector */
828 if (len < 0.0) {
829 if (len < node->minBack) {amp = 1.0;}
830 else {
831 amp = (len - node->maxBack) / (node->maxBack - node->minBack);
832 }
833 } else {
834 if (len < node->minFront) {amp = 1.0;}
835 else {
836 amp = (node->maxFront - len) / (node->maxFront - node->minFront);
837 }
838 }
839
840 /* Now, fit in the intensity. Send along command, with
841 source number, amplitude, balance, and the current Framerate */
842 amp = amp*node->intensity;
843 if (sound_from_audioclip) {
844 sprintf (mystring,"AMPL %d %f %f",acp->__sourceNumber,amp,angle);
845 } else {
846 sprintf (mystring,"MMPL %d %f %f",mcp->__textureTableIndex, amp, angle); //__sourceNumber,amp,angle);
847 }
848 Sound_toserver(mystring);
849 }
850 FW_GL_POP_MATRIX();
851 }
852#endif /* MUST_RE_IMPLEMENT_SOUND_WITH_OPENAL */
853}
854
855
856int parse_audioclip(struct X3D_AudioClip *node,char *bbuffer, int len){
857#ifdef HAVE_OPENAL
858 ALint buffer = AL_NONE;
859#ifdef HAVE_ALUT
860 buffer = alutCreateBufferFromFileImage (bbuffer, len);
861//#elif HAVE_SDL
862#endif
863 if (buffer == AL_NONE)
864 buffer = BADAUDIOSOURCE;
865#else
866 int buffer = BADAUDIOSOURCE;
867#endif
868 //printf("parse_audioclip buffer=%d\n",buffer);
869 return buffer;
870}
871
872double compute_duration(int ibuffer){
873
874 double retval = 1.0;
875#ifdef HAVE_OPENAL
876 int ibytes;
877 int ibits;
878 int ichannels;
879 int ifreq;
880 double framesizebytes, bytespersecond;
881 alGetBufferi(ibuffer,AL_FREQUENCY,&ifreq);
882 alGetBufferi(ibuffer,AL_BITS,&ibits);
883 alGetBufferi(ibuffer,AL_CHANNELS,&ichannels);
884 alGetBufferi(ibuffer,AL_SIZE,&ibytes);
885 framesizebytes = (double)(ibits * ichannels)/8.0;
886 bytespersecond = framesizebytes * (double)ifreq;
887 if(bytespersecond > 0.0)
888 retval = (double)(ibytes) / bytespersecond;
889 else
890 retval = 1.0;
891#endif
892#ifdef HAVE_OLDSOUND
893 //not sure how this is supposed to work, havent compiled it, good luck
894 float pitch;
895 double stime, sttime;
896 int loop;
897 pitch = node->pitch;
898 stime = node->startTime;
899 sttime = node->stopTime;
900 loop = node->loop;
901
902 retval = SoundSourceInit (ibuffer, node->loop,
903 (double) pitch,(double) stime, (double) sttime, filename);
904
905#endif
906 return retval;
907}
908bool process_res_audio(resource_item_t *res){
909 //s_list_t *l;
910 openned_file_t *of;
911 //struct Shader_Script* ss;
912 char *buffer;
913 int len;
914 struct X3D_AudioClip *node;
915
916 buffer = NULL;
917 len = 0;
918 switch (res->type) {
919 case rest_invalid:
920 return FALSE;
921 break;
922
923 case rest_string:
924 buffer = res->URLrequest;
925 break;
926 case rest_url:
927 case rest_file:
928 case rest_multi:
929 //l = (s_list_t *) res->openned_files;
930 //if (!l) {
931 // /* error */
932 // return FALSE;
933 //}
934
935 //of = ml_elem(l);
936 of = res->openned_files;
937 if (!of) {
938 /* error */
939 return FALSE;
940 }
941
942 buffer = of->fileData;
943 len = of->fileDataSize;
944 break;
945 }
946
947 node = (struct X3D_AudioClip *) res->whereToPlaceData;
948 //node->__FILEBLOB = buffer;
949 node->__sourceNumber = parse_audioclip(node,buffer,len); //__sourceNumber will be openAL buffer number
950 if(node->__sourceNumber > -1) {
951 node->duration_changed = compute_duration(node->__sourceNumber);
952 MARK_EVENT (X3D_NODE(node), offsetof(struct X3D_AudioClip, duration_changed));
953 return TRUE;
954 }
955 return FALSE;
956}
957
958
959/* returns the audio duration, unscaled by pitch */
960double return_Duration (struct X3D_AudioClip *node) {
961 double retval;
962 int indx;
963 indx = node->__sourceNumber;
964 if (indx < 0) retval = 1.0;
965 else if (indx > 50) retval = 1.0;
966 else
967 {
968#ifdef HAVE_OPENAL
969 retval = node->duration_changed;
970#else
971 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
972 retval = p->AC_LastDuration[indx];
973#endif
974 }
975 return retval;
976}