FreeWRL/FreeX3D  3.0.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 
45 int freewrlSystem (char *string);
46 
47 key_t IPCKey;
48 int msq_fromclnt;
49 int msq_toclnt;
50 
51 int current_max = -1; /* the maximum source number recieved so far.*/
52 int registered[MAXSOURCES]; /* is source registered? (boolean)*/
53 int active[MAXSOURCES]; /* is source active? (boolean)*/
54 int loop[MAXSOURCES]; /* is this sound looped? (boolean)*/
55 SNDFILE *sndfile[MAXSOURCES]; /* structure containing sound file info*/
56 
57 FWSNDMSG msg; /* incoming message*/
58 
59 float fps = 30.0; /* initial value...*/
60 int currentSource = -1;
61 
62 char cp2[310]; /* hold the current filename, in case of errors*/
63 
64 int S_Server_IPC = -1;
65 int xx;
66 
67 unsigned char* data;
68 
69 
70 /*
71  * Sound file handling routines
72  */
73 
74 /* if the sndfile is open, rewind and set bytes_remaining*/
75 void 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.*/
98 int 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 
119 int 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 
205 SNDFILE *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  */
250 void 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 
263 int 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*/
269 void 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 
393 int 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. */
469 int 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