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
43#define BADAUDIOSOURCE -9999
44//#undef HAVE_LIBSOUND
45#ifdef HAVE_LIBSOUND
46#undef HAVE_OPENAL
47#endif //HAVE_LIBSOUND
48#ifdef HAVE_OPENAL
49#include <AL/al.h>
50#include <AL/alc.h>
51#include <AL/alext.h>
52#ifdef HAVE_ALUT
53#include <AL/alut.h>
54#endif //HAVE_ALUT
55
56typedef struct ivec2 {int X; int Y;} ivec2;
57typedef struct icset { int p; int d; int ld; int n; int s; int ls; } icset;
58
59/* InitAL opens the default device and sets up a context using default
60 * attributes, making the program ready to call OpenAL functions. */
61void* fwInitAL(void)
62{
63 ALCdevice *device;
64 ALCcontext *ctx;
65
66 /* Open and initialize a device with default settings */
67 device = alcOpenDevice(NULL);
68 if(!device)
69 {
70 fprintf(stderr, "Could not open a device!\n");
71 return NULL;
72 }
73
74 ctx = alcCreateContext(device, NULL);
75 if(ctx == NULL || alcMakeContextCurrent(ctx) == ALC_FALSE)
76 {
77 if(ctx != NULL)
78 alcDestroyContext(ctx);
79 alcCloseDevice(device);
80 fprintf(stderr, "Could not set a context!\n");
81 return NULL;
82 }
83
84 printf("Opened \"%s\"\n", alcGetString(device, ALC_DEVICE_SPECIFIER));
85 return ctx;
86}
87
88/* CloseAL closes the device belonging to the current context, and destroys the
89 * context. */
90void fwCloseAL(void *alctx)
91{
92 ALCdevice *device;
93 ALCcontext *ctx;
94
95 //ctx = alcGetCurrentContext();
96 ctx = alctx;
97 if(ctx == NULL)
98 return;
99
100 device = alcGetContextsDevice(ctx);
101
102 alcMakeContextCurrent(NULL);
103 alcDestroyContext(ctx);
104 alcCloseDevice(device);
105}
106
107#endif
108#ifdef HAVE_LIBSOUND
109#include "../../libsound/libsound.h"
110//libsound is our /src/libsound C wrapper lib over
111// LabSound https://github.com/LabSound/LabSound
112#endif //HAVE_LIBSOUND
113
114typedef struct pComponent_Sound{
115#ifdef HAVE_OPENAL
116 void *alContext;
117#endif //HAVE_OPENAL
118 Stack *audio_context_stack;
119 Stack *audio_parent_stack;
120 Stack* doppler_factor_stack;
121 Stack* splitter_source_stack;
122}* ppComponent_Sound;
123void *Component_Sound_constructor(){
124 void *v = MALLOCV(sizeof(struct pComponent_Sound));
125 memset(v,0,sizeof(struct pComponent_Sound));
126 return v;
127}
128void Component_Sound_init(struct tComponent_Sound *t){
129 //public
130 /* Sounds can come from AudioClip nodes, or from MovieTexture nodes. Different
131 structures on these */
132 t->sound_from_audioclip= 0;
133
134 /* is the sound engine started yet? */
135 t->SoundEngineStarted = FALSE;
136 //private
137 t->prv = Component_Sound_constructor();
138 {
139 ppComponent_Sound p = (ppComponent_Sound)t->prv;
140 /* for printing warnings about Sound node problems - only print once per invocation */
141 p->audio_context_stack = newStack(int);
142 stack_push(int, p->audio_context_stack, 0); //a null will signal we have no audio context yet.
143 p->audio_parent_stack = newStack(icset);
144 icset aps = { 0, 0, 0, 0, 0, 0 };
145 stack_push(icset, p->audio_parent_stack, aps); //a null will signal we have no audio parent yet.
146 p->doppler_factor_stack = newStack(float);
147 stack_push(float, p->doppler_factor_stack, 1.0f);
148 p->splitter_source_stack = newStack(ivec2);
149 ivec2 sss = { 0, 0 };
150 stack_push(ivec2, p->splitter_source_stack, sss);
151#ifdef HAVE_OPENAL
152 p->alContext = NULL;
153#endif //HAVE_OPENAL
154
155 }
156}
157void Component_Sound_clear(struct tComponent_Sound *t){
158 ppComponent_Sound p = (ppComponent_Sound)t->prv;
159 deleteVector(struct X3D_Node*,p->audio_context_stack);
160}
161//ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
162
163// Position of the listener.
164float ListenerPos[] = { 0.0, 0.0, 0.0 };
165// Velocity of the listener.
166float ListenerVel[] = { 0.0, 0.0, 0.0 };
167// Orientation of the listener. (first 3 elements are "at", second 3 are "up")
168float ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };
169
170int SoundEngineInit(void)
171{
172 int retval = FALSE;
173#ifdef HAVE_OPENAL
174 {
175 void *alctx;
176 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
177 retval = TRUE;
178 /* Initialize OpenAL with the default device, and check for EFX support. */
179 alctx = fwInitAL();
180 if(!alctx ){
181 ConsoleMessage("initAL failed\n");
182 retval = FALSE;
183 }
184 p->alContext = alctx;
185#ifdef HAVE_ALUT
186 if(!alutInitWithoutContext(NULL,NULL)) //this does not create an AL context (simple)
187 {
188 ALenum error = alutGetError ();
189 ConsoleMessage("%s\n", alutGetErrorString (error));
190 retval = FALSE;
191 }
192#endif //HAVE_ALUT
193
194 //listener is avatar,
195 //we could move both listener and sources in world coordinates
196 //instead we'll work in avatar/local coordinates and
197 //freeze listener at 0,0,0 and update the position of the sound sources
198 //relative to listener, on each frame
199 alListenerfv(AL_POSITION, ListenerPos);
200 alListenerfv(AL_VELOCITY, ListenerVel);
201 alListenerfv(AL_ORIENTATION, ListenerOri);
202 if(1){
203 //ALenum error;
204 if(FALSE) //meters)
205 alSpeedOfSound(345.0f); //alDopplerVelocity(34.0f); //m/s
206 else //feet
207 alSpeedOfSound(1132.0f); //alDopplerVelocity(1132.0f); // using feet/second – change propagation velocity
208 alDopplerFactor(1.0f); // exaggerate pitch shift by 20%
209 //if ((error = alGetError()) != AL_NO_ERROR) DisplayALError("alDopplerX : ", error);
210 }
211 alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED); //here's what I think web3d wants
212 //alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); //seems a bit faint
213 }
214#endif //HAVE_OPENAL
215#ifdef HAVE_LIBSOUND
216 // labsound does have some resources --HRDF table and a few more things I think-- which would be 'installed'
217 // and would need to be loaded at some point from a known path. Maybe loaded here.
218 retval = TRUE;
219#endif //HAVE_LIBSOUND
220 gglobal()->Component_Sound.SoundEngineStarted = retval;
221 return retval;
222}
223
224int haveSoundEngine(){
225 ttglobal tg = gglobal();
226
227 if (!tg->Component_Sound.SoundEngineStarted) {
228 #ifdef SEVERBOSE
229 printf ("SetAudioActive: initializing SoundEngine\n");
230 #endif
231 tg->Component_Sound.SoundEngineStarted = SoundEngineInit();
232 }
233 return tg->Component_Sound.SoundEngineStarted;
234}
235
236
237#define LOAD_INITIAL_STATE 0
238#define LOAD_REQUEST_RESOURCE 1
239#define LOAD_FETCHING_RESOURCE 2
240//#define LOAD_PARSING 3
241#define LOAD_STABLE 10
242
243void locateAudioSource (struct X3D_AudioBuffer *node) {
244 resource_item_t *res;
245 //resource_item_t *parentPath;
246 //ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
247 int debug = 1;
248 if(debug) printf("\nurl %s\n", node->url.p[0]->strptr);
249 switch (node->__loadstatus) {
250 case LOAD_INITIAL_STATE: /* nothing happened yet */
251
252 if (node->url.n == 0) {
253 node->__loadstatus = LOAD_STABLE; /* a "do-nothing" approach */
254 break;
255 } else {
256 res = resource_create_multi(&(node->url));
257 if(node->_nodeType == NODE_MovieTexture)
258 res->media_type = resm_movie;
259 else //if(node->_nodeType == NODE_AudioClip)
260 res->media_type = resm_audio;
261 node->__loadstatus = LOAD_REQUEST_RESOURCE;
262 node->__loadResource = res;
263 }
264 if(debug) printf("1");
265 break;
266
267 case LOAD_REQUEST_RESOURCE:
268 res = node->__loadResource;
269 resource_identify(node->_parentResource, res);
270 res->actions = resa_download | resa_load; //not resa_parse which we do below
271 res->ectx = (void*)node->_executionContext;
272 res->whereToPlaceData = X3D_NODE(node);
273 //res->offsetFromWhereToPlaceData = offsetof (struct X3D_AudioClip, __FILEBLOB);
274 resitem_enqueue(ml_new(res));
275 node->__loadstatus = LOAD_FETCHING_RESOURCE;
276 if(debug) printf("2");
277 break;
278
279 case LOAD_FETCHING_RESOURCE:
280 res = node->__loadResource;
281 /* printf ("load_Inline, we have type %s status %s\n",
282 resourceTypeToString(res->type), resourceStatusToString(res->status)); */
283 if(res->complete){
284 if (res->status == ress_loaded) {
285 if (res->actions != resa_process) {
286 res->actions = resa_process;
287 res->complete = FALSE;
288 resitem_enqueue(ml_new(res));
289 }
290 } else if ((res->status == ress_failed) || (res->status == ress_invalid)) {
291 //no hope left
292 printf ("resource failed to load\n");
293 for(int ii=0;ii<node->url.n;ii++)
294 printf ("-- url[%d]=%s\n",ii,node->url.p[ii]->strptr);
295 node->__loadstatus = LOAD_STABLE; // a "do-nothing" approach
296 node->__sourceNumber = BADAUDIOSOURCE;
297 } else if (res->status == ress_parsed) {
298 node->__loadstatus = LOAD_STABLE;
299 } //if (res->status == ress_parsed)
300 } //if(res->complete)
301 //end case LOAD_FETCHING_RESOURCE
302 if(debug) printf("3");
303 break;
304
305 case LOAD_STABLE:
306 if(debug) printf("4");
307 break;
308 }
309}
310int loadstatus_AudioClip(struct X3D_AudioClip *node){
311 int istate = 0;
312 if(node){
313 if(node->__loadstatus > LOAD_INITIAL_STATE && node->__loadstatus < LOAD_STABLE)
314 istate = 1;
315 if(node->__loadstatus == LOAD_STABLE)
316 istate = 2;
317 }
318 return istate;
319}
320//void compile_AudioClip(struct X3D_AudioClip* node) {
321//
322//}
323
324
325
326
327int parse_audioclip(struct X3D_AudioClip* node, char* bbuffer, int len, char* url) {
328#ifdef HAVE_OPENAL
329 ALint buffer = AL_NONE;
330#ifdef HAVE_ALUT
331 buffer = alutCreateBufferFromFileImage(bbuffer, len);
332 //#elif HAVE_SDL
333#endif
334 if (buffer == AL_NONE)
335 buffer = BADAUDIOSOURCE;
336#elif HAVE_LIBSOUND
337 int buffer;
338 //if(0)
339 buffer = libsound_createBusFromBuffer0(bbuffer, len);
340 //else
341 // buffer = libsound_createBusFromFile0(url);
342#else
343 int buffer = BADAUDIOSOURCE;
344#endif
345 //printf("parse_audioclip buffer=%d\n",buffer);
346 return buffer;
347}
348
349double compute_duration(int ibuffer) {
350
351 double retval = 1.0;
352#ifdef HAVE_OPENAL
353 int ibytes;
354 int ibits;
355 int ichannels;
356 int ifreq;
357 double framesizebytes, bytespersecond;
358 alGetBufferi(ibuffer, AL_FREQUENCY, &ifreq);
359 alGetBufferi(ibuffer, AL_BITS, &ibits);
360 alGetBufferi(ibuffer, AL_CHANNELS, &ichannels);
361 alGetBufferi(ibuffer, AL_SIZE, &ibytes);
362 framesizebytes = (double)(ibits * ichannels) / 8.0;
363 bytespersecond = framesizebytes * (double)ifreq;
364 if (bytespersecond > 0.0)
365 retval = (double)(ibytes) / bytespersecond;
366 else
367 retval = 1.0;
368#endif //HAVE_OPENAL
369#ifdef HAVE_LIBSOUND
370 retval = libsound_computeDuration0(ibuffer);
371#endif //HAVE_LIBSOUND
372 return retval;
373}
374bool process_res_audio(resource_item_t* res) {
375 //s_list_t *l;
376 openned_file_t* of;
377 //struct Shader_Script* ss;
378 char* buffer;
379 int len;
380 struct X3D_AudioClip* node;
381
382 buffer = NULL;
383 len = 0;
384 switch (res->type) {
385 case rest_invalid:
386 return FALSE;
387 break;
388
389 case rest_string:
390 buffer = res->URLrequest;
391 break;
392 case rest_url:
393 case rest_file:
394 case rest_multi:
395 //l = (s_list_t *) res->openned_files;
396 //if (!l) {
397 // /* error */
398 // return FALSE;
399 //}
400
401 //of = ml_elem(l);
402 of = res->openned_files;
403 if (!of) {
404 /* error */
405 return FALSE;
406 }
407
408 buffer = of->fileData;
409 len = of->fileDataSize;
410 break;
411 }
412
413 node = (struct X3D_AudioClip*)res->whereToPlaceData;
414 //node->__FILEBLOB = buffer;
415 node->__sourceNumber = parse_audioclip(node, buffer, len, res->actual_file); //__sourceNumber will be openAL buffer number
416 if (node->__sourceNumber > -1 ) {
417 if (node->_nodeType == NODE_AudioClip) {
418 node->duration_changed = compute_duration(node->__sourceNumber);
419 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_AudioClip, duration_changed));
420 }
421 return TRUE;
422 }
423 return FALSE;
424}
425
426
427/* returns the audio duration, unscaled by pitch */
428double return_Duration(struct X3D_AudioClip* node) {
429 double retval;
430 int indx;
431 indx = node->__sourceNumber;
432 if (indx < 0) retval = 1.0;
433 else if (indx > 50) retval = 1.0;
434 else
435 {
436#if defined(HAVE_OPENAL) || defined(HAVE_LIBSOUND)
437 retval = node->duration_changed;
438#endif
439 }
440 return retval;
441}
442
443
444
445
446#ifdef HAVE_OPENAL
447
448void render_AudioClip(struct X3D_AudioClip* node) {
449 /* audio clip is a flat sound -no 3D- and a sound node (3D) refers to it
450 specs: if an audioclip can't be reached in the scenegraph, then it doesn't play
451 */
452
453 /* is this audio wavelet initialized yet? */
454 if (node->__loadstatus != LOAD_STABLE) {
455 locateAudioSource(node);
456 }
457 if (node->__loadstatus != LOAD_STABLE) return;
458 /* is this audio ok? if so, the sourceNumber will range
459 * between 0 and infinity; if it is BADAUDIOSOURCE, bad source.
460 * check out locateAudioSource to find out reasons */
461 if (node->__sourceNumber == BADAUDIOSOURCE) return;
462
463}
464
465void render_Sound (struct X3D_Sound *node) {
466/* updates the position and velocity vector of the sound source relative to the listener/avatar
467 so 3D sound effects can be rendered: distance attenuation, stereo left/right volume balance,
468 and doppler (pitch) effect
469 - refers to sound source ie audioclip or movie
470 - an audioclip may be DEFed and USEd in multiple Sounds, but will be playing the same tune at the same time
471*/
472 int sound_from_audioclip;
473
474 struct X3D_AudioClip *acp = NULL;
475 struct X3D_MovieTexture *mcp = NULL;
476 struct X3D_Node *tmpN = NULL;
477 //ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
478
479 /* why bother doing this if there is no source? */
480 if (node->source == NULL)
481 return;
482
483 /* ok, is the source a valid node?? */
484
485 /* might be a PROTO expansion, as in what Adam Nash does... */
486 POSSIBLE_PROTO_EXPANSION(struct X3D_Node *, node->source,tmpN)
487
488 /* did not find a valid source node, even after really looking at a PROTO def */
489 if (tmpN == NULL) return;
490
491 sound_from_audioclip = FALSE;
492 if (tmpN->_nodeType == NODE_AudioClip) {
493 acp = (struct X3D_AudioClip *) tmpN;
494 sound_from_audioclip = TRUE;
495 }else if (tmpN->_nodeType == NODE_MovieTexture){
496 //mcp = (struct X3D_MovieTexture *) tmpN;
497 //july 2016 ordered fields in MovieTexture to mach AudioClip, can up-caste
498 acp = (struct X3D_AudioClip *) tmpN;
499 } else {
500 ConsoleMessage ("Sound node- source type of %s invalid",stringNodeType(tmpN->_nodeType));
501 node->source = NULL; /* stop messages from scrolling forever */
502 return;
503 }
504
505
506 /* 4 sources of openAL explanations and examples:
507 - http://open-activewrl.sourceforge.net/data/OpenAL_PGuide.pdf
508 - http://forum.devmaster.net/t and type 'openal' in the search box to get several lessons on openal
509 - http://kcat.strangesoft.net/openal.html example code (win32 desktop is using this openal-soft implementation of openal)
510 - http://en.wikipedia.org/wiki/OpenAL links
511 - <al.h> comments
512 */
513 if(acp){
514 if(haveSoundEngine()){
515 if( acp->__sourceNumber < 0){
516 render_AudioClip(acp);
517 }
518 if( acp->__sourceNumber > -1 ){
519 //have a buffer loaded
520 int i;
521 GLDOUBLE modelMatrix[16];
522 GLDOUBLE SourcePosd[3] = { 0.0f, 0.0f, 0.0f };
523 ALfloat SourcePos[3];
524
525 //transform source local coordinate 0,0,0 location into avatar/listener space
526 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelMatrix);
527 transformAFFINEd(SourcePosd,SourcePosd,modelMatrix);
528 for(i=0;i<3;i++) SourcePos[i] = (ALfloat)SourcePosd[i];
529
530 if( node->__sourceNumber < 0){
531 //convert buffer to openAL sound source
532 ALint source;
533 source = 0;
534 alGenSources(1, (ALuint *)&source);
535 alSourcei(source, AL_BUFFER, acp->__sourceNumber);
536 alSourcef (source, AL_PITCH, acp->pitch);
537 alSourcef (source, AL_GAIN, node->intensity );
538 alSourcei (source, AL_LOOPING, acp->loop);
539 alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE); //we'll treat the avatar/listener as fixed, and the sources moving relative
540 //openAL will automatically mix multiple sources for one listener, there's no need for .priority hint
541 alSourcef (source, AL_MAX_DISTANCE, node->maxFront);
542 //no attempt is made to implement minBack, maxBack ellipsoidal as in web3d specs
543 //- just a spherical sound, and with spatialize attempt at a cone
544 node->__lasttime = TickTime();
545 veccopy3f(node->__lastlocation.c,SourcePos);
546
547 node->__sourceNumber = source;
548 //assert(alGetError()==AL_NO_ERROR && "Failed to setup sound source");
549 if(alGetError()!=AL_NO_ERROR) {
550 static int once = 0;
551 if(!once){
552 ConsoleMessage("Failed to setup sound source\n");
553 once = 1;
554 }
555 node->__sourceNumber = BADAUDIOSOURCE;
556 }
557 }
558 if( node->__sourceNumber > -1){
559 int istate;
560 ALfloat SourceVel[3] = { 0.0f, 0.0f, 0.0f };
561 float travelled[3];
562 double traveltime;
563
564 //update position
565 alSourcefv(node->__sourceNumber, AL_POSITION, SourcePos);
566
567 //update velocity for doppler effect
568 vecdif3f(travelled,node->__lastlocation.c,SourcePos);
569 traveltime = TickTime() - node->__lasttime;
570 if(traveltime > 0.0)
571 vecscale3f(SourceVel,travelled,1.0f/(float)traveltime);
572 alSourcefv(node->__sourceNumber, AL_VELOCITY, SourceVel);
573
574 node->__lasttime = TickTime();
575 veccopy3f(node->__lastlocation.c,SourcePos);
576
577 //directional sound - I don't hear directional effects with openAL-Soft
578 //AL_CONE_OUTER_GAIN f the gain when outside the oriented cone
579 //AL_CONE_INNER_ANGLE f, i the gain when inside the oriented cone
580 //AL_CONE_OUTER_ANGLE f, i outer angle of the sound cone, in degrees default is 360
581 if(node->spatialize){
582 double dird[3];
583 ALfloat dirf[3];
584 //transform source direction into avatar/listener space
585 for(i=0;i<3;i++) dird[i] = node->direction.c[i];
586 transformAFFINEd(dird,dird,modelMatrix);
587 for(i=0;i<3;i++) dirf[i] = (float)dird[i];
588 if (1)
589 alSourcefv(node->__sourceNumber, AL_DIRECTION, dirf);
590 else
591 alSource3f(node->__sourceNumber, AL_DIRECTION, dirf[0], dirf[1], dirf[2]);
592 alSourcef(node->__sourceNumber, AL_CONE_OUTER_GAIN, .5f);
593 alSourcef(node->__sourceNumber,AL_CONE_INNER_ANGLE,90.0f);
594 alSourcef(node->__sourceNumber,AL_CONE_OUTER_ANGLE,135.0f);
595 }
596
597 // for routed values going to audioclip, update values
598 alSourcef (node->__sourceNumber, AL_PITCH, acp->pitch);
599 alSourcef (node->__sourceNumber, AL_GAIN, node->intensity );
600 alSourcei (node->__sourceNumber, AL_LOOPING, acp->loop);
601 // update to audioclip start,stop,pause,resume is done in do_AudioTick()
602 if(acp->isPaused) alSourcePause(node->__sourceNumber);
603 //execute audioclip state
604 alGetSourcei(node->__sourceNumber, AL_SOURCE_STATE,&istate);
605 if(acp->isActive ){
606 if(istate != AL_PLAYING && !acp->isPaused){
607 alSourcePlay(node->__sourceNumber);
608 //printf(".play.");
609 }
610 }else{
611 if(istate != AL_STOPPED)
612 alSourceStop(node->__sourceNumber);
613 }
614 }
615 }
616 }
617 }
618
619}
620void visit_check_sound(struct X3D_Node* node, unsigned int iframe) {}
621#endif //HAVE_OPENAL
622
623#ifdef HAVE_LIBSOUND
624void register_visit_check(struct X3D_Node* node);
625void visit_check_sound(struct X3D_Node* node, unsigned int iframe) {
626 struct X3D_SoundRep* srep = getSoundRep(node);
627 if (srep->icontext) {
628 if (iframe == srep->iframe) {
629 libsound_resumeContext0(srep->icontext);
630 //libsound_resumeNode0(node);
631 } else {
632 //not visited on last frame, perhaps in a switch deactivated branch
633 //lets pause the context
634 libsound_pauseContext0(srep->icontext);
635 //libsound_pauseNode0(node);
636 }
637 }
638}
639// v4 visibility functions, push & pop (to be) called from all X3DGroupingNode child_ functions
640void push_audio_context(int audio_context) {
641 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
642 stack_push(int, p->audio_context_stack, audio_context);
643}
644int peek_audio_context() {
645 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
646 return stack_top(int, p->audio_context_stack);
647}
648void create_and_push_audio_context(struct X3D_Node* node) {
649 //Hypothesis: Destination / output audio nodes create a context, and child source and processing audio nodes use the context
650 struct X3D_SoundRep* srep = getSoundRep(node);
651 if (!srep->icontext) {
652 int jcontext = peek_audio_context();
653 if (!jcontext) {
654 jcontext = libsound_createContext0();
655 }
656 srep->icontext = jcontext;
657 register_visit_check(node);
658 }
659 push_audio_context(srep->icontext);
660}
661void pop_audio_context() {
662 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
663 stack_pop(int, p->audio_context_stack);
664}
665void push_audio_parent(int inode) {
666 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
667 icset aps = { 0, 0, 0, 0, 0, 0 };
668 aps.p = inode;
669 aps.d = 0;
670 aps.ld = 0;
671 stack_push(icset, p->audio_parent_stack, aps);
672}
673void push_audio_parent3(int inode, int dstChan, int lstDst) {
674 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
675 icset aps = { 0, 0, 0, 0, 0, 0 };
676 aps.p = inode;
677 aps.d = dstChan;
678 aps.ld = lstDst;
679 stack_push(icset, p->audio_parent_stack, aps);
680}
681void push_audio_parentnode(struct X3D_Node* node) {
682 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
683 struct X3D_SoundRep* srep = getSoundRep(node);
684 push_audio_parent(srep->inode);
685}
686void pop_audio_parent() {
687 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
688 stack_pop(icset, p->audio_parent_stack);
689}
690icset peek_audio_parent() {
691 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
692 return stack_top(icset, p->audio_parent_stack);
693}
694void push_doppler_factor(float dopplerFactor) {
695 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
696 stack_push(float, p->doppler_factor_stack, dopplerFactor);
697}
698
699void pop_doppler_factor() {
700 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
701 stack_pop(float, p->doppler_factor_stack);
702}
703float peek_doppler_factor() {
704 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
705 return stack_top(float, p->doppler_factor_stack);
706}
707
708void push_splitter_source_index(int source_index, int last_source_index) {
709 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
710 ivec2 ssi;
711 ssi.x = source_index;
712 ssi.y = last_source_index;
713 stack_push(ivec2, p->splitter_source_stack, ssi);
714}
715
716void pop_splitter_source_index() {
717 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
718 stack_pop(ivec2, p->splitter_source_stack);
719}
720ivec2 peek_splitter_source_index() {
721 ppComponent_Sound p = (ppComponent_Sound)gglobal()->Component_Sound.prv;
722 return stack_top(ivec2, p->splitter_source_stack);
723}
724
725int newconnect(struct X3D_SoundRep* srep, icset iparent) {
726 if (!srep->connections) srep->connections = newStack(ivec3);
727 int duplicate = 0;
728 for (int i = 0; i < vectorSize(srep->connections); i++) {
729 icset conn = vector_get(icset, srep->connections, i);
730 if (conn.p == iparent.p && conn.n == iparent.n && conn.d == iparent.d && conn.s == iparent.s)
731 duplicate = 1;
732 }
733 if (!duplicate) {
734 stack_push(icset, srep->connections, iparent);
735 }
736 return 1 - duplicate;
737}
738int disconnect(struct X3D_SoundRep* srep, icset iparent) {
739 if (iparent.d == iparent.ld && iparent.s == iparent.ls) return 0; //no change in destination or source
740 if (!srep->connections) return 0; //no connections yet to delete
741 int found_old = -1;
742 for (int i = 0; i < vectorSize(srep->connections); i++) {
743 icset conn = vector_get(icset, srep->connections, i);
744 // LOGIC HERE IS STILL UNDER REVIEW
745 // proposed merger node: ls = s, ld = d at end of each render_ChannelMerger
746 // v4 draft merger/selector nodes: ls = s at end of each render_ChannelSelector
747 // the problem is initializing on first render only, so non-zero ls = s, ld = d
748 if (conn.p == iparent.p && conn.n == iparent.n) {
749 if (iparent.d != iparent.ld && conn.d == iparent.ld)
750 if (conn.s == iparent.s || conn.s == iparent.ls) found_old = i; //WHAT IF BOTH DESTINATION AND SOURCE CHANGE ON SAME FRAME?
751 if (iparent.s != iparent.ls && conn.s == iparent.ls)
752 if (conn.d == iparent.d || conn.d == iparent.ld) found_old = i; //WHAT IF BOTH DESTINATION AND SOURCE CHANGE ON SAME FRAME?
753 }
754 }
755 if (found_old > -1) {
756 vector_remove_elem(icset, srep->connections, found_old);
757 }
758 return found_old > -1 ? 1 : 0;
759}
760void update_connections(struct X3D_SoundRep* srep, icset iparent)
761{
762 if (newconnect(srep, iparent)) {
763 libsound_connect(srep->icontext, iparent);
764 //libsound_print_connections();
765 }
766 if (disconnect(srep, iparent)) {
767 libsound_disconnect(srep->icontext, iparent);
768 //libsound_print_connections();
769 }
770}
771
772void render_AudioClip(struct X3D_AudioClip* node) {
773 /* audio clip is a flat sound -no 3D- and a sound node (3D) refers to it
774 specs: if an audioclip can't be reached in the scenegraph, then it doesn't play
775 */
776 /* is this audio wavelet initialized yet? */
777 if (node->__loadstatus != LOAD_STABLE) {
778 locateAudioSource((struct X3D_AudioBuffer*)node); //downcast to share resource loading code
779 }
780 if (node->__loadstatus != LOAD_STABLE) return;
781 /* is this audio ok? if so, the sourceNumber will range
782 * between 0 and infinity; if it is BADAUDIOSOURCE, bad source.
783 * check out locateAudioSource to find out reasons */
784 if (node->__sourceNumber == BADAUDIOSOURCE) return;
785 if (node->__sourceNumber < 0) return; //if mpeg with no sound track, it will be loaded, but no sourceNumber
786 int icontext = peek_audio_context();
787 if (icontext == 0) return; //could be called from render_MovieTexture on the texture-draw pass
788 //..or called from separate Sound node as audioclip
789 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
790 srep->iframe = gglobal()->Mainloop.iframe;
791 srep->ibuffer = node->__sourceNumber;
792 srep->dopplerFactor = peek_doppler_factor();
793
794 icset iparent = peek_audio_parent();
795 //printf("ac audio_context %d parent_node %d\n", peek_audio_context(), iparent.x);
796
797 //node->gain = 1.0;
798 libsound_updateNode3(icontext, iparent, X3D_NODE(node));
799 iparent.s = 0;
800 iparent.n = srep->inode;
801 update_connections(srep, iparent);
802
803 //if (newconnect(srep, iparent)) {
804 // iparent.s = 0;
805 // iparent.n = srep->inode;
806 // libsound_connect(srep->icontext, iparent);
807 //}
808}
809void render_AudioBuffer(struct X3D_AudioBuffer* node) {
810 // two ways to load an audiobuffer:
811 // 1) floats representing PCM data, in MFFloat buffer field
812 // -- this field may be populated at run time, for example a script using a math formula
813 // 2) url loading of .wav. If url is empty, assume #1
814 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
815 srep->iframe = gglobal()->Mainloop.iframe;
816 if (node->__loadstatus == LOAD_STABLE) return;
817 if (node->url.n == 0) {
818 // 1) PCM float data
819 if (node->__loadstatus != LOAD_STABLE) {
820 //check for (scene or run-time) populated buffer MFFloat data
821 if (node->buffer.n) {
822 node->__sourceNumber = libsound_createBusFromPCM32(node->buffer.p, node->bufferChannels, node->buffer.n);
823 srep->ibuffer = node->__sourceNumber;
824 node->__loadstatus = LOAD_STABLE;
825 node->_ichange++;
826 }
827 }
828 }
829 else
830 {
831 //2) load .wav via URL (like audioclip)
832 if (node->__loadstatus != LOAD_STABLE) {
833 locateAudioSource(node);
834 }
835 if (node->__loadstatus != LOAD_STABLE) return;
836 /* is this audio ok? if so, the sourceNumber will range
837 * between 0 and infinity; if it is BADAUDIOSOURCE, bad source.
838 * check out locateAudioSource to find out reasons */
839 if (node->__sourceNumber == BADAUDIOSOURCE) return;
840 srep->ibuffer = node->__sourceNumber;
841 node->_ichange++;
842
843 //we don't connect() to parent.
844 // Parent looks in its bufferNode field and if not null, mines this node directly to setImpluse
845 }
846}
847void render_BufferAudioSource(struct X3D_BufferAudioSource* node) {
848 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
849 srep->iframe = gglobal()->Mainloop.iframe;
850 if (node->buffer && node->buffer->_nodeType == NODE_AudioBuffer) {
851 struct X3D_AudioBuffer* AB = (struct X3D_AudioBuffer*)node->buffer;
852 render_AudioBuffer(AB);
853 if (AB->_ichange != AB->_change)
854 node->_ichange++;
855 AB->_ichange = AB->_change;
856 srep->ibuffer = max(0,AB->__sourceNumber);
857 node->__sourceNumber = AB->__sourceNumber;
858 }
859 if (srep->ibuffer) { //wait for AudioBuffer URL to load
860 icset iparent = peek_audio_parent();
861 if (node->_ichange != node->_change) {
862
863 struct X3D_Node* anode = (struct X3D_Node*)node;
864 int icontext = peek_audio_context();
865 libsound_updateNode3(icontext, iparent, anode);
866 //MARK_NODE_COMPILED
867 node->_ichange = node->_change;
868 }
869 iparent.s = 0;
870 iparent.n = srep->inode;
871 update_connections(srep, iparent);
872
873 //if (newconnect(srep, iparent)) {
874 // iparent.s = 0;
875 // iparent.n = srep->inode;
876 // libsound_connect(srep->icontext, iparent);
877 //}
878 }
879
880}
881
882void render_AudioDestination(struct X3D_AudioDestination* node) {
883 struct X3D_Node* anode = (struct X3D_Node*)node;
884 icset have_parent = peek_audio_parent();
885 create_and_push_audio_context(anode);
886 if (!have_parent.p)
887 push_audio_parent(1); //should be the audio context device node
888
889 //push_audio_parentnode(anode);
890 //libsound_updateNode0(peek_audio_context(), have_parent, anode);
891
892 //printf("ad audio_context %d parent_node %d this node %d\n", peek_audio_context(), have_parent, peek_audio_parent());
893
894 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
895 srep->iframe = gglobal()->Mainloop.iframe;
896 if (node->children.n) {
897 for (int i = 0; i < node->children.n; i++)
898 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
899 render_node(X3D_NODE(node->children.p[i]));
900 }
901 //pop_audio_parent(); // sound audio destination node
902 if (!have_parent.p)
903 pop_audio_parent(); //audio context device node 1
904 pop_audio_context();
905}
906static struct X3D_ListenerPoint* singleton_listenerpoint = NULL;
907static double listenerpoint_matrix[16];
908void render_ListenerPoint(struct X3D_ListenerPoint* node) {
909 //in theory it should be a static singleton.
910 //we could do a stack, and peek at the last one loaded, in case there's a transform stack
911 singleton_listenerpoint = node;
912 double modelview[16];
913 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelview);
914 //concatonate the .position, .orientation
915
916 // I forget how. do I convert position and orientation to 2 separate matrices
917 // then multiply matrices?
918 // how convert position to matrix?
919 // how convert vrmlrot to matrix
920 double matt[16], matr[16], mat[16], matinv[16], mattot[16];
921 double cc[4], pp[3];
922 float2double(cc, node->orientation.c,4);
923 float2double(pp, node->position.c,3);
924 //matrotate(matr, cc[3], cc[0], cc[1], cc[2]); //has a different sense on the angles
925 matrixFromAxisAngle4d(matr, cc[3], cc[0], cc[1], cc[2]);
926 mattranslate(matt, pp[0], pp[1], pp[2]);
927 matmultiplyAFFINE(mat, matr, matt);
928 matmultiplyAFFINE(mattot, mat, modelview);
929 if (0) {
930 matinverseAFFINE(matinv, mattot);
931 matcopy(listenerpoint_matrix, matinv);
932 }
933 else {
934 matcopy(listenerpoint_matrix, mattot);
935 }
936 //Q how test, is there some geometry I can transform here,
937 // to make sure I have the orientation and position sense right?
938 // here's a test field for geometry to visualize. I'll use Transform containing axes in the scene
939 if (node->visualization) {
940 FW_GL_PUSH_MATRIX(); //copies and pushes current modelview on stack
941 FW_GL_TRANSFORM_D(mat); //now apply the above to prep for child_Tranform
942 render_node(X3D_NODE(node->visualization));
943 FW_GL_POP_MATRIX(); // back to modelview matrix
944 }
945 //then all panner nodes in all contexts replace context.listener default (viewpoint) with this pose
946 // prepare listener position
947 double position[3], direction[3], up[3];
948 float posf[3], dirf[3], upf[3];
949 int trackview = node->trackCurrentView ? 1 : 0;
950 vecsetd(position, 0.0, 0.0, 0.0);
951 transformAFFINEd(position, position, listenerpoint_matrix);
952 // prepare listener direction vector
953 vecsetd(direction, 1.0,0.0, 0.0); //default in web audio
954 vecsetd(up, 0.0, 1.0, 0.0);
955 transformAFFINEd(direction, direction, listenerpoint_matrix);
956 vecdifd(direction, direction, position);
957 vecnormald(direction, direction);
958 transformAFFINEd(up, up, listenerpoint_matrix);
959 vecdifd(up, up, position);
960 vecnormald(up, up);
961
962 double2float(posf, position, 3);
963 double2float(dirf, direction, 3);
964 double2float(upf, up, 3);
965 libsound_setListenerPose(posf, dirf, upf, trackview); //sets as static singleton, and all contexts adopt
966}
967
968void update_Sound_pose(struct X3D_Sound* node){
969 // update the pose of this sound source node relative to avatar
970 // avatar listener is always at location 0,0,0 looking 0 0 -1, up 0 1 0
971 // and sound source node does all the work transforming relative to moving avatar via modelMatrix on each frame
972 // transformed location, direction are stored in node.__lastlocation and node.__lastdirection
973 int i;
974 GLDOUBLE modelMatrix[16];
975 GLDOUBLE SourcePosd[3] = { 0.0f, 0.0f, 0.0f };
976 float SourcePos[3];
977
978 //transform source local coordinate 0,0,0 location into avatar/listener space
979 FW_GL_GETDOUBLEV(GL_MODELVIEW_MATRIX, modelMatrix);
980
981 transformAFFINEd(SourcePosd, SourcePosd, modelMatrix);
982 for (i = 0; i < 3; i++) SourcePos[i] = (float)SourcePosd[i];
983
984 if (node->__sourceNumber < 0) {
985 node->__lasttime = TickTime();
986 veccopy3f(node->__lastlocation.c, SourcePos); //transformed location
987
988 node->__sourceNumber = 0;
989 }
990 if (node->__sourceNumber > -1) {
991 int istate;
992 float SourceVel[3] = { 0.0f, 0.0f, 0.0f };
993 float travelled[3];
994 double traveltime;
995
996 //update velocity for doppler effect
997 vecdif3f(travelled, SourcePos, node->__lastlocation.c);
998 traveltime = TickTime() - node->__lasttime;
999 if (traveltime > 0.0)
1000 vecscale3f(node->__velocity.c, travelled, 1.0f / (float)traveltime);
1001
1002
1003 node->__lasttime = TickTime();
1004 veccopy3f(node->__lastlocation.c, SourcePos);
1005
1006 //directional sound
1007 //if (node->spatialize)
1008 {
1009 double dird[3];
1010 double zero[3];
1011 float dirf[3];
1012 //transform source direction into avatar/listener space
1013 //for (i = 0; i < 3; i++) dird[i] = node->direction.c[i];
1014 float2double(dird, node->direction.c, 3);
1015 vecsetd(zero, 0.0, 0.0, 0.0);
1016
1017 //zero[0] = zero[1] = zero[2] = 0.0;
1018 transformAFFINEd(dird, dird, modelMatrix);
1019 transformAFFINEd(zero, zero, modelMatrix);
1020
1021 vecdifd(dird, dird, zero);
1022 vecnormald(dird, dird);
1023 //for (i = 0; i < 3; i++) dirf[i] = (float)dird[i];
1024 double2float(node->__lastdirection.c, dird, 3);
1025 //veccopy3f(node->__lastdirection.c, dirf); //transformed direction
1026 /*
1027 if (1)
1028 alSourcefv(node->__sourceNumber, AL_DIRECTION, dirf);
1029 else
1030 alSource3f(node->__sourceNumber, AL_DIRECTION, dirf[0], dirf[1], dirf[2]);
1031 alSourcef(node->__sourceNumber, AL_CONE_OUTER_GAIN, .5f);
1032 alSourcef(node->__sourceNumber, AL_CONE_INNER_ANGLE, 90.0f);
1033 alSourcef(node->__sourceNumber, AL_CONE_OUTER_ANGLE, 135.0f);
1034 */
1035 }
1036 }
1037}
1038
1039void render_Sound(struct X3D_Sound* node) {
1040 struct X3D_Node* anode = (struct X3D_Node*)node;
1041 icset have_parent = peek_audio_parent();
1042 create_and_push_audio_context(anode); //Sound is a destination/output audioNode
1043 update_Sound_pose(node);
1044 if (!have_parent.p)
1045 push_audio_parent(1); //should be the audio context device node
1046 int icontext = peek_audio_context();
1047 libsound_updateNode3(icontext,peek_audio_parent(), anode);
1048 push_audio_parentnode(anode);
1049 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1050 srep->iframe = gglobal()->Mainloop.iframe;
1051
1052 if (node->source)
1053 //libsound_updateNode0(icontext,anode,node->source);
1054 render_node(node->source);
1055 if (node->children.n) {
1056 for (int i = 0; i < node->children.n; i++)
1057 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1058 render_node(X3D_NODE(node->children.p[i]));
1059 }
1060 pop_audio_parent(); // sound panner node
1061 if(!have_parent.p)
1062 pop_audio_parent(); //audio context device node 1
1063 pop_audio_context();
1064}
1065
1066void render_SpatialSound(struct X3D_SpatialSound* node) {
1067 struct X3D_Node* anode = (struct X3D_Node*)node;
1068 icset have_parent = peek_audio_parent();
1069 create_and_push_audio_context(anode); //Sound is a destination/output audioNode
1070 update_Sound_pose((struct X3D_Sound*)node); //down-cast to Sound, and assume the fields accessed are in same order as Sound
1071 if (!have_parent.p)
1072 push_audio_parent(1); //should be the audio context device node
1073 int icontext = peek_audio_context();
1074 libsound_updateNode3(icontext, peek_audio_parent(), anode);
1075 push_doppler_factor(node->__dopplerFactor);
1076 push_audio_parentnode(anode);
1077 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1078
1079 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1080 srep->iframe = gglobal()->Mainloop.iframe;
1081 if (node->children.n) {
1082 for (int i = 0; i < node->children.n; i++)
1083 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1084 render_node(X3D_NODE(node->children.p[i]));
1085 }
1086 pop_audio_parent(); // sound panner node
1087 pop_doppler_factor(node->__dopplerFactor);
1088 if(!have_parent.p)
1089 pop_audio_parent(); //audio context device node 1
1090 pop_audio_context();
1091
1092}
1093#endif //HAVE_LIBSOUND
1094
1095
1096#ifdef HAVE_LIBSOUND
1097
1098void render_PeriodicWave(struct X3D_PeriodicWave* node);
1099
1100void render_OscillatorSource(struct X3D_OscillatorSource *node){
1101 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1102 srep->iframe = gglobal()->Mainloop.iframe;
1103 //SenseInterp.c > OscillatorSourceTick:
1104 // no exemplar in Ticks of setting _changed, just MARK_EVENT which doesnt seem to (it updates route event)
1105 // could/should it set _ichange if any MARK_EVENTs? DONE, WORKS
1106 icset iparent = peek_audio_parent();
1107 if (node->_ichange != node->_change) {
1108 //if (node->_ichange == 0) return;
1109
1110 struct X3D_Node* anode = (struct X3D_Node*)node;
1111 int icontext = peek_audio_context();
1112 libsound_updateNode3(icontext, iparent, anode);
1113 //MARK_NODE_COMPILED
1114 node->_ichange = node->_change;
1115 }
1116 iparent.s = 0;
1117 iparent.n = srep->inode;
1118 update_connections(srep, iparent);
1119
1120 //if (newconnect(srep, iparent)) {
1121 // libsound_connect(srep->icontext, iparent);
1122 //}
1123
1124 if (node->periodicWave) {
1125 push_audio_parent(srep->inode);
1126 render_PeriodicWave((struct X3D_PeriodicWave*)node->periodicWave); //like rendering a child, check if anything changed.
1127 pop_audio_parent();
1128 }
1129
1130
1131}
1132void render_PeriodicWave(struct X3D_PeriodicWave* node) {
1133 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1134 srep->iframe = gglobal()->Mainloop.iframe;
1135 if (node->_ichange != node->_change) {
1136 //if (node->_ichange == 0) return;
1137
1138 struct X3D_Node* anode = (struct X3D_Node*)node;
1139 int icontext = peek_audio_context();
1140 icset iparent = peek_audio_parent();
1141 libsound_updateNode3(icontext, iparent, anode);
1142 //MARK_NODE_COMPILED
1143 }
1144
1145
1146}
1147
1148void render_ChannelMerger(struct X3D_ChannelMerger* node) {
1149 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1150 srep->iframe = gglobal()->Mainloop.iframe;
1151 struct X3D_Node* anode = (struct X3D_Node*)node;
1152 icset iparent = peek_audio_parent();
1153 if (node->_ichange != node->_change) {
1154 int icontext = peek_audio_context();
1155 libsound_updateNode3(icontext, iparent, anode);
1156 //MARK_NODE_COMPILED
1157 node->_ichange = node->_change;
1158 }
1159 iparent.s = 0;
1160 iparent.n = srep->inode;
1161 update_connections(srep, iparent);
1162
1163 //if (newconnect(srep, iparent)) {
1164 // libsound_connect(srep->icontext, iparent);
1165 //}
1166
1167 //push_audio_parentnode(anode);
1168 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1169 //srep->imerger = srep->inode; //libsound audio nodes check if their parent is a merger..
1170 int inode = srep->inode;
1171 int noisy = 0;
1172 if (node->children.n) {
1173 //for (int i = 0; i < node->children.n; i++) {
1174 if (noisy) printf("render_merger nchan %d\n", node->indexDestination.n);
1175 if (noisy) libsound_print_connections();
1176 if (node->selectors.n) {
1177 // proposal 3, selector is a 3-tuple (sourceChannel, destinationChannel, stream)
1178 // and merger.selectors mfnde holds the selectors
1179 // merger.children is a list of audio streams
1180 for (int i = 0; i < node->selectors.n; i++) {
1181 struct X3D_ChannelSelector* selector = (struct X3D_ChannelSelector*)node->selectors.p[i];
1182 if (!selector->_initialized) {
1183 selector->_lastChannelDestination = selector->channelDestination;
1184 selector->_lastChannelSource = selector->channelSource;
1185 selector->_lastStream = selector->stream;
1186 selector->_initialized = TRUE;
1187 }
1188
1189 int destination_index = selector->channelDestination;
1190 int last_destination_index = selector->_lastChannelDestination;
1191 int source_index = selector->channelSource;
1192 int last_source_index = selector->_lastChannelSource;
1193 if (noisy) printf("%d indxDst %d indxSrc %d\n", i, destination_index, source_index);
1194 if (source_index > -1) push_splitter_source_index(source_index, last_source_index); //-1 means there's no splitter in the audio stream
1195 if (destination_index != last_destination_index)
1196 printf("changing destination\n");
1197 push_audio_parent3(inode, destination_index, last_destination_index); // 0 is over-ridden by source_index if child is a Splitter
1198 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1199 //srep->idestination = i; // .. and if so connect to their parent using the recommended destination channel
1200 //int ichild = min(node->children.n - 1, source_index); //RE-USE LAST CHILD IF FEWER THAN INDXDST.N
1201 int ichild = selector->stream; //if there aren't enough streams, re-use the last one
1202 render_node(X3D_NODE(node->children.p[ichild]));
1203 pop_audio_parent();
1204 if (source_index > -1) pop_splitter_source_index();
1205 if (noisy) libsound_print_connections();
1206 if (noisy) printf("\n");
1207 selector->_lastChannelDestination = selector->channelDestination;
1208 selector->_lastChannelSource = selector->channelSource;
1209 selector->_lastStream = selector->stream;
1210 }
1211 } else if (node->indexDestination.n && node->indexSource.n && node->indexStream.n) {
1212 //Doug's proposal2 way with (indexStream,indexSource,indexDestination) tuples, Merger.children[i] == audio stream [i]
1213 for (int i = 0; i < node->indexDestination.n; i++) {
1214 int destination_index = node->indexDestination.p[i];
1215 int last_destination_index = srep->last_indexDestination[i];
1216 int source_index = node->indexSource.p[i];
1217 int last_source_index = srep->last_indexSource[i];
1218 if (noisy) printf("%d indxDst %d indxSrc %d\n", i, destination_index, source_index);
1219 if (source_index > -1) push_splitter_source_index(source_index, last_source_index); //-1 means there's no splitter in the audio stream
1220 push_audio_parent3(inode, destination_index, last_destination_index); // 0 is over-ridden by source_index if child is a Splitter
1221 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1222 //srep->idestination = i; // .. and if so connect to their parent using the recommended destination channel
1223 //int ichild = min(node->children.n - 1, source_index); //RE-USE LAST CHILD IF FEWER THAN INDXDST.N
1224 int ichild = node->indexStream.p[min(i, node->indexStream.n - 1)]; //if there aren't enough indexStream, re-use the last one
1225 render_node(X3D_NODE(node->children.p[ichild]));
1226 pop_audio_parent();
1227 if (source_index > -1) pop_splitter_source_index();
1228 if (noisy) libsound_print_connections();
1229 if (noisy) printf("\n");
1230 }
1231 memcpy(srep->last_indexSource, node->indexSource.p, node->indexSource.n * sizeof(int));
1232 memcpy(srep->last_indexDestination, node->indexDestination.p, node->indexDestination.n * sizeof(int));
1233 srep->last_count = node->indexDestination.n;
1234 } else {
1235 //thunk to v4 spec way, with ChannelSelector, and children[i] == mergerChannel[i]
1236 for (int i = 0; i < node->children.n; i++) {
1237 int destination_index = i;
1238 if (noisy) printf("%d indxDst %d \n", i, destination_index);
1239 push_audio_parent3(inode, destination_index, 0); // 0 is over-ridden by source_index if child is a Splitter
1240 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1241 //srep->idestination = i; // .. and if so connect to their parent using the recommended destination channel
1242 int ichild = i;
1243 render_node(X3D_NODE(node->children.p[ichild]));
1244 pop_audio_parent();
1245 if (noisy) libsound_print_connections();
1246 if (noisy) printf("\n");
1247 }
1248 }
1249 }
1250}
1251
1252void render_ChannelSelector(struct X3D_ChannelSelector* node) {
1253
1254 // doesn't push or pop parent, so children will connect to grandparent
1255 // we don't come through here with proposal3
1256 struct X3D_ChannelSelector* selector = node;
1257 if (!selector->_initialized) {
1258 selector->_lastChannelSource = selector->channelSource;
1259 selector->_initialized = TRUE;
1260 }
1261
1262 int source_index = selector->channelSource;
1263 int last_source_index = selector->_lastChannelSource;
1264 if (source_index > -1) push_splitter_source_index(source_index, last_source_index); //-1 means there's no splitter in the audio stream
1265 if (node->children.n) {
1266 for (int i = 0; i < node->children.n; i++)
1267 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1268 render_node(X3D_NODE(node->children.p[i]));
1269 }
1270 pop_splitter_source_index();
1271 selector->_lastChannelSource = selector->channelSource;
1272 //node->lastChannelSelection = node->channelSelection;
1273}
1274void render_ChannelSplitter(struct X3D_ChannelSplitter* node) {
1275 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1276 srep->iframe = gglobal()->Mainloop.iframe;
1277 struct X3D_Node* anode = (struct X3D_Node*)node;
1278 ivec2 splitter_source_index = peek_splitter_source_index();
1279 icset iparent = peek_audio_parent(); //(inode, destinationIndex, lastDestinationIndex)
1280 iparent.s = splitter_source_index.x;
1281 iparent.ls = splitter_source_index.y;
1282 if (node->_ichange != node->_change) {
1283 int icontext = peek_audio_context();
1284 libsound_updateNode3(icontext, iparent, anode);
1285 //MARK_NODE_COMPILED
1286 node->_ichange = node->_change;
1287 }
1288 iparent.n = srep->inode;
1289 update_connections(srep, iparent);
1290 //if (newconnect(srep, iparent)) {
1291 // libsound_connect(srep->icontext,iparent);
1292 // //libsound_print_connections();
1293 //}
1294 //if (disconnect(srep, iparent)) {
1295 // libsound_disconnect(srep->icontext, iparent);
1296 // //libsound_print_connections();
1297 //}
1298
1299 push_audio_parentnode(anode);
1300 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1301
1302 if (node->children.n) {
1303 for (int i = 0; i < node->children.n; i++)
1304 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1305 render_node(X3D_NODE(node->children.p[i]));
1306 }
1307 //printf("number of outputs nodes = %d\n", node->outputs.n);
1308 pop_audio_parent(); // sound panner node
1309
1310}
1311void render_Gain(struct X3D_Gain* node) {
1312 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1313 srep->iframe = gglobal()->Mainloop.iframe;
1314 struct X3D_Node* anode = (struct X3D_Node*)node;
1315 icset iparent = peek_audio_parent();
1316
1317 if (node->_ichange != node->_change) {
1318 //if (node->_ichange == 0) return;
1319 int icontext = peek_audio_context();
1320 libsound_updateNode3(icontext, iparent, anode);
1321 //MARK_NODE_COMPILED
1322 node->_ichange = node->_change;
1323 }
1324 iparent.n = srep->inode;
1325 iparent.s = 0;
1326 update_connections(srep, iparent);
1327
1328 //if (newconnect(srep, iparent)) {
1329 // libsound_connect(srep->icontext, iparent);
1330 //}
1331
1332 push_audio_parentnode(anode);
1333 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1334 if (node->children.n) {
1335 for (int i = 0; i < node->children.n; i++)
1336 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1337 render_node(X3D_NODE(node->children.p[i]));
1338 }
1339 pop_audio_parent(); // sound panner node
1340
1341}
1342
1343void render_Delay(struct X3D_Delay* node) {
1344 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1345 srep->iframe = gglobal()->Mainloop.iframe;
1346 struct X3D_Node* anode = (struct X3D_Node*)node;
1347 icset iparent = peek_audio_parent();
1348
1349 if (node->_ichange != node->_change) {
1350 //if (node->_ichange == 0) return;
1351
1352
1353 int icontext = peek_audio_context();
1354 libsound_updateNode3(icontext, iparent, anode);
1355 //MARK_NODE_COMPILED
1356 node->_ichange = node->_change;
1357 }
1358 iparent.n = srep->inode;
1359 iparent.s = 0;
1360 update_connections(srep, iparent);
1361
1362 //if (newconnect(srep, iparent)) {
1363 // libsound_connect(srep->icontext, iparent);
1364 //}
1365
1366
1367 push_audio_parentnode(anode);
1368 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1369 if (node->children.n) {
1370 for (int i = 0; i < node->children.n; i++)
1371 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1372 render_node(X3D_NODE(node->children.p[i]));
1373 }
1374 pop_audio_parent();
1375
1376}
1377
1378void render_Analyser(struct X3D_Analyser* node) {
1379 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1380 srep->iframe = gglobal()->Mainloop.iframe;
1381 struct X3D_Node* anode = (struct X3D_Node*)node;
1382 icset iparent = peek_audio_parent();
1383 if (node->_ichange != node->_change) {
1384 //if (node->_ichange == 0) return;
1385
1386
1387 int icontext = peek_audio_context();
1388 libsound_updateNode3(icontext, iparent, anode);
1389 //MARK_NODE_COMPILED
1390 node->_ichange = node->_change;
1391 node->_ichange++; //come in here every loop
1392 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_Analyser, floatFrequencyData));
1393 //MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_Analyser, byteFrequencyData));
1394 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_Analyser, floatTimeDomainData));
1395 //MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_Analyser, byteTimeDomainData));
1396
1397
1398 }
1399 iparent.n = srep->inode;
1400 iparent.s = 0;
1401 update_connections(srep, iparent);
1402
1403 //if (newconnect(srep, iparent)) {
1404 // libsound_connect(srep->icontext, iparent);
1405 //}
1406
1407 push_audio_parentnode(anode);
1408 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1409 if (node->children.n) {
1410 for (int i = 0; i < node->children.n; i++)
1411 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1412 render_node(X3D_NODE(node->children.p[i]));
1413 }
1414 pop_audio_parent();
1415
1416}
1417void render_BiquadFilter(struct X3D_BiquadFilter* node) {
1418 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1419 srep->iframe = gglobal()->Mainloop.iframe;
1420 struct X3D_Node* anode = (struct X3D_Node*)node;
1421 icset iparent = peek_audio_parent();
1422 if (node->_ichange != node->_change) {
1423 //if (node->_ichange == 0) return;
1424
1425
1426 int icontext = peek_audio_context();
1427 libsound_updateNode3(icontext, iparent, anode);
1428 //MARK_NODE_COMPILED
1429 node->_ichange = node->_change;
1430 }
1431 iparent.n = srep->inode;
1432 iparent.s = 0;
1433 update_connections(srep, iparent);
1434
1435 //if (newconnect(srep, iparent)) {
1436 // libsound_connect(srep->icontext, iparent);
1437 //}
1438
1439 push_audio_parentnode(anode);
1440 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1441 if (node->children.n) {
1442 for (int i = 0; i < node->children.n; i++)
1443 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1444 render_node(X3D_NODE(node->children.p[i]));
1445 }
1446 pop_audio_parent();
1447
1448}
1449
1450void render_DynamicsCompressor(struct X3D_DynamicsCompressor* node) {
1451 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1452 srep->iframe = gglobal()->Mainloop.iframe;
1453 struct X3D_Node* anode = (struct X3D_Node*)node;
1454 icset iparent = peek_audio_parent();
1455
1456 if (node->_ichange != node->_change) {
1457 //if (node->_ichange == 0) return;
1458
1459
1460 int icontext = peek_audio_context();
1461 libsound_updateNode3(icontext, iparent, anode);
1462 //MARK_NODE_COMPILED
1463 node->_ichange = node->_change;
1464 MARK_EVENT(X3D_NODE(node), offsetof(struct X3D_DynamicsCompressor, reduction));
1465
1466 }
1467 iparent.n = srep->inode;
1468 iparent.s = 0;
1469 update_connections(srep, iparent);
1470
1471 //if (newconnect(srep, iparent)) {
1472 // libsound_connect(srep->icontext, iparent);
1473 //}
1474
1475 push_audio_parentnode(anode);
1476 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1477 if (node->children.n) {
1478 for (int i = 0; i < node->children.n; i++)
1479 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1480 render_node(X3D_NODE(node->children.p[i]));
1481 }
1482 pop_audio_parent();
1483}
1484
1485void render_WaveShaper(struct X3D_WaveShaper* node) {
1486 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1487 srep->iframe = gglobal()->Mainloop.iframe;
1488 struct X3D_Node* anode = (struct X3D_Node*)node;
1489 icset iparent = peek_audio_parent();
1490
1491 if (node->_ichange != node->_change) {
1492 //if (node->_ichange == 0) return;
1493
1494 int icontext = peek_audio_context();
1495 libsound_updateNode3(icontext, iparent, anode);
1496 //MARK_NODE_COMPILED
1497 node->_ichange = node->_change;
1498
1499 }
1500 iparent.n = srep->inode;
1501 iparent.s = 0;
1502 update_connections(srep, iparent);
1503
1504 //if (newconnect(srep, iparent)) {
1505 // libsound_connect(srep->icontext, iparent);
1506 //}
1507
1508 push_audio_parentnode(anode);
1509 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1510 if (node->children.n) {
1511 for (int i = 0; i < node->children.n; i++)
1512 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1513 render_node(X3D_NODE(node->children.p[i]));
1514 }
1515 pop_audio_parent();
1516
1517}
1518
1519
1520
1521void render_Convolver(struct X3D_Convolver* node) {
1522 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1523 srep->iframe = gglobal()->Mainloop.iframe;
1524 struct X3D_Node* anode = (struct X3D_Node*)node;
1525 icset iparent = peek_audio_parent();
1526 if (node->buffer && node->buffer->_nodeType == NODE_AudioBuffer) {
1527 render_AudioBuffer((struct X3D_AudioBuffer*)node->buffer);
1528 if (node->buffer->_ichange != node->buffer->_change)
1529 node->_ichange++;
1530 node->buffer->_ichange = node->buffer->_change;
1531 }
1532 if (node->_ichange != node->_change) {
1533 //if (node->_ichange == 0) return;
1534
1535 int icontext = peek_audio_context();
1536 libsound_updateNode3(icontext, iparent, anode);
1537 //MARK_NODE_COMPILED
1538 node->_ichange = node->_change;
1539
1540 }
1541 iparent.n = srep->inode;
1542 iparent.s = 0;
1543 update_connections(srep, iparent);
1544
1545 //if (newconnect(srep, iparent)) {
1546 // libsound_connect(srep->icontext, iparent);
1547 //}
1548 push_audio_parentnode(anode);
1549 //printf("ss audio_context %d parent_node %d\n", peek_audio_context(), have_parent);
1550 if (node->children.n) {
1551 for (int i = 0; i < node->children.n; i++)
1552 //libsound_updateNode0(icontext,anode,(struct X3D_Node*) node->children.p[i]);
1553 render_node(X3D_NODE(node->children.p[i]));
1554 }
1555 pop_audio_parent();
1556
1557}
1558
1559void render_MicrophoneSource(struct X3D_MicrophoneSource* node) {
1560 // labsound has a few Example.hpp examples on using 'devices' as microphones
1561 struct X3D_SoundRep* srep = getSoundRep(X3D_NODE(node));
1562 srep->iframe = gglobal()->Mainloop.iframe;
1563 struct X3D_Node* anode = (struct X3D_Node*)node;
1564 icset iparent = peek_audio_parent();
1565
1566 if (node->_ichange != node->_change) {
1567 //if (node->_ichange == 0) return;
1568
1569 int icontext = peek_audio_context();
1570 libsound_updateNode3(icontext, iparent, anode);
1571 //MARK_NODE_COMPILED
1572 node->_ichange = node->_change;
1573
1574 }
1575 iparent.n = srep->inode;
1576 iparent.s = 0;
1577 update_connections(srep, iparent);
1578
1579 //if (newconnect(srep, iparent)) {
1580 // libsound_connect(srep->icontext, iparent);
1581 //}
1582
1583}
1584
1585
1586
1587void render_StreamAudioSource(struct X3D_StreamAudioSource *node){
1588 COMPILE_IF_REQUIRED
1589 //don't know if / how to do this one in native code
1590 // web audio API has a MediaStreamSource that pulls from html elements
1591 // Labsound doesn't have MediaStreamSource
1592}
1593
1594void render_StreamAudioDestination(struct X3D_StreamAudioDestination *node){
1595 COMPILE_IF_REQUIRED
1596}
1597
1598
1599
1600
1601
1602void render_ListenerPointSource(struct X3D_ListenerPointSource *node){
1603 COMPILE_IF_REQUIRED
1604}
1605
1606#else //HAVE_LIBSOUND
1607//HAVE_OPENAL
1608void render_AudioBuffer(struct X3D_AudioBuffer* node) {}
1609void render_ListenerPoint(struct X3D_ListenerPoint* node) {}
1610void render_OscillatorSource(struct X3D_OscillatorSource* node) {}
1611void render_BufferAudioSource(struct X3D_BufferAudioSource* node) {}
1612void render_StreamAudioSource(struct X3D_StreamAudioSource* node) {}
1613void render_WaveShaper(struct X3D_WaveShaper* node) {}
1614void render_PeriodicWave(struct X3D_PeriodicWave* node) {}
1615void render_AudioDestination(struct X3D_AudioDestination* node) {}
1616void render_StreamAudioDestination(struct X3D_StreamAudioDestination* node) {}
1617void render_Analyser(struct X3D_Analyser* node) {}
1618void render_ChannelMerger(struct X3D_ChannelMerger* node) {}
1619void render_ChannelSelector(struct X3D_ChannelSelector* node) {}
1620void render_ChannelSplitter(struct X3D_ChannelSplitter* node) {}
1621void render_BiquadFilter(struct X3D_BiquadFilter* node) {}
1622void render_Convolver(struct X3D_Convolver* node) {}
1623void render_Delay(struct X3D_Delay* node) {}
1624void render_DynamicsCompressor(struct X3D_DynamicsCompressor* node) {}
1625void render_Gain(struct X3D_Gain* node) {}
1626void render_ListenerPointSource(struct X3D_ListenerPointSource* node) {}
1627void render_MicrophoneSource(struct X3D_MicrophoneSource* node) {}
1628void render_SpatialSound(struct X3D_SpatialSound* node) {}
1629#endif //HAVE_LIBSOUND
Definition libmidi.h:3