FreeWRL/FreeX3D  3.0.0
Component_Sound.c
1 /*
2 
3 
4 X3D 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. */
48 void* 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. */
77 void 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 
97 typedef 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];
107 void *Component_Sound_constructor(){
108  void *v = MALLOCV(sizeof(struct pComponent_Sound));
109  memset(v,0,sizeof(struct pComponent_Sound));
110  return v;
111 }
112 void 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;
139 void Sound_toserver(char *message)
140 {}
141 
142 // Position of the listener.
143 float ListenerPos[] = { 0.0, 0.0, 0.0 };
144 // Velocity of the listener.
145 float ListenerVel[] = { 0.0, 0.0, 0.0 };
146 // Orientation of the listener. (first 3 elements are "at", second 3 are "up")
147 float ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
148 
149 int 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 
198 void waitformessage(void)
199 {}
200 
201 void SoundEngineDestroy(void)
202 {}
203 
204 int SoundSourceRegistered(int num)
205 {
206  if(num > -1) return TRUE;
207  return FALSE;
208 }
209 
210 float SoundSourceInit(int num, int loop, double pitch, double start_time, double stop_time, char *url)
211 {return 0.0f;}
212 
213 void SetAudioActive(int num, int stat)
214 {}
215 
216 
217 int 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
230 OLDCODEvoid render_AudioControl (struct X3D_AudioControl *node) {
231 OLDCODE GLDOUBLE mod[16];
232 OLDCODE GLDOUBLE proj[16];
233 OLDCODE struct point_XYZ vec, direction, location;
234 OLDCODE double len;
235 OLDCODE double angle;
236 OLDCODE float midmin, midmax;
237 OLDCODE
238 OLDCODE /* do the sound registering first, and tell us if this is an audioclip*/
239 OLDCODE /* or movietexture.*/
240 OLDCODE
241 OLDCODE
242 OLDCODE /* if not enabled, do nothing */
243 OLDCODE if (!node) return;
244 OLDCODE if (node->__oldEnabled != node->enabled) {
245 OLDCODE node->__oldEnabled = node->enabled;
246 OLDCODE MARK_EVENT(X3D_NODE(node),offsetof (struct X3D_AudioControl, enabled));
247 OLDCODE }
248 OLDCODE if (!node->enabled) return;
249 OLDCODE
250 OLDCODE direction.x = node->direction.c[0];
251 OLDCODE direction.y = node->direction.c[1];
252 OLDCODE direction.z = node->direction.c[2];
253 OLDCODE
254 OLDCODE location.x = node->location.c[0];
255 OLDCODE location.y = node->location.c[1];
256 OLDCODE location.z = node->location.c[2];
257 OLDCODE
258 OLDCODE midmin = (node->minFront - node->minBack) / (float) 2.0;
259 OLDCODE midmax = (node->maxFront - node->maxBack) / (float) 2.0;
260 OLDCODE
261 OLDCODE
262 OLDCODE FW_GL_PUSH_MATRIX();
263 OLDCODE
264 OLDCODE /*
265 OLDCODE first, find whether or not we are within the maximum circle.
266 OLDCODE
267 OLDCODE translate to the location, and move the centre point, depending
268 OLDCODE on whether we have a direction and differential maxFront and MaxBack
269 OLDCODE directions.
270 OLDCODE */
271 OLDCODE
272 OLDCODE FW_GL_TRANSLATE_D (location.x + midmax*direction.x,
273 OLDCODE location.y + midmax*direction.y,
274 OLDCODE location.z + midmax * direction.z);
275 OLDCODE
276 OLDCODE /* make the ellipse a circle by scaling...
277 OLDCODE FW_GL_SCALE_F (direction.x*2.0 + 0.5, direction.y*2.0 + 0.5, direction.z*2.0 + 0.5);
278 OLDCODE - scaling needs work - we need direction information, and parameter work. */
279 OLDCODE
280 OLDCODE if ((fabs(node->minFront - node->minBack) > 0.5) ||
281 OLDCODE (fabs(node->maxFront - node->maxBack) > 0.5)) {
282 OLDCODE if (!soundWarned) {
283 OLDCODE printf ("FreeWRL:Sound: Warning - minBack and maxBack ignored in this version\n");
284 OLDCODE soundWarned = TRUE;
285 OLDCODE }
286 OLDCODE }
287 OLDCODE
288 OLDCODE
289 OLDCODE
290 OLDCODE FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, mod);
291 OLDCODE FW_GL_GETDOUBLEV(GL_PROJECTION_MATRIX, proj);
292 OLDCODE FW_GLU_UNPROJECT(viewport[2]/2,viewport[3]/2,0.0,
293 OLDCODE mod,proj,viewport, &vec.x,&vec.y,&vec.z);
294 OLDCODE /* printf ("mod %lf %lf %lf proj %lf %lf %lf\n",*/
295 OLDCODE /* mod[12],mod[13],mod[14],proj[12],proj[13],proj[14]);*/
296 OLDCODE
297 OLDCODE len = sqrt(VECSQ(vec));
298 OLDCODE /* printf ("len %f\n",len); */
299 OLDCODE /* printf("Sound: len %f mB %f mF %f angles (%f %f %f)\n",len,*/
300 OLDCODE /* -node->maxBack, node->maxFront,vec.x,vec.y,vec.z);*/
301 OLDCODE
302 OLDCODE
303 OLDCODE /* pan left/right. full left = 0; full right = 1.*/
304 OLDCODE if (len < 0.001) angle = 0;
305 OLDCODE else {
306 OLDCODE if (APPROX (mod[12],0)) {
307 OLDCODE /* printf ("mod12 approaches zero\n");*/
308 OLDCODE mod[12] = 0.001;
309 OLDCODE }
310 OLDCODE angle = fabs(atan2(mod[14],mod[12])) - (PI/2.0);
311 OLDCODE angle = angle/(PI/2.0);
312 OLDCODE
313 OLDCODE /* Now, scale this angle to make it between -0.5*/
314 OLDCODE /* and +0.5; if we divide it by 2.0, we will get*/
315 OLDCODE /* this range, but if we divide it by less, then*/
316 OLDCODE /* the sound goes "hard over" to left or right for*/
317 OLDCODE /* a bit.*/
318 OLDCODE angle = angle / 1.5;
319 OLDCODE
320 OLDCODE /* now scale to 0 to 1*/
321 OLDCODE angle = angle + 0.5;
322 OLDCODE
323 OLDCODE /* and, "reverse" the value, so that left is left, and right is right */
324 OLDCODE angle = 1.0 - angle;
325 OLDCODE
326 OLDCODE /* bounds check...*/
327 OLDCODE if (angle > 1.0) angle = 1.0;
328 OLDCODE if (angle < 0.0) angle = 0.0;
329 OLDCODE
330 OLDCODE #ifdef SOUNDVERBOSE
331 OLDCODE printf ("angle: %f\n",angle);
332 OLDCODE #endif
333 OLDCODE }
334 OLDCODE
335 OLDCODE /* convert to a MIDI control value */
336 OLDCODE node->panFloatVal = (float) angle;
337 OLDCODE node->panInt32Val = (int) (angle * 128);
338 OLDCODE if (node->panInt32Val < 0) node->panInt32Val = 0; if (node->panInt32Val > 127) node->panInt32Val = 127;
339 OLDCODE
340 OLDCODE
341 OLDCODE node->volumeFloatVal = (float) 0.0;
342 OLDCODE /* is this within the maxFront maxBack? */
343 OLDCODE
344 OLDCODE /* this code needs rework JAS */
345 OLDCODE if (len < node->maxFront) {
346 OLDCODE /* did this node become active? */
347 OLDCODE if (!node->isActive) {
348 OLDCODE node->isActive = TRUE;
349 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, isActive));
350 OLDCODE #ifdef SOUNDVERBOSE
351 OLDCODE printf ("AudioControl node is now ACTIVE\n");
352 OLDCODE #endif
353 OLDCODE
354 OLDCODE
355 OLDCODE /* record the length for doppler shift comparisons */
356 OLDCODE node->__oldLen = len;
357 OLDCODE }
358 OLDCODE
359 OLDCODE /* note: using vecs, length is always positive - need to work in direction
360 OLDCODE vector */
361 OLDCODE if (len < 0.0) {
362 OLDCODE if (len < node->minBack) {node->volumeFloatVal = (float) 1.0;}
363 OLDCODE else { node->volumeFloatVal = ((float) len - node->maxBack) / (node->maxBack - node->minBack); }
364 OLDCODE } else {
365 OLDCODE if (len < node->minFront) {node->volumeFloatVal = (float) 1.0;}
366 OLDCODE else { node->volumeFloatVal = (node->maxFront - (float) len) / (node->maxFront - node->minFront); }
367 OLDCODE }
368 OLDCODE
369 OLDCODE /* work out the delta for len */
370 OLDCODE if (APPROX(node->maxDelta, 0.0)) {
371 OLDCODE printf ("AudioControl: maxDelta approaches zero!\n");
372 OLDCODE node->deltaFloatVal = (float) 0.0;
373 OLDCODE } else {
374 OLDCODE #ifdef SOUNDVERBOSE
375 OLDCODE printf ("maxM/S %f \n",(node->__oldLen - len)/ (TickTime()- lastTime));
376 OLDCODE #endif
377 OLDCODE
378 OLDCODE /* calculate change as Metres/second */
379 OLDCODE
380 OLDCODE /* compute node->deltaFloatVal, and clamp to range of -1.0 to 1.0 */
381 OLDCODE node->deltaFloatVal = (float) ((node->__oldLen - len)/(TickTime()-lastTime()))/node->maxDelta;
382 OLDCODE if (node->deltaFloatVal < (float) -1.0) node->deltaFloatVal = (float) -1.0; if (node->deltaFloatVal > (float) 1.0) node->deltaFloatVal = (float) 1.0;
383 OLDCODE node->__oldLen = len;
384 OLDCODE }
385 OLDCODE
386 OLDCODE /* Now, fit in the intensity. Send along command, with
387 OLDCODE source number, amplitude, balance, and the current Framerate */
388 OLDCODE node->volumeFloatVal = node->volumeFloatVal*node->intensity;
389 OLDCODE node->volumeInt32Val = (int) (node->volumeFloatVal * 128.0);
390 OLDCODE if (node->volumeInt32Val < 0) node->volumeInt32Val = 0; if (node->volumeInt32Val > 127) node->volumeInt32Val = 127;
391 OLDCODE
392 OLDCODE node->deltaInt32Val = (int) (node->deltaFloatVal * 64.0) + 64;
393 OLDCODE if (node->deltaInt32Val < 0) node->deltaInt32Val = 0; if (node->deltaInt32Val > 127) node->deltaInt32Val = 127;
394 OLDCODE
395 OLDCODE #ifdef SOUNDVERBOSE
396 OLDCODE printf ("AudioControl: amp: %f (%d) angle: %f (%d) delta: %f (%d)\n",node->volumeFloatVal,node->volumeInt32Val,
397 OLDCODE node->panFloatVal, node->panInt32Val ,node->deltaFloatVal,node->deltaInt32Val);
398 OLDCODE #endif
399 OLDCODE
400 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, volumeInt32Val));
401 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, volumeFloatVal));
402 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, panInt32Val));
403 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, panFloatVal));
404 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, deltaInt32Val));
405 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, deltaFloatVal));
406 OLDCODE
407 OLDCODE } else {
408 OLDCODE /* node just became inActive */
409 OLDCODE if (node->isActive) {
410 OLDCODE node->isActive = FALSE;
411 OLDCODE MARK_EVENT (X3D_NODE(node), offsetof (struct X3D_AudioControl, isActive));
412 OLDCODE #ifdef SOUNDVERBOSE
413 OLDCODE printf ("AudioControl node is now INACTIVE\n");
414 OLDCODE #endif
415 OLDCODE }
416 OLDCODE }
417 OLDCODE
418 OLDCODE FW_GL_POP_MATRIX();
419 OLDCODE}
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 
428 void 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 }
490 int 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 }
500 void 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 
558 void 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, &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 
856 int 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 
872 double 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 }
908 bool process_res_audio(resource_item_t *res){
909  //s_list_t *l;
910  openned_file_t *of;
911  //struct Shader_Script* ss;
912  const 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 */
960 double 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 }