Crossfire Client, Branch  R11627
cfsndserv.c
Go to the documentation of this file.
00001 static char *rcsid_cfsndserv_c =
00002     "$Id: cfsndserv.c 8649 2008-03-29 10:58:58Z ryo_saeba $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007     Copyright (C) 2003 Tim Hentenaar
00008 
00009     This program is free software; you can redistribute it and/or modify
00010     it under the terms of the GNU General Public License as published by
00011     the Free Software Foundation; either version 2 of the License, or
00012     (at your option) any later version.
00013 
00014     This program is distributed in the hope that it will be useful,
00015     but WITHOUT ANY WARRANTY; without even the implied warranty of
00016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017     GNU General Public License for more details.
00018 
00019     You should have received a copy of the GNU General Public License
00020     along with this program; if not, write to the Free Software
00021     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00022 
00023     The author can be reached via e-mail to crossfire-devel@real-time.com
00024 */
00025 
00026 /* Comment from the original author is below.  In addition to OSS
00027  * and ALSA, sun sound is also supported.
00028  */
00029 
00030 /*
00031  * (c) 1998 Jacek Konieczny <jajcus@zeus.polsl.gliwice.pl>
00032  *
00033  * This file contains the server for sound support for the client.
00034  * It supports both ALSA_SOUND and OSS_SOUND. Any other sound system support
00035  * can be easily added - only new init_audio and audio_play
00036  * need be written.
00037  *
00038  * If you have any problems please e-mail me.
00039  */
00040 
00041 
00042 /*#define ALSA_SOUND*/
00043 /*#define OSS_SOUND*/
00044 /*#define SGI_SOUND*/
00045 /*#define SUN_SOUND*/
00046 
00047 /*#define SOUND_DEBUG*/
00048 
00049 #include <config.h>
00050 
00051 #include <stdio.h>
00052 #include <sys/types.h>
00053 #include <sys/stat.h>
00054 #include <time.h>
00055 
00056 #ifdef HAVE_FCNTL_H
00057 #include <fcntl.h>
00058 #endif
00059 
00060 #ifdef HAVE_UNISTD_H
00061 #include <unistd.h>
00062 #endif
00063 
00064 #include <math.h>
00065 
00066 #ifdef HAVE_SYS_IOCTL_H
00067 #include <sys/ioctl.h>
00068 #endif
00069 
00070 #ifdef HAVE_SYS_SELECT_H
00071 #include <sys/select.h>
00072 #endif
00073 
00074 #ifdef HAVE_STRING_H
00075 #include <string.h>
00076 #endif
00077 
00078 #include <stdlib.h>
00079 #include <ctype.h>
00080 #include <errno.h>
00081 
00082 #include "newclient.h"
00083 #include "soundsdef.h"
00084 
00085 
00086 #if defined(ALSA_SOUND)
00087 #  include <sys/asoundlib.h>
00088 #  define AUDIODEV "/dev/dsp"
00089     snd_pcm_t *handle=NULL;
00090 #elif defined(OSS_SOUND)
00091 #  include <sys/soundcard.h>
00092 #  define AUDIODEV "/dev/dsp"
00093 #elif defined(SGI_SOUND)
00094 #  include <audio.h>
00095 #  define AUDIODEV "/foo/bar"
00096 #elif defined(SUN_SOUND)
00097 #  include <sys/audioio.h>
00098 #  define AUDIODEV "/dev/audio"
00099 #else
00100 #error Not known sound system defined
00101 #endif
00102 
00103 #define CONFIG_FILE "/.crossfire/sndconfig"
00104 #define MAX_SOUNDS 1024
00105 
00106 /*
00107  * A replacement of strdup(), since it's not defined at some
00108  * unix variants.
00109  */
00110 
00111 char *strdup_local(char *str) {
00112   char *c=(char *)malloc(sizeof(char)*strlen(str)+1);
00113   strcpy(c,str);
00114   return c;
00115 }
00116 
00117 
00118 
00119 typedef struct Sound_Info {
00120     char *filename;
00121     char *symbolic;
00122     unsigned char volume;
00123     int size;
00124     unsigned char *data;
00125 } Sound_Info;
00126 
00127 Sound_Info normal_sounds[MAX_SOUNDS], spell_sounds[MAX_SOUNDS],
00128 default_normal, default_spell;
00129 
00130 #define SOUND_DECREASE 0.1
00131 
00132 /* mixer variables */
00133 char *buffers=NULL;
00134 int *sounds_in_buffer=NULL;
00135 int current_buffer=0; /* Next buffer we will write out */
00136 int first_free_buffer=0; /* So we know when to stop playing sounds */
00137 
00138 int soundfd=0;
00139 
00140 /* sound device parameters */
00141 int stereo=0,bit8=0,sample_size=0,frequency=0,sign=0,zerolevel=0;
00142 
00143 #ifdef SUN_SOUND
00144 struct sound_settings{
00145     int stereo, bit8, sign, frequency, buffers, buflen,simultaneously;
00146     const char *audiodev;
00147 } settings={0,1,1,11025,100,4096,4,AUDIODEV};
00148 
00149 #else
00150 
00151 struct sound_settings{
00152     int stereo, bit8, sign, frequency, buffers, buflen,simultaneously;
00153     const char *audiodev;
00154 } settings={0,1,0,11025,100,1024,4,AUDIODEV};
00155 
00156 #endif
00157 
00158 /* parses a line from the sound file.  This is a little uglier because
00159  * we store some static values in the function so we know what we are doing -
00160  * however, it is somewhat necessary so that we can use this same function
00161  * to parse both files and the compiled in data.
00162  *
00163  * Note that this function will modify the data in line.  lineno is just
00164  * for error tracking purposes.
00165  */
00166 
00167 static void parse_sound_line(char *line, int lineno) {
00168     static int readtype=0, lastnum=0;
00169     int newnum, len;
00170     char *cp,*volume,*symbolic,*cp1,filename[512];
00171 
00172     if (line[0]=='#' || line[0]=='\n') return;
00173 
00174     if (!strcmp(line,"Standard Sounds:\n")) {
00175         lastnum=0;
00176         readtype=1;
00177         return;
00178     }
00179     if (!strcmp(line,"Spell Sounds:\n")) {
00180         lastnum=0;
00181         readtype=2;
00182         return;
00183     }
00184     if (!readtype) {
00185 #ifdef SOUND_DEBUG
00186         fprintf(stderr,"Got input without finding section header yet:\n%d:%s\n",
00187                 lineno, line);
00188 #endif
00189         return;
00190     }
00191 
00192     if (line[strlen(line)-1]=='\n') line[strlen(line)-1]='\0';
00193 
00194     len=strcspn(line, " \t");
00195     line[len]='\0';
00196     cp = line+len+1;
00197 
00198 
00199     /* Skip all whitespace for the next field */
00200     while (*cp!='\0' && (*cp==' ' || *cp=='\t'))
00201         cp++;
00202 
00203     volume=cp;
00204 
00205     /* No symbolic name or number - that is ok */
00206     cp1=cp;
00207     if (!(cp=strchr(cp1,' ')) && !(cp=strchr(cp1,'\t'))) {
00208         newnum=lastnum+1;
00209         symbolic=NULL;
00210     } else {    /* We think we have a symbolic name */
00211         /* Don't need to nulterm the volume, since we atoi it anyways */
00212         while (*cp!='\0' && (*cp==' ' || *cp=='\t'))
00213             cp++;
00214 
00215         symbolic=cp;
00216         /* Some symbolc names are double quote protected.  If, do some
00217          * special processing.  We strip off the quotes.
00218          */
00219         if (*symbolic=='"') {
00220             symbolic++;
00221             for (cp=symbolic; *cp!='\0' && *cp!='"'; cp++) ;
00222             *cp='\0';
00223             cp++;
00224         }
00225         /* Lets try to find the sound number now */
00226         cp1 = cp;
00227         if (!(cp=strchr(cp1,' '))  && !(cp=strchr(cp1,'\t')))
00228             newnum=lastnum+1;
00229         else {
00230             *cp++='\0';
00231             while (*cp!='\0' && (*cp==' ' || *cp=='\t'))
00232                 cp++;
00233             if (isdigit(*cp))
00234                 newnum=atoi(cp);
00235             else newnum=lastnum+1;
00236         }
00237     }
00238     if (newnum < 0 || newnum>MAX_SOUNDS) {
00239         fprintf(stderr,"Invalid sound number %d, line %d, buf %s\n",
00240                 newnum, lineno, line);
00241         return;
00242     }
00243 
00244     /* Compatibility processing for older files - if the file ends in
00245      * .au, convert to .raw.  A bit of a hack, but probably better than
00246      * trying to play an au file.
00247      */
00248     strcpy(filename, line);
00249     cp = filename + strlen(filename)-3;
00250     if (!strcmp(cp, ".au"))
00251         strcpy(cp, ".raw");
00252 
00253     if (symbolic && !strcmp(symbolic,"DEFAULT")) {
00254         if (readtype==1) {
00255             default_normal.filename=strdup_local(filename);
00256             default_normal.volume=atoi(volume);
00257         } else if (readtype==2) {
00258             default_spell.filename=strdup_local(filename);
00259             default_spell.volume=atoi(volume);
00260         }
00261         return;
00262     }
00263     else {
00264         if (readtype==1) {
00265             normal_sounds[newnum].filename = strdup_local(filename);
00266             normal_sounds[newnum].volume = atoi(volume);
00267             if (symbolic) normal_sounds[newnum].symbolic=strdup_local(symbolic);
00268             else normal_sounds[newnum].symbolic=NULL;
00269         } else if (readtype==2) {
00270             spell_sounds[newnum].filename = strdup_local(filename);
00271             spell_sounds[newnum].volume = atoi(volume);
00272             if (symbolic) spell_sounds[newnum].symbolic=strdup_local(symbolic);
00273             else spell_sounds[newnum].symbolic=NULL;
00274         }
00275         lastnum=newnum;
00276     }
00277 }
00278 
00279 #if defined(ALSA_SOUND)
00280 int init_audio(){
00281 
00282     int card=0,device=0,err;
00283     snd_pcm_channel_params_t params;
00284 
00285     printf("cfsndserv compiled for ALSA sound system\n");
00286     fflush(stdout);
00287 
00288     if ( (err = snd_pcm_open( &handle, card, device, SND_PCM_OPEN_PLAYBACK )) <0 ) {
00289         fprintf( stderr, "open failed: %s\n", snd_strerror( err ) );
00290         return -1;
00291     }
00292 
00293     params.channel = SND_PCM_CHANNEL_PLAYBACK;
00294     params.mode = SND_PCM_MODE_BLOCK;
00295 
00296     if (settings.bit8)
00297         params.format.format = settings.sign?SND_PCM_SFMT_S8:SND_PCM_SFMT_U8;
00298     else
00299         params.format.format = settings.sign?SND_PCM_SFMT_S16_LE:SND_PCM_SFMT_U16_LE;
00300 
00301      params.format.rate = settings.frequency;
00302      params.format.voices = settings.stereo?2:1;
00303      params.buf.block.frag_size = settings.buflen/2;
00304      params.buf.block.frags_max = 2;
00305      params.buf.block.frags_min = 1;
00306 
00307      if ( (err = snd_pcm_channel_params( handle, &params )) < 0 ) {
00308         fprintf( stderr, "format setup failed: %s\nTrying defaults\n"
00309                                  , snd_strerror( err ) );
00310         params.format.format = SND_PCM_SFMT_U8;
00311         params.format.rate = 11025;
00312         params.format.voices = 1;
00313         if ( (err = snd_pcm_channel_params( handle, &params )) < 0 ) {
00314             fprintf( stderr, "format setup failed: %s\n", snd_strerror( err ) );
00315             snd_pcm_close( handle );
00316             return -1;
00317         }
00318      }
00319      switch(params.format.format){
00320         case SND_PCM_SFMT_S8:
00321            bit8=1;
00322            sign=1;
00323            break;
00324         case SND_PCM_SFMT_U8:
00325            bit8=1;
00326            sign=0;
00327            break;
00328         case SND_PCM_SFMT_S16_LE:
00329            bit8=0;
00330            sign=1;
00331            break;
00332         case SND_PCM_SFMT_U16_LE:
00333            bit8=0;
00334            sign=0;
00335            break;
00336         default:
00337            fprintf(stderr,"Coulnd't set proper format\n");
00338            return -1;
00339      }
00340 
00341      sample_size=params.format.voices*(bit8?1:2);
00342      stereo=(params.format.voices==1)?0:1;
00343      frequency=params.format.rate;
00344 
00345      soundfd=snd_pcm_file_descriptor(handle,SND_PCM_CHANNEL_PLAYBACK);
00346      snd_pcm_nonblock_mode( handle, 1 );
00347 
00348      return 0;
00349 }
00350 
00351 int audio_play(int buffer,int off){
00352 
00353   return snd_pcm_write(handle,buffers+settings.buflen*buffer+off,settings.buflen-off);
00354 }
00355 
00356 #elif defined(OSS_SOUND)
00357 
00358 int init_audio(void){
00359 
00360   const char *audiodev;
00361   int value,format,tmp;
00362 
00363   printf("cfsndserv compiled for OSS sound system\n");
00364   fflush(stdout);
00365 
00366   /* Open the audio device */
00367   if ( (audiodev=getenv("AUDIODEV")) == NULL ) {
00368           audiodev = settings.audiodev;
00369   }
00370   soundfd = open(audiodev, (O_WRONLY|O_NONBLOCK), 0);
00371   if ( soundfd < 0 ) {
00372            fprintf(stderr,"Couldn't open %s: %s\n", audiodev, strerror(errno));                return(-1);
00373   }
00374 
00375   /* Set the audio buffering parameters */
00376   value=0;
00377   for(tmp=settings.buflen/2;tmp;tmp>>=1) value++;
00378 
00379   value |= 0x00020000;
00380   if ( ioctl(soundfd, SNDCTL_DSP_SETFRAGMENT, &value) < 0 ) {
00381             fprintf(stderr,"Couldn't set audio fragment spec\n");
00382             return(-1);
00383   }
00384   if (settings.bit8)
00385     format=settings.sign?AFMT_S8:AFMT_U8;
00386   else
00387     format=settings.sign?AFMT_S16_LE:AFMT_U16_LE;
00388 
00389   value=format;
00390   if ( (ioctl(soundfd, SNDCTL_DSP_SETFMT,&value) < 0) ||
00391                                             (value != format) ) {
00392              fprintf(stderr,"Couldn't set audio format\n");
00393   }
00394 
00395   switch(value){
00396      case AFMT_S16_LE:
00397          bit8=0;
00398          sign=1;
00399          break;
00400      case AFMT_U16_LE:
00401          bit8=0;
00402          sign=0;
00403          break;
00404      case AFMT_S8:
00405          bit8=1;
00406          sign=1;
00407          break;
00408      case AFMT_U8:
00409          bit8=1;
00410          sign=0;
00411          break;
00412      default:
00413          return -1;
00414   }
00415 
00416   stereo = settings.stereo;
00417   ioctl(soundfd, SNDCTL_DSP_STEREO, &stereo);
00418 
00419   frequency = settings.frequency;
00420   if ( ioctl(soundfd, SNDCTL_DSP_SPEED, &frequency) < 0 ) {
00421           fprintf(stderr,"Couldn't set audio frequency\n");
00422           return(-1);
00423   }
00424   sample_size=(bit8?1:2)*(stereo?2:1);
00425   return 0;
00426 }
00427 
00428 int audio_play(int buffer,int off){
00429     int wrote;
00430 #ifdef SOUND_DEBUG
00431     printf("audio play - writing starting at %d, %d bytes",
00432           settings.buflen*buffer+off,settings.buflen-off);
00433     fflush(stdout);
00434 #endif
00435   wrote=write(soundfd,buffers+settings.buflen*buffer+off,settings.buflen-off);
00436 #ifdef SOUND_DEBUG
00437     printf("...wrote %d bytes\n", wrote);
00438     fflush(stdout);
00439 #endif
00440   return wrote;
00441 }
00442 /* End of OSS sound */
00443 
00444 #elif defined(SGI_SOUND)
00445 
00446 ALconfig        soundconfig;
00447 ALport  soundport;
00448 
00449 int init_audio()
00450 {
00451         long            params[2];
00452 
00453         printf("cfsndserv compiled for SGI sound system\n");
00454         fflush(stdout);
00455 
00456         /* Allocate ALconfig structure. */
00457 
00458         if ((soundconfig=ALnewconfig())==0)
00459         {
00460                 fprintf(stderr,"Could not allocate ALconfig structure.\n");
00461                 return -1;
00462         }
00463 
00464         /* Set number of channels */
00465 
00466         if (ALsetchannels(soundconfig,(stereo=settings.stereo)?2:1)==-1)
00467         {
00468                 fprintf(stderr,"Could not set number of channels.\n");
00469                 return -1;
00470         }
00471 
00472         /* Set sample format */
00473 
00474         if (ALsetsampfmt(soundconfig,AL_SAMPFMT_TWOSCOMP)==-1)
00475         {
00476                 fprintf(stderr,"Could not set audio sample format.\n");
00477                 return -1;
00478         }
00479         sign=1;
00480 
00481         /* Set sample width */
00482 
00483         if (ALsetwidth(soundconfig,(bit8=settings.bit8)?AL_SAMPLE_8:AL_SAMPLE_16)==-1)
00484         {
00485                 fprintf(stderr,"Could not set audio sample width.\n");
00486                 return -1;
00487         }
00488         sample_size=(stereo?2:1)*(bit8?1:2);
00489 
00490         /* Set frequency */
00491 
00492         params[0]=AL_OUTPUT_RATE;
00493         params[1]=frequency=settings.frequency;
00494         if (ALsetparams(AL_DEFAULT_DEVICE,params,2)==-1)
00495         {
00496                 fprintf(stderr,"Could not set output rate of default device.\n");
00497                 return -1;
00498         }
00499 
00500         /* Open audio port */
00501 
00502         if ((soundport=ALopenport("cfsndserv port","w",soundconfig))==NULL)
00503         {
00504                 fprintf(stderr,"Could not open audio port.\n");
00505                 return -1;
00506         }
00507         soundfd=ALgetfd(soundport);
00508         return 0;
00509 }
00510 
00511 int audio_play(int buffer,int off)
00512 {
00513         ALwritesamps(soundport,buffers+settings.buflen*buffer+off,(settings.buflen-off)/sample_size);
00514         return settings.buflen-off;
00515 }
00516 
00517 #elif defined(SUN_SOUND)
00518 
00519 int init_audio(){
00520 
00521   const char *audiodev;
00522   int value,format,tmp;
00523   audio_info_t  audio_info;
00524   audio_device_t audio_device;
00525 
00526   printf("cfsndserv compiled for SUN sound system\n");
00527   fflush(stdout);
00528 
00529   /* Open the audio device */
00530   if ( (audiodev=getenv("AUDIODEV")) == NULL ) {
00531           audiodev = settings.audiodev;
00532   }
00533   soundfd = open(audiodev, (O_WRONLY|O_NONBLOCK), 0);
00534   if ( soundfd < 0 ) {
00535            fprintf(stderr,"Couldn't open %s: %s\n", audiodev, strerror(errno));                return(-1);
00536   }
00537 
00538   if (ioctl(soundfd, AUDIO_GETDEV, &audio_device) < 0) {
00539     fprintf(stderr,"Couldn't get audio device ioctl\n");
00540     return(-1);
00541   }
00542   if ( ioctl(soundfd, AUDIO_GETINFO, &audio_info) < 0 ) {
00543     fprintf(stderr,"Couldn't get audio information ioctl\n");
00544     return(-1);
00545   }
00546   /* The capabilities on different sun hardware vary wildly.
00547    * We attempt to get a working setup no matter what hardware we are
00548    * running on.
00549    */
00550 
00551    /* This is sparc 10, sparc 20 class systems */
00552    if (!strcmp(audio_device.name, "SUNW,dbri")) {
00553         /* To use linear encoding, we must use 16 bit and some fixed
00554          * frequencies.  11025 matches what the rest of the systems use
00555          */
00556         audio_info.play.precision = 16;
00557         audio_info.play.encoding=AUDIO_ENCODING_LINEAR;
00558         audio_info.play.sample_rate=11025;
00559    }
00560     /* This is used on many of the ultra machines */
00561    else if (!strcmp(audio_device.name, "SUNW,CS4231")) {
00562         /* To use linear encoding, we must use 16 bit and some fixed
00563          * frequencies.  11025 matches what the rest of the systems use
00564          */
00565         audio_info.play.precision = 16;
00566         audio_info.play.encoding=AUDIO_ENCODING_LINEAR;
00567         audio_info.play.sample_rate=11025;
00568    }
00569 
00570   audio_info.play.channels = settings.stereo?2:1;
00571   stereo= settings.stereo;
00572 
00573   bit8=(audio_info.play.precision==8)?1:0;
00574   frequency=settings.frequency;
00575   sample_size=(bit8?1:2)*(stereo?2:1);
00576   fprintf(stderr,"SUN_SOUND: bit8=%d, stereo=%d, freq=%d, sample_size=%d\n",
00577           bit8, stereo, frequency, sample_size);
00578 
00579   if ( ioctl(soundfd, AUDIO_SETINFO, &audio_info) < 0 ) {
00580             perror("Couldn't set audio information ioctl");
00581             return(-1);
00582   }
00583   return 0;
00584 }
00585 
00586 int audio_play(int buffer,int off){
00587     int wrote;
00588 #ifdef SOUND_DEBUG
00589     printf("audio play - writing starting at %d, %d bytes",
00590           settings.buflen*buffer+off,settings.buflen-off);
00591     fflush(stdout);
00592 #endif
00593   wrote=write(soundfd,buffers+settings.buflen*buffer+off,settings.buflen-off);
00594 #ifdef SOUND_DEBUG
00595     printf("...wrote %d bytes\n", wrote);
00596     fflush(stdout);
00597 #endif
00598   return wrote;
00599 }
00600 /* End of Sun sound */
00601 
00602 #endif
00603 
00604 
00605 
00606 /* init_sounds open the audio device, and reads any configuration files
00607  * that need to be.  It returns 0 on success.  On failure, the calling
00608  * function will likely disable sound support/requests from the server.
00609  */
00610 
00611 int init_sounds(void)
00612 {
00613     int i;
00614     FILE *fp;
00615     char path[256], buf[512];
00616 
00617 #ifdef SOUND_DEBUG
00618     fprintf( stderr,"Settings: bits: %i, ",settings.bit8?8:16);
00619     fprintf( stderr,"%s, ",settings.sign?"signed":"unsigned");
00620     fprintf( stderr,"%s, ",settings.stereo?"stereo":"mono");
00621     fprintf( stderr,"frequency: %i, ",settings.frequency);
00622     fprintf( stderr,"device: %s\n",settings.audiodev);
00623 #endif
00624 
00625     buffers = (char *)malloc( settings.buffers * settings.buflen );
00626     if ( !buffers ) return -1;
00627     sounds_in_buffer = (int *)calloc( settings.buffers,sizeof(int) );
00628     if ( !sounds_in_buffer ) return -1;
00629 
00630     if (init_audio()) return -1;
00631 
00632     if (sign) zerolevel=0;
00633     else zerolevel=bit8?0x80:0x8000;
00634 
00635     memset(buffers,zerolevel,settings.buflen*settings.buffers);
00636 
00637 #ifdef SOUND_DEBUG
00638     fprintf( stderr,"bits: %i, ",bit8?8:16);
00639     fprintf( stderr,"%s, ",sign?"signed":"unsigned");
00640     fprintf( stderr,"%s, ",stereo?"stereo":"mono");
00641     fprintf( stderr,"freq: %i, ",frequency);
00642     fprintf( stderr,"smpl_size: %i, ",sample_size);
00643     fprintf( stderr,"0level: %i\n",zerolevel);
00644 #endif
00645 
00646     for (i=0; i<MAX_SOUNDS; i++) {
00647         normal_sounds[i].filename=NULL;
00648         spell_sounds[i].filename=NULL;
00649         normal_sounds[i].size=-1;
00650         spell_sounds[i].size=-1;
00651     }
00652     default_normal.filename=NULL;
00653     default_spell.filename=NULL;
00654 
00655     sprintf(path,"%s/.crossfire/sounds", getenv("HOME"));
00656     i=0;
00657     if (!(fp=fopen(path,"r"))) {
00658         fprintf(stderr,"Unable to open %s - will use built in defaults\n", path);
00659         for (; i<sizeof(def_sounds)/sizeof(char*); i++) {
00660             strcpy(buf, def_sounds[i]);
00661             parse_sound_line(buf,i);
00662         }
00663     } else while (fgets(buf, 511, fp)!=NULL) {
00664         buf[511]='\0';
00665         parse_sound_line(buf, ++i);
00666     }
00667     /* Note in both cases below, we leave the symbolic name untouched. */
00668     for (i=0; i<MAX_SOUNDS; i++) {
00669         if (!normal_sounds[i].filename) {
00670             normal_sounds[i].filename=default_normal.filename;
00671             normal_sounds[i].volume=default_normal.volume;
00672         }
00673         if (!spell_sounds[i].filename) {
00674             spell_sounds[i].filename=default_spell.filename;
00675             spell_sounds[i].volume=default_spell.volume;
00676         }
00677         normal_sounds[i].data=NULL;
00678         spell_sounds[i].data=NULL;
00679     }
00680     return 0;
00681 }
00682 
00683 /* Plays sound 'soundnum'.  soundtype is 0 for normal sounds, 1 for
00684  * spell_sounds.  This might get extended in the future.  x,y are offset
00685  * (assumed from player) to play sound.  This information is used to
00686  * determine value and left vs right speaker balance.
00687  * This doesn't really play a sound, rather it just addes it to
00688  * the buffer to be played later on.
00689  */
00690 
00691 static void play_sound(int soundnum, int soundtype, int x, int y)
00692 {
00693     Sound_Info *si;
00694     int buf,off;
00695     int i;
00696     unsigned left_ratio,right_ratio;
00697     double dist;
00698 
00699     buf=current_buffer;
00700     if (buf>=settings.buffers) buf=1;
00701 
00702     if (buf == 0) buf++;
00703 
00704     /* check if the buffer isn't full */
00705 #ifdef SOUND_DEBUG
00706     fprintf(stderr,"Sounds in buffer %i: %i\n",buf,sounds_in_buffer[buf]);
00707 #endif
00708     if (sounds_in_buffer[buf]>settings.simultaneously) return;
00709 
00710     if (soundnum>=MAX_SOUNDS || soundnum<0) {
00711         fprintf(stderr,"Invalid sound number: %d\n", soundnum);
00712         return;
00713     }
00714     if (soundfd==-1) {
00715         fprintf(stderr,"Sound device is not open\n");
00716         return;
00717     }
00718 
00719     if (soundtype < SOUND_NORMAL || soundtype == 0) soundtype = SOUND_NORMAL;
00720 
00721     if (soundtype==SOUND_NORMAL) {
00722         si = &normal_sounds[soundnum];
00723     }
00724     else if (soundtype==SOUND_SPELL) {
00725         si = &spell_sounds[soundnum];
00726     }
00727     else {
00728         fprintf(stderr,"Unknown soundtype: %d\n", soundtype);
00729         return;
00730     }
00731 
00732     if (!si->filename) {
00733         fprintf(stderr,"Sound %d (type %d) is not defined\n", soundnum, soundtype);
00734         return;
00735     }
00736 
00737     /*
00738      *   Load the sound if it is not loaded yet.
00739      *
00740      */
00741     if (!si->data){
00742        FILE *f;
00743        struct stat sbuf;
00744 #ifdef SOUND_DEBUG
00745        fprintf(stderr,"Loading file: %s\n",si->filename);
00746 #endif
00747        if (stat(si->filename,&sbuf)){
00748            perror(si->filename);
00749            return;
00750        }
00751        si->size=sbuf.st_size;
00752        if (si->size <=0 ) return;
00753        if (si->size*sample_size > settings.buflen*(settings.buffers-1) ){
00754           fprintf(stderr,"Sound %s too long (%i > %i)\n",si->filename,si->size,
00755                     settings.buflen*(settings.buffers-1)/sample_size);
00756           return;
00757        }
00758        si->data=(unsigned char *)malloc(si->size);
00759        f=fopen(si->filename,"r");
00760        if (!f){
00761            perror(si->filename);
00762            return;
00763        }
00764        fread(si->data,1,si->size,f);
00765        fclose(f);
00766     }
00767 
00768 #ifdef SOUND_DEBUG
00769     fprintf(stderr,"Playing sound %i (%s), volume %i, x,y=%d,%d\n",soundnum,si->symbolic,si->volume,x,y);
00770 #endif
00771     /* calculate volume multiplers */
00772     dist=sqrt(x*x+y*y);
00773     right_ratio=left_ratio=((1<<16)*si->volume)/(100*settings.simultaneously*(1+SOUND_DECREASE*dist));
00774     if (stereo){
00775       double diff;
00776       if (dist)
00777         diff=(1.0-fabs((double)x/dist));
00778       else
00779         diff=1;
00780 #ifdef SOUND_DEBUG
00781       printf("diff: %f\n",diff);
00782       fflush(stdout);
00783 #endif
00784       if (x<0) right_ratio*=diff;
00785       else left_ratio*=diff;
00786     }
00787 
00788 #ifdef SOUND_DEBUG
00789     fprintf(stderr,"Ratio: %i, %i\n",left_ratio,right_ratio);
00790 #endif
00791 
00792     /* insert the sound to the buffers */
00793     sounds_in_buffer[buf]++;
00794     off=0;
00795     for(i=0;i<si->size;i++){
00796         int dat=si->data[i]-0x80;
00797 
00798         if (bit8){
00799           if (!stereo){
00800              buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00801           }
00802           else{
00803             buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00804             buffers[buf*settings.buflen+off+1]+=(dat*right_ratio)>>16;
00805           }
00806         }
00807         else{ /* 16 bit output */
00808           if (!stereo){
00809 #ifdef WORDS_BIGENDIAN
00810              buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff;
00811              buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00812           }
00813           else{
00814             buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff;
00815             buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00816             buffers[buf*settings.buflen+off+3]+=((dat*right_ratio)>>8)&0xff;
00817             buffers[buf*settings.buflen+off+2]+=(dat*right_ratio)>>16;
00818           }
00819 #else
00820              buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff;
00821              buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16;
00822           }
00823           else{
00824             buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff;
00825             buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16;
00826             buffers[buf*settings.buflen+off+2]+=((dat*right_ratio)>>8)&0xff;
00827             buffers[buf*settings.buflen+off+3]+=(dat*right_ratio)>>16;
00828           }
00829 #endif
00830         }
00831 
00832         off+=sample_size;
00833 
00834         if (off>=settings.buflen){
00835           off=0;
00836           buf++;
00837           if (buf>=settings.buffers) {
00838             buf=0;
00839           }
00840         }
00841     }
00842 #ifdef SOUND_DEBUG
00843     fprintf(stderr,"Added %d bytes, last buffer=%d, lastpos=%d\n",
00844             si->size, buf, off);
00845 #endif
00846     /* This write did not wrap the buffers */
00847     if (buf+1 > current_buffer) {
00848         if ((buf+1 > first_free_buffer) && (first_free_buffer >= current_buffer))
00849             first_free_buffer = buf+1;
00850     } else {    /* Buffers did wrap */
00851         if (((buf+1 > first_free_buffer) && (first_free_buffer < current_buffer)) ||
00852             (first_free_buffer >= current_buffer))
00853                 first_free_buffer = buf+1;
00854     }
00855     if (first_free_buffer >= settings.buffers) first_free_buffer=0;
00856 
00857 }
00858 
00859 int SoundCmd(unsigned char *data,  int len)
00860 {
00861     int x, y, num, type;
00862     int i;
00863 
00864     i=sscanf((char *)data,"%x %x %x %x",&num,&type,&x,&y);
00865     if (i!=4){
00866         fprintf(stderr,"Wrong input!\n");
00867         return -1;
00868     }
00869 #ifdef SOUND_DEBUG
00870     fprintf(stderr,"Playing sound %d (type %d), offset %d, %d\n",
00871             num, type, x ,y);
00872 #endif
00873     play_sound(num, type, x, y);
00874     return 0;
00875 }
00876 
00877 int write_settings(void) {
00878 FILE *f;
00879 char *home;
00880 char *path;
00881 
00882   if ( (home=getenv("HOME")) == NULL ) return -1;
00883   path=(char *)malloc(strlen(home)+strlen(CONFIG_FILE)+1);
00884   if (!path) return -1;
00885   strcpy(path,home);
00886   strcat(path,CONFIG_FILE);
00887   f=fopen(path,"w");
00888   if (!f) return -1;
00889   fprintf(f,"# Crossfire sound server settings\n");
00890   fprintf(f,"# Please note, that not everything will work\n\n");
00891   fprintf(f,"stereo: %i\n",settings.stereo);
00892   fprintf(f,"bits: %i\n",settings.bit8?8:16);
00893   fprintf(f,"signed: %i\n",settings.sign);
00894   fprintf(f,"frequency: %i\n",settings.frequency);
00895   fprintf(f,"buffers: %i\n",settings.buffers);
00896   fprintf(f,"buflen: %i\n",settings.buflen);
00897   fprintf(f,"simultaneously: %i\n",settings.simultaneously);
00898 /*  fprintf(f,"device: %s\n",settings.audiodev);*/
00899   fclose(f);
00900   return 0;
00901 }
00902 
00903 int read_settings(void) {
00904     FILE *f;
00905     char *home;
00906     char *path;
00907     char linebuf[1024];
00908     if ( (home=getenv("HOME")) == NULL ) return 0;
00909 
00910     path=(char *)malloc(strlen(home)+strlen(CONFIG_FILE)+1);
00911     if (!path) return 0;
00912 
00913     strcpy(path,home);
00914     strcat(path,CONFIG_FILE);
00915 
00916     f=fopen(path,"r");
00917     if (!f) return -1;
00918 
00919     while(fgets(linebuf,1023,f)!=NULL) {
00920         linebuf[1023]=0;
00921         /* Strip off the newline */
00922         linebuf[strlen(linebuf)-1]=0;
00923 
00924         if (strncmp(linebuf,"stereo:",strlen("stereo:"))==0)
00925             settings.stereo=atoi(linebuf+strlen("stereo:"))?1:0;
00926         else if (strncmp(linebuf,"bits:",strlen("bits:"))==0)
00927             settings.bit8=(atoi(linebuf+strlen("bits:"))==8)?1:0;
00928         else if (strncmp(linebuf,"signed:",strlen("signed:"))==0)
00929             settings.sign=atoi(linebuf+strlen("signed:"))?1:0;
00930         else if (strncmp(linebuf,"buffers:",strlen("buffers:"))==0)
00931             settings.buffers=atoi(linebuf+strlen("buffers:"));
00932         else if (strncmp(linebuf,"buflen:",strlen("buflen:"))==0)
00933             settings.buflen=atoi(linebuf+strlen("buflen:"));
00934         else if (strncmp(linebuf,"frequency:",strlen("frequency:"))==0)
00935             settings.frequency=atoi(linebuf+strlen("frequency:"));
00936         else if (strncmp(linebuf,"simultaneously:",strlen("simultaneously:"))==0)
00937             settings.simultaneously=atoi(linebuf+strlen("simultaneously:"));
00938 #if 0
00939         else if (strncmp(linebuf,"device: ",strlen("device: "))==0)
00940                 settings.audiodev=strdup_local(linebuf+strlen("device: "));
00941 #endif
00942     }
00943     fclose(f);
00944     return 0;
00945 }
00946 
00947 int main(int argc, char *argv[])
00948 {
00949     int infd;
00950     char inbuf[1024];
00951     int inbuf_pos=0,sndbuf_pos=0;
00952     fd_set inset,outset;
00953 
00954     printf ("%s\n",rcsid_cfsndserv_c);
00955     fflush(stdout);
00956     if (read_settings()) write_settings();
00957     if (init_sounds()) return 1;
00958     /* we don't use the file descriptor method */
00959     if (!soundfd) return 1;
00960     infd=fileno(stdin);
00961     FD_ZERO(&inset);
00962     FD_ZERO(&outset);
00963     FD_SET(soundfd,&outset);
00964     FD_SET(infd,&inset);
00965     while(1){
00966 #if defined(SGI_SOUND)
00967       /*
00968       The buffer of an audio port can hold 100000 samples. If we allow sounds to
00969       be written to the port whenever there is enough room in the buffer, all
00970       sounds will be played sequentially, which is wrong. We can set the
00971       fillpoint to a high value to prevent this.
00972       */
00973       ALsetfillpoint(soundport,100000);
00974 #endif
00975 
00976 
00977       select(FD_SETSIZE,&inset,&outset,NULL,NULL);
00978 
00979       if (FD_ISSET(soundfd,&outset)){
00980          /* no sounds to play */
00981          if (current_buffer==first_free_buffer) FD_CLR(soundfd,&outset);
00982          else{
00983            int wrote;
00984            wrote=audio_play(current_buffer,sndbuf_pos);
00985            if (wrote<settings.buflen-sndbuf_pos) sndbuf_pos+=wrote;
00986            else{
00987              /* clean the buffer */
00988              memset(buffers+settings.buflen*current_buffer,zerolevel,settings.buflen);
00989              sounds_in_buffer[current_buffer]=0;
00990              sndbuf_pos=0;
00991              current_buffer++;
00992              if (current_buffer>=settings.buffers) current_buffer=0;
00993            }
00994         }
00995       } else {
00996         /* We need to reset this if it is not set - otherwise, we will never
00997          * finish playing the sounds
00998          */
00999         FD_SET(soundfd,&outset);
01000       }
01001 
01002       if (FD_ISSET(infd,&inset)){
01003         int err=read(infd,inbuf+inbuf_pos,1);
01004         if (err<1 && errno!=EINTR){
01005           if (err<0) perror("read");
01006           break;
01007         }
01008         if (inbuf[inbuf_pos]=='\n'){
01009           inbuf[inbuf_pos++]=0;
01010           if (!SoundCmd((unsigned char*)inbuf,inbuf_pos)) FD_SET(soundfd,&outset);
01011           inbuf_pos=0;
01012         }
01013         else{
01014           inbuf_pos++;
01015           if (inbuf_pos>=1024){
01016              fprintf(stderr,"Input buffer overflow!\n");
01017              inbuf_pos=0;
01018           }
01019         }
01020       }
01021       FD_SET(infd,&inset);
01022     }
01023 
01024     return 0;
01025 }