FreeWRL / FreeX3D 4.3.0
main.c
1
2/****************************************************************************
3 This file is part of the FreeWRL/FreeX3D Distribution.
4
5 Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
6
7 FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 FreeWRL/FreeX3D is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
19****************************************************************************/
20
21/*********************************************************************
22 *
23 * FreeWRL SoundServer engine
24 *
25 * Copyright (C) 2002 John Stewart, CRC Canada.
26 * DISTRIBUTED WITH NO WARRANTY, EXPRESS OR IMPLIED.
27 * See the GNU Library General Public License (file COPYING in the distribution)
28 * for conditions of use and redistribution.
29 *
30 * Wav decoding data came from data sheets at:
31 * http:www.borg.com/~jglatt/tech/wave.htm
32 *
33 * Some programming info from:
34 * http: vengeance.et.tudelft.nl/ecfh/Articles/devdsp-0.1.txt
35 *
36 *********************************************************************/
37
38# include <config.h>
39
40#include "system.h"
41#include "soundheader.h"
42#include <input/InputFunctions.h>
43#include "internal.h"
44
45int freewrlSystem (char *string);
46
47key_t IPCKey;
48int msq_fromclnt;
49int msq_toclnt;
50
51int current_max = -1; /* the maximum source number recieved so far.*/
52int registered[MAXSOURCES]; /* is source registered? (boolean)*/
53int active[MAXSOURCES]; /* is source active? (boolean)*/
54int loop[MAXSOURCES]; /* is this sound looped? (boolean)*/
55SNDFILE *sndfile[MAXSOURCES]; /* structure containing sound file info*/
56
57FWSNDMSG msg; /* incoming message*/
58
59float fps = 30.0; /* initial value...*/
60int currentSource = -1;
61
62char cp2[310]; /* hold the current filename, in case of errors*/
63
64int S_Server_IPC = -1;
65int xx;
66
67unsigned char* data;
68
69
70/*
71 * Sound file handling routines
72 */
73
74/* if the sndfile is open, rewind and set bytes_remaining*/
75void rewind_to_beginning (SNDFILE *wavfile) {
76 if (wavfile != NULL) {
77 if (wavfile->fd != NULL) {
78 /* printf ("rewinding to beginning...\n");*/
79 /* printf ("seek set is %d, chunkSize %d\n",*/
80 /* (int)wavfile->wavdataoffset,*/
81 /* (int)wavfile->DataChunk.chunkSize);*/
82
83 wavfile->bytes_remaining = wavfile->DataChunk.chunkSize;
84 fseek (wavfile->fd, wavfile->wavdataoffset, SEEK_SET);
85 /* printf ("rewind bytes remaining %ld\n",wavfile->bytes_remaining);*/
86
87 if (wavfile->bytes_remaining <= 0) {
88 printf ("Error in getting wavfile DataChunk\n");
89 wavfile->fd = NULL;
90 return;
91 }
92 }
93 }
94}
95
96
97/* find a chunk start.*/
98int chunk (char *buf, char *find, int len) {
99 int mycnt;
100
101 mycnt=0;
102 while (mycnt < (len) - strlen(find)) {
103 if (strncmp (&buf[mycnt], find, strlen(find)) == 0) {
104 /* found it!*/
105 /* printf ("found %s at %d\n",find, mycnt);*/
106 return (mycnt);
107 } else {
108 /* printf ("not found, mycnt = %d\n",mycnt);*/
109 mycnt ++;
110 }
111 }
112 return -1; /* did not find it*/
113}
114
115
116/* Decode what kind of file this is - skip through the headers,*/
117/* save the type in the SNDFILE structure.*/
118
119int querySoundType(SNDFILE *me) {
120 int br;
121
122 br = fread(me->data,1,BUFSIZE,me->fd);
123 me->dataptr = chunk (me->data,"RIFF",BUFSIZE);
124
125 /* Not a RIFF file*/
126 if (me->dataptr < 0) {
127 printf ("SoundEngine:not a RIFF file\n\t%s\n",cp2);
128 return -1;
129 }
130
131 br = chunk (&me->data[me->dataptr],"WAVE",BUFSIZE);
132 /* a WAVE file*/
133 if (br < 0) {
134 printf ("SoundEngine:not a WAVE file\n\t%s\n",cp2);
135 return -1;
136 }
137
138 me->dataptr +=br;
139 br = chunk (&me->data[me->dataptr],"fmt ",BUFSIZE);
140 /* have format*/
141 if (br < 0) {
142 printf ("SoundServer:no fmt found in WAVE file\n\t%s\n",cp2);
143 return -1;
144 }
145
146 me->dataptr += br;
147
148 /* copy over format header information*/
149 memcpy (&me->FormatChunk, &me->data[me->dataptr], sizeof (fmtChnk));
150
151 /*
152 printf ("fmt chunkid %c%c%c%c\n",me->FormatChunk.chunkID[0],
153 me->FormatChunk.chunkID[1],me->FormatChunk.chunkID[2],me->FormatChunk.chunkID[3]);
154 printf ("fmt chunkSize %ld\n", me->FormatChunk.chunkSize);
155 printf ("fmt wChannels %d\n", me->FormatChunk. wChannels);
156 printf ("fmt wFormatTag %d\n", me->FormatChunk. wFormatTag);
157 printf ("fmt dwSamplesPerSec %ld\n", me->FormatChunk. dwSamplesPerSec);
158 printf ("fmt dwAvgBytesPerSec %ld\n", me->FormatChunk. dwAvgBytesPerSec);
159 printf ("fmt wBlockAlign %d\n", me->FormatChunk. wBlockAlign);
160 printf ("fmt wBitsPerSample %d\n", me->FormatChunk. wBitsPerSample);
161 */
162
163 if (me->FormatChunk. wFormatTag != 1) {
164 printf ("SoundServer:compressed WAV not handled yet\n\t%s\n",
165 cp2);
166 return -1;
167 }
168
169 /* pass over the fmt chunk - note the chunkSize does not include all. see spec.*/
170 me->dataptr += 8 + me->FormatChunk.chunkSize;
171
172
173 br = chunk (&me->data[me->dataptr],"data",BUFSIZE);
174 /* have data*/
175 if (br < 0) {
176 printf ("SoundServer:no data found in WAVE file\n\t%s\n",cp2);
177 return -1;
178 }
179
180 me->dataptr += br;
181 memcpy (&me->DataChunk, &me->data[me->dataptr], sizeof (datChnk));
182
183 /*
184 printf ("data chunkid %c%c%c%c\n",me->DataChunk.chunkID[0],
185 me->DataChunk.chunkID[1],me->DataChunk.chunkID[2],me->DataChunk.chunkID[3]);
186 printf ("data chunkSize %lx\n", me->DataChunk.chunkSize);
187 printf ("actual number of sample frames %ld\n",me->DataChunk.chunkSize/me->FormatChunk.wBlockAlign);
188 printf ("dataptr is %d\n",me->dataptr);
189 */
190
191 /* does this file have a zero chunksize?*/
192 if (me->DataChunk.chunkSize <= 0) {
193 printf ("SoundServer:WAV DataChunk size invalid\n\t%s\n",cp2);
194 return -1;
195 }
196
197 /* is this file compressed?*/
198
199 me->wavdataoffset = me->dataptr+8; /* wavdataoffset is the actual position of start of data*/
200 return WAVFILE;
201}
202
203/* Open and initiate sound file*/
204
205SNDFILE *openSound (char *path,int soundNo) {
206
207 SNDFILE *mysound;
208
209 mysound = (SNDFILE *) malloc (sizeof(SNDFILE)); /* This is the return value*/
210
211 if (!mysound) return NULL; /* memory allocation error*/
212
213 mysound->fd = fopen(path,"r");
214 mysound->bytes_remaining = UNINITWAV;
215
216 if (mysound->fd == NULL) {
217 free (mysound);
218 return NULL;
219 }
220
221 /* ok - we have the file opened. Assume WAV file, because that's*/
222 /* what we handle for now.*/
223
224 switch (querySoundType(mysound)) {
225 case WAVFILE: {
226 return initiateWAVSound(mysound,soundNo);
227 break;
228 }
229 case MP3FILE: {
230 mysound->type = MP3FILE;
231 break;
232 }
233 case MPGFILE: {
234 mysound->type = MPGFILE;
235 break;
236 }
237 default: {
238 printf ("unknown file type: %s\n",cp2);
239 free (mysound);
240 return NULL;
241 }
242 }
243 /* we should never reach here...*/
244 return NULL;
245}
246
247/*
248 * Receive information from FreeWRL
249 */
250void toclnt(char *message_to_send) {
251 msg.mtype= 1;
252 (void) strcpy(msg.msg, message_to_send);
253 /* printf ("SoundEngine - sending back %s\n",msg.msg);*/
254
255 while((xx=msgsnd(msq_toclnt, &msg,strlen(msg.msg)+1,IPC_NOWAIT)) != 0);
256 if (xx) { /* Send to client */
257 printf ("SoundEngineServer - error sending ready msg\n");
258 exit(1);
259 }
260 /* printf ("SoundEngine - sendT back %s\n",msg.msg);*/
261}
262
263int fromclnt () {
264 return msgrcv(msq_fromclnt,&msg,256,1,0);
265}
266
267
268/* Go through, and act on the message -it is stored in the global "msg" struct*/
269void process_command () {
270 float x,y,z; /* temporary variables*/
271 int a,b,cp2len; /* temporary variables*/
272 int myloop;
273 int mysource;
274 float bal; /* balance*/
275 char cp[310]; /* temporary variable*/
276 char st[10];
277 char pitch[30];
278 double duration;
279
280 /* printf ("processing %s\n",msg.msg);*/
281
282 if (strncmp ("REGS",msg.msg,4) == 0) {
283 /* a REGISTER message*/
284 a=5; b=0;
285 /* printf ("REGS matched len %d, first start %c %c %c %c\n",strlen(msg.msg),*/
286 /* msg.msg[a],msg.msg[a+1], msg.msg[a+2], msg.msg[a+3]);*/
287
288 /* start SOX conversion...*/
289 cp[0]='\0';
290 strcpy(cp,SOUNDCONV);
291 strcat(cp," ");
292 b = strlen(cp);
293 cp2len=0; /* keep the original file name around for a bit.*/
294
295 /* copy over the url name; skip past the REGS: at beginning.*/
296 while ((a<strlen(msg.msg)-1) && (b<300) && (msg.msg[a]>' ')) {
297 cp[b]=msg.msg[a];
298 cp2[cp2len] = msg.msg[a];
299 b++; a++; cp2len++;
300 }
301 cp[b]='\0'; cp2[cp2len]='\0';
302
303 /* get rest of parameters*/
304 /* printf ("getting rest of parameters from %s\n",&msg.msg[a]);*/
305 sscanf (&msg.msg[a], " %d %d %f %f %f",&mysource,&myloop,&x,&y,&z);
306
307 /* do the pitch*/
308 strcat (cp, " -r ");
309 if ((x > 1.01) || (x < 0.98)) {
310 if (x<0.01) x = 1;
311 sprintf (pitch,"%d ",(int) ((float)22050.0/x));
312 } else {
313 sprintf (pitch,"%d ",22050);
314 }
315 strcat (cp,pitch);
316
317 /* finish the conversion line*/
318 strcat (cp,"-c2 -w /tmp/sound");
319 b = strlen(cp);
320
321 sprintf (st,"%d.wav",mysource);
322 /* printf ("ST is %s\n cp is %s\n",st,cp);*/
323 strcat (cp,st);
324
325 /* strcat (cp, " 2>/tmp/FreeWRL_Errors");*/
326
327 /* printf ("going to system %s\n",cp); */
328 freewrlSystem (cp);
329
330 /* make the new, converted file name, then later, open it*/
331 strcpy (cp,"/tmp/sound");
332 strcat (cp,st);
333
334 /* printf ("registering source %d loop %d x %f y %f z %f name %s \n",mysource,myloop,x,y,z,cp);*/
335
336 if (mysource > current_max) current_max = mysource;
337
338
339 /* Can we open this sound file?*/
340
341 /* printf ("REGS opening sound\n");*/
342 sndfile[mysource] = openSound(cp,mysource);
343 if (sndfile[mysource] == NULL) {
344 printf ("SoundServer:open problem for:\n\t %s\n",cp2);
345 duration = 1.0;
346 } else {
347 /* Copy over all of the temporary data to the correct place.*/
348 registered[mysource] = 1; /* is source registered? (boolean)*/
349 loop[mysource] = myloop; /* is this sound looped? (boolean)*/
350 sndfile[mysource]->pitch = x; /* pitch of 1 = standard playback*/
351
352 sndfile[mysource]->ampl = 0; /* Gain of this sound*/
353 sndfile[mysource]->balance = 50; /* balance of this sound.*/
354
355 duration = (double) sndfile[mysource]->DataChunk.chunkSize / (double) sndfile[mysource]->FormatChunk.dwAvgBytesPerSec;
356
357 }
358 sprintf (cp, "REGS %d %f",mysource,(float)duration);
359 toclnt(cp); /* Tell client we're ready */
360
361 } else if (strncmp ("AMPL",msg.msg,4) == 0) {
362 /* set amplitude for this sound source*/
363
364/* printf ("%s\n",msg.msg);*/
365 /* format is command, source#, amplitude, balance, Framerate */
366
367 sscanf (msg.msg,"AMPL %d %f %f %f",&a,&x,&bal,&fps);
368 /* printf ("got ampl for sound %d\n",a);*/
369 if ((registered[a] == 1) && (a>=0) && (a<MAXSOURCES)) {
370 sndfile[a]->ampl = (int) (x*100.0);
371 /* printf ("ampl conv, orig %f now %d\n",x,sndfile[a]->ampl);*/
372 sndfile[a]->balance = (int) ((float)bal * 100.0);
373 }
374 playWavFragment ();
375 } else if (strncmp ("ACTV",msg.msg,4) == 0) {
376 /* set this source to be active*/
377 sscanf (msg.msg,"ACTV %d %d",&a,&b);
378 if ((a>=0) && (a<MAXSOURCES)) {
379 active[a]=b;
380 if (b==1) {
381 /* sound is becoming active*/
382 rewind_to_beginning (sndfile[a]);
383 }
384 }
385 /* printf ("ACTV parsing, active%d now is %d from message %s\n",a,b,msg.msg);*/
386
387 /* } else {*/
388 /* printf ("SoundEngine - unknown message recieved %s\n",msg.msg);*/
389 }
390}
391
392
393int main(int argc,char **argv) {
394
395 /* FIXME: argc is minimum 1 since argv[0] contains program's name */
396 if (argc <1) {
397 printf ("Server: too few args\n");
398 exit(1);
399 }
400
401 if ((argc == 2) && !strcmp(argv[1],"-v")) {
402 printf("FreeWRL sound server\nVersion: %s\n", freewrl_snd_get_version());
403 exit(0);
404 }
405
406 /* initiate tables*/
407 for (xx=0; xx<MAXSOURCES; xx++) {
408 registered[xx] = 0;
409 active[xx] = 0;
410 sndfile[xx] = NULL;
411 }
412
413 /* open the DSP*/
414 initiateDSP();
415
416 /* printf ("Server - getting the client IPC from argv %s\n", argv[0]);*/
417 S_Server_IPC=getppid();
418
419 /* printf ("a='%s', msg='%s', d='%d'.\n", argv[0],msg.msg,S_Server_IPC);*/
420 if (!strncmp("INIT",argv[0],4)) {
421 sscanf (argv[0],"%s%d",msg.msg,&S_Server_IPC);
422 } else {
423 printf ("SoundServer: no Client_IPC on command line\n");
424 /* printf ("a='%s', msg='%s', dud='%d'.\n", argv[0],msg.msg,dud);*/
425 exit(1);
426 }
427
428 /* get message queues*/
429 if ((msq_fromclnt = msgget(S_Server_IPC,0666)) < 0) {
430 printf ("SoundServer: no IPC queue available\n");
431 exit(1);
432 }
433 if ((msq_toclnt = msgget(S_Server_IPC+1,0666)) < 0) {
434 printf ("SoundServer: no IPC queue available\n");
435 exit(1);
436 }
437 /* printf ("Server, ok, msq_fromclnt=%x msq_toclnt=%x key %d\n",*/
438 /* msq_fromclnt,msq_toclnt,S_Server_IPC);*/
439
440
441 toclnt("OK"); /* Tell client we're ready */
442
443 do {
444 xx = fromclnt();
445 if (xx < 0) {
446 /* gets here if the client exited*/
447 exit (0);
448 }
449
450 /* printf ("server, from FreeWRL=%x message='%s'\n",xx,msg.msg);*/
451 process_command ();
452 } while (strncmp ("QUIT",msg.msg,4));
453#if 0
454 int count;
455 char fileRemove[200];
456 for (count=0; count<current_max; count++) {
457 sprintf (fileRemove,"/tmp/sound%d.wav",count);
458 /* printf ("unlinking %d\n",count);*/
459 unlinkShadowFile(fileRemove);
460 }
461#endif
462 /* printf ("Server exiting normally\n");*/
463 exit(0);
464}
465
466/* get all system commands, and pass them through here. What we do
467 * is take parameters and execl them, in specific formats, to stop
468 * people (or, to try to stop) from typing malicious code. */
469int freewrlSystem (char *sysline) {
470
471#define MAXEXECPARAMS 10
472#define EXECBUFSIZE 2000
473 int ok;
474 char *paramline[MAXEXECPARAMS];
475 char buf[EXECBUFSIZE];
476 char *internbuf;
477 int count;
478 pid_t childProcess;
479 int pidStatus;
480
481 int waitForChild;
482
483 waitForChild = TRUE;
484
485 ok = FALSE;
486 internbuf = buf;
487
488 /* bounds check */
489 if (strlen(sysline)>=EXECBUFSIZE) return FALSE;
490 strcpy (buf,sysline);
491
492 /* printf ("freewrlSystem, have %s here\n",internbuf);*/
493 for (count=0; count<MAXEXECPARAMS; count++) paramline[count] = NULL;
494
495 /* split the command off of internbuf, for execing. */
496 count = 0;
497 while (internbuf != NULL) {
498 paramline[count] = internbuf;
499 internbuf = strchr(internbuf,' ');
500 if (internbuf != NULL) {
501 /* printf ("more strings here! :%s:\n",internbuf);*/
502 *internbuf = '\0';
503 /* printf ("param %d is :%s:\n",count,paramline[count]);*/
504 internbuf++;
505 count ++;
506 if (count >= MAXEXECPARAMS) return -1; /* never...*/
507 }
508 }
509
510/* printf ("finished while loop, count %d\n",count);*/
511/* { int xx;*/
512/* for (xx=0; xx<MAXEXECPARAMS;xx++) {*/
513/* printf ("item %d is :%s:\n",xx,paramline[xx]);*/
514/* }}*/
515
516
517 /* is the last string "&"? if so, we don't need to wait around */
518 if (strncmp(paramline[count],"&",strlen(paramline[count])) == 0) {
519 waitForChild=FALSE;
520 paramline[count] = '\0'; /* remove the ampersand.*/
521 }
522
523 if (count > 0) {
524 switch (childProcess=fork()) {
525 case -1:
526 perror ("fork"); exit(1);
527
528 case 0: {
529 int Xrv;
530
531 /* child process */
532 /* printf ("child execing, pid %d %d\n",childProcess, getpid());*/
533 Xrv = execl(paramline[0],
534 paramline[0],paramline[1], paramline[2],
535 paramline[3],paramline[4],paramline[5],
536 paramline[6],paramline[7],NULL);
537 /* printf ("child finished execing\n");*/
538 exit (Xrv);
539 }
540 default: {
541 /* parent process */
542 /* printf ("parent waiting for child %d\n",childProcess);*/
543
544 /* do we have to wait around? */
545 if (!waitForChild) {
546 /* printf ("do not have to wait around\n");*/
547 return 0;
548 }
549 waitpid (childProcess,&pidStatus,0);
550 /* printf ("parent - child finished - pidStatus %d \n",*/
551 /* pidStatus);*/
552 }
553 }
554 return pidStatus;
555 } else {
556 printf ("System call failed :%s:\n",sysline);
557 }
558 return -1;
559}
560