Crossfire Client, Branch  R11627
alsa9.c
Go to the documentation of this file.
00001 static char *rcsid_cfsndserv_c =
00002     "$Id: alsa9.c 6716 2007-06-27 18:57:31Z akirschbaum $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001-2005,  Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 
00026 /* This file is only used for alsa 0.9 and later sound.  Alsa sound code is
00027  * sufficiently different from the rest that trying to keep it common didn't
00028  * make much sense.
00029  */
00030 
00031 #define SOUND_DEBUG
00032 
00033 /* Debugs the actual writing of data - this generally isn't that interesting,
00034  * and generates a lot of messages which tends to obscure the more interesting
00035  * ones.
00036  */
00037 /*#define SOUND_DEBUG_WRITES */
00038 
00039 #include <config.h>
00040 
00041 #include <stdio.h>
00042 #include <sys/types.h>
00043 #include <sys/stat.h>
00044 #include <time.h>
00045 
00046 #ifdef HAVE_FCNTL_H
00047 #include <fcntl.h>
00048 #endif
00049 
00050 #ifdef HAVE_UNISTD_H
00051 #include <unistd.h>
00052 #endif
00053 
00054 #include <math.h>
00055 
00056 #ifdef HAVE_SYS_IOCTL_H
00057 #include <sys/ioctl.h>
00058 #endif
00059 
00060 #ifdef HAVE_SYS_SELECT_H
00061 #include <sys/select.h>
00062 #endif
00063 
00064 #ifdef HAVE_STRING_H
00065 #include <string.h>
00066 #endif
00067 
00068 #include <stdlib.h>
00069 #include <ctype.h>
00070 #include <errno.h>
00071 
00072 #include "newclient.h"
00073 #include "soundsdef.h"
00074 
00075 
00076 #include <alsa/asoundlib.h>
00077 #include <alsa/pcm_plugin.h>
00078 
00079 
00080 #define ALSA_PCM_NEW_HW_PARAMS_API
00081 #define AUDIODEV "default:0,0"
00082 snd_pcm_t *handle = NULL;
00083 int sndbuf_pos=0;
00084 
00085 #define ALSA9_ERROR(str,err) { \
00086                 fprintf(stderr,"ALSA9 Error: %s %s\n",str,snd_strerror(err)); }
00087 
00088 #define CONFIG_FILE "/.crossfire/sndconfig"
00089 
00090 
00091 #define SOUND_DECREASE 0.1
00092 
00093 /* mixer variables */
00094 int *sounds_in_buffer=NULL;
00095 int current_buffer=0; /* Next buffer we will write out */
00096 int first_free_buffer=0; /* So we know when to stop playing sounds */
00097 
00098 int soundfd=0;
00099 
00100 /* sound device parameters */
00101 int stereo=0,sample_size=0,frequency=0,sign=0,zerolevel=0;
00102 
00103 struct sound_settings{
00104     int stereo, bit8, sign, frequency, buffers, buflen,simultaneously;
00105     const char *audiodev;
00106 } settings={0,1, 0, 8000,200,2048,4,AUDIODEV};
00107 
00108 #include "common.c"
00109 
00110 
00111 snd_pcm_hw_params_t *params;
00112 static snd_pcm_uframes_t chunk_size = 0;
00113 int err = 0;
00114 
00115 void alsa_audio_close(void) { snd_pcm_close(handle); }
00116 
00117 int init_audio(void) {
00118     snd_pcm_sw_params_t *sw_params;
00119     unsigned int format;
00120 
00121     printf("cfsndserv compiled for ALSA9 sound system\n");
00122     fflush(stdout);
00123 
00124     /* open the PCM device */
00125     if ((err = snd_pcm_open(&handle,AUDIODEV,SND_PCM_STREAM_PLAYBACK,0)) <0) {
00126         ALSA9_ERROR("init_audio(): ",err);
00127         exit(1);
00128     }
00129 
00130     atexit(alsa_audio_close);
00131 
00132     /* allocate and zero out params */
00133     snd_pcm_hw_params_alloca(&params);
00134 
00135     if ((err = snd_pcm_hw_params_any(handle,params)) <0) {
00136         ALSA9_ERROR("init_audio(): ",err);
00137         exit(1);
00138     }
00139 
00140     /* set the access mode (interleaved) */
00141     if ((err = snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED)) <0) {
00142         ALSA9_ERROR("init_audio(): ",err);
00143         exit(1);
00144     }
00145 
00146     /* set the format */
00147 
00148    if (settings.bit8)
00149        format = settings.sign?SND_PCM_FORMAT_S8:SND_PCM_FORMAT_U8;
00150    else
00151        format = SND_PCM_FORMAT_U16;
00152 
00153 
00154   if ((err = snd_pcm_hw_params_set_format(handle,params,format))<0) {
00155         ALSA9_ERROR("init_audio(): ",err);
00156         exit(1);
00157   }
00158 
00159   /* set the number of channels */
00160   if ((err = snd_pcm_hw_params_set_channels(handle,params,settings.stereo?2:1))<0) {
00161         ALSA9_ERROR("init_audio(): ",err);
00162         exit(1);
00163    }
00164 
00165   stereo = settings.stereo?1:0;
00166 
00167   /* set the rate (our frequency, or closest match) */
00168   unsigned int r = (unsigned int)settings.frequency;
00169   if (r == 0) r = 41100;
00170   int dir = 0;
00171   frequency = snd_pcm_hw_params_set_rate_near(handle,params,&r,&dir);
00172 
00173   /* get sample size */
00174   sample_size = (snd_pcm_format_physical_width(format) * (settings.stereo?2:1)) / 8;
00175   #ifdef SOUND_DEBUG
00176         printf("init_audio(): sample_size = %d\n",sample_size);
00177     fflush(stdout);
00178   #endif
00179 
00180 
00181     /* apply the settings */
00182     if ((err = snd_pcm_hw_params(handle,params))<0) {
00183         ALSA9_ERROR("init_audio(): ",err);
00184         exit(1);
00185     }
00186 
00187     err=snd_pcm_nonblock(handle, 1);
00188     if (err < 0) {
00189         ALSA9_ERROR("nonblock setting error: %s", err);
00190         exit(1);
00191     }
00192 
00193     if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
00194             fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
00195                                  snd_strerror (err));
00196             exit (1);
00197     }
00198     if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) {
00199             fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
00200                                  snd_strerror (err));
00201             exit (1);
00202     }
00203     if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, 4096)) < 0) {
00204             fprintf (stderr, "cannot set minimum available count (%s)\n",
00205                                  snd_strerror (err));
00206             exit (1);
00207     }
00208     if ((err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, 0U)) < 0) {
00209             fprintf (stderr, "cannot set start mode (%s)\n",
00210                                  snd_strerror (err));
00211             exit (1);
00212     }
00213     if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) {
00214             fprintf (stderr, "cannot set software parameters (%s)\n",
00215                                  snd_strerror (err));
00216             exit (1);
00217     }
00218 
00219     /* Zerolevel=0x80 seems to work best for both 8 and 16 bit audio */
00220     if (sign) zerolevel=0;
00221     else zerolevel=0x80;
00222 
00223     snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
00224 
00225     return 0;
00226 }
00227 
00228 void alsa_recover(int e) {
00229         /* Recover from various errors */
00230         if (e == -EAGAIN) {
00231             return;
00232         } else if (e == -EPIPE) {
00233                 err = snd_pcm_prepare(handle);
00234                 if (err < 0) {
00235                         ALSA9_ERROR("alsa_recover(): Unable to recover from underrun. ",err);
00236                         return;
00237                 }
00238         } else if (e == -ESTRPIPE) {
00239                 while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1);
00240                 if (err < 0) {
00241                         err = snd_pcm_prepare(handle);
00242                         if (err < 0) {
00243                                 ALSA9_ERROR("alsa_recover(): Unable to recover from suspend. ",err);
00244                                 return;
00245                         }
00246                 }
00247         } else ALSA9_ERROR("alsa_recover(): ",e);
00248 
00249 }
00250 
00251 
00252 
00253 /* Does the actual task of writing the data to the socket.
00254  * The ALSA write logic is a bit odd, in that the count you pass in
00255  * is not the number of bytes you are writing, but the number of
00256  * samples you are writing.  Thus, if you have stereo with 1 byte/channel,
00257  * you'd divide the number by 2.  If you have 16 bit audio with stereo,
00258  * you'd divide the number by 4.
00259  */
00260 int audio_play(int buffer, int off) {
00261     int count = (settings.buflen - off) / sample_size;
00262 
00263     if (count > chunk_size) count=chunk_size;
00264 
00265 #ifdef SOUND_DEBUG_WRITES
00266     printf("audio play - writing starting at %d, %d bytes, off=%d\n",
00267           settings.buflen*buffer+off,count, off);
00268     fflush(stdout);
00269 #endif
00270 
00271     err = snd_pcm_writei(handle,buffers+settings.buflen*buffer+off, count);
00272 
00273     if (err < 0) {
00274         alsa_recover(err);
00275         return 0;
00276     } else {
00277         return err * sample_size;
00278     }
00279 }
00280 
00281 
00282 
00283 
00284 
00285 
00286 
00287 /* Plays sound 'soundnum'.  soundtype is 0 for normal sounds, 1 for
00288  * spell_sounds.  This might get extended in the future.  x,y are offset
00289  * (assumed from player) to play sound.  This information is used to
00290  * determine value and left vs right speaker balance.
00291  * This doesn't really play a sound, rather it just addes it to
00292  * the buffer to be played later on.
00293  */
00294 
00295 void play_sound(int soundnum, int soundtype, int x, int y)
00296 {
00297     Sound_Info *si;
00298     int buf,off;
00299     int i;
00300     unsigned left_ratio,right_ratio;
00301     double dist;
00302 
00303     buf=current_buffer;
00304     if (buf>=settings.buffers) buf=1;
00305 
00306     if (buf == 0) buf++;
00307 
00308     /* check if the buffer isn't full */
00309 #ifdef SOUND_DEBUG
00310     fprintf(stderr,"Sounds in buffer %i: %i\n",buf,sounds_in_buffer[buf]);
00311 #endif
00312     if (sounds_in_buffer[buf]>settings.simultaneously) return;
00313 
00314     if (soundnum>=MAX_SOUNDS || soundnum<0) {
00315         fprintf(stderr,"Invalid sound number: %d\n", soundnum);
00316         return;
00317     }
00318 
00319     if (soundtype < SOUND_NORMAL || soundtype == 0) soundtype = SOUND_NORMAL;
00320 
00321     if (soundtype==SOUND_NORMAL) {
00322         si = &normal_sounds[soundnum];
00323     }
00324     else if (soundtype==SOUND_SPELL) {
00325         si = &spell_sounds[soundnum];
00326     }
00327     else {
00328         fprintf(stderr,"Unknown soundtype: %d\n", soundtype);
00329         return;
00330     }
00331 
00332     if (!si->filename) {
00333         fprintf(stderr,"Sound %d (type %d) is not defined\n", soundnum, soundtype);
00334         return;
00335     }
00336 
00337     /*
00338      *   Load the sound if it is not loaded yet.
00339      *
00340      */
00341     if (!si->data){
00342        FILE *f;
00343        struct stat sbuf;
00344 #ifdef SOUND_DEBUG
00345        fprintf(stderr,"Loading file: %s\n",si->filename);
00346 #endif
00347        if (stat(si->filename,&sbuf)){
00348            perror(si->filename);
00349            return;
00350        }
00351        si->size=sbuf.st_size;
00352        if (si->size <=0 ) return;
00353        if (si->size*sample_size > settings.buflen*(settings.buffers-1) ){
00354           fprintf(stderr,"Sound %s too long (%i > %i)\n",si->filename,si->size,
00355                     settings.buflen*(settings.buffers-1)/sample_size);
00356           return;
00357        }
00358        si->data=(unsigned char *)malloc(si->size);
00359        f=fopen(si->filename,"r");
00360        if (!f){
00361            perror(si->filename);
00362            return;
00363        }
00364        fread(si->data,1,si->size,f);
00365        fclose(f);
00366     }
00367 
00368     /* calculate volume multiplers */
00369     dist=sqrt(x*x+y*y);
00370 #ifdef SOUND_DEBUG
00371     fprintf(stderr,"Playing sound %i (%s), volume %i, x,y=%d,%d, dist=%f\n",soundnum,si->symbolic,si->volume,x,y, dist);
00372 #endif
00373     right_ratio=left_ratio=((1<<16)*si->volume)/(100*(1+SOUND_DECREASE*dist));
00374 
00375     if (stereo){
00376         double diff;
00377         if (dist)
00378             diff=(1.0-fabs((double)x/dist));
00379         else
00380             diff=1;
00381 #ifdef SOUND_DEBUG
00382         printf("diff: %f\n",diff);
00383         fflush(stdout);
00384 #endif
00385         if (x<0) right_ratio*=diff;
00386         else left_ratio*=diff;
00387     }
00388 
00389 #ifdef SOUND_DEBUG
00390     fprintf(stderr,"Ratio: %i, %i\n",left_ratio,right_ratio);
00391 #endif
00392 
00393     /* insert the sound to the buffers */
00394     sounds_in_buffer[buf]++;
00395     off=0;
00396     for(i=0;i<si->size;i++){
00397         int dat=si->data[i]-0x80;
00398         if (settings.bit8) {
00399           if (!stereo){
00400              buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00401           }
00402           else{
00403             buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00404             buffers[buf*settings.buflen+off+1]+=(dat*right_ratio)>>16;
00405           }
00406         }
00407         else{ /* 16 bit output */
00408           if (!stereo){
00409 #ifdef WORDS_BIGENDIAN
00410              buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff;
00411              buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00412           }
00413           else{
00414             buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff;
00415             buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
00416             buffers[buf*settings.buflen+off+3]+=((dat*right_ratio)>>8)&0xff;
00417             buffers[buf*settings.buflen+off+2]+=(dat*right_ratio)>>16;
00418           }
00419 #else
00420              buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff;
00421              buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16;
00422           }
00423           else{
00424             buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff;
00425             buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16;
00426             buffers[buf*settings.buflen+off+2]+=((dat*right_ratio)>>8)&0xff;
00427             buffers[buf*settings.buflen+off+3]+=(dat*right_ratio)>>16;
00428           }
00429 #endif
00430         }
00431 
00432         off+=sample_size;
00433 
00434         if (off>=settings.buflen){
00435           off=0;
00436           buf++;
00437           if (buf>=settings.buffers) {
00438             buf=0;
00439           }
00440         }
00441     }
00442 #ifdef SOUND_DEBUG
00443     fprintf(stderr,"Added %d bytes, last buffer=%d, lastpos=%d\n",
00444             si->size, buf, off);
00445 #endif
00446     /* This write did not wrap the buffers */
00447     if (buf+1 > current_buffer) {
00448         if ((buf+1 > first_free_buffer) && (first_free_buffer >= current_buffer))
00449             first_free_buffer = buf+1;
00450     } else {    /* Buffers did wrap */
00451         if (((buf+1 > first_free_buffer) && (first_free_buffer < current_buffer)) ||
00452             (first_free_buffer >= current_buffer))
00453                 first_free_buffer = buf+1;
00454     }
00455     if (first_free_buffer >= settings.buffers) first_free_buffer=0;
00456 
00457 
00458 }
00459 
00460 
00461 int main(int argc, char *argv[])
00462 {
00463     int infd;
00464     char inbuf[1024];
00465     int inbuf_pos=0, sfd, frames, maxframes;
00466     fd_set inset;
00467     struct timeval timeout;
00468 
00469     printf ("%s\n",rcsid_cfsndserv_c);
00470 
00471     fflush(stdout);
00472 
00473     if (read_settings()) write_settings();
00474     if (init_sounds()) return 1;
00475 
00476     infd=fileno(stdin);
00477     FD_ZERO(&inset);
00478     FD_SET(infd,&inset);
00479 
00480     /* need to know max amount of space */
00481     maxframes = snd_pcm_avail_update (handle);
00482 
00483     while(1){
00484         timeout.tv_sec=0;
00485         timeout.tv_usec=10000;
00486         FD_SET(infd,&inset);
00487 
00488         sfd = select(FD_SETSIZE,&inset,NULL,NULL,&timeout);
00489 
00490         /* ALSA9 doesn't provide us with an fd to use to make
00491          * sure we have space for writing.  So instead, we use
00492          * a timeout with select and if there is space, make send
00493          * more data.
00494          */
00495         frames = snd_pcm_avail_update (handle);
00496 /*      fprintf(stderr,"frames=%d\n", frames);*/
00497         while (((settings.buflen+frames)> maxframes) || (frames == -EPIPE)) {
00498 
00499             if (current_buffer != first_free_buffer) {
00500                 int wrote;
00501 
00502                 if (frames == -EPIPE)   snd_pcm_prepare(handle);
00503                 wrote = audio_play(current_buffer, sndbuf_pos);
00504 #ifdef SOUND_DEBUG_WRITES
00505                 printf("play_sound(): wrote %d\n",wrote);
00506                 fflush(stdout);
00507 #endif
00508                 if (wrote < settings.buflen-sndbuf_pos) {
00509                     sndbuf_pos+=wrote;
00510                 }
00511                 else{
00512                     /* clean the buffer */
00513                     memset(buffers+settings.buflen*current_buffer,zerolevel,settings.buflen);
00514                     sounds_in_buffer[current_buffer]=0;
00515                     sndbuf_pos=0;
00516                     current_buffer++;
00517                     if (current_buffer>=settings.buffers) current_buffer=0;
00518                 }
00519             } else
00520                 break;
00521             frames = snd_pcm_avail_update (handle);
00522         }
00523 
00524 
00525         if (sfd > 0) {
00526             if (FD_ISSET(infd,&inset)){
00527                 int err=read(infd,inbuf+inbuf_pos,1);
00528                 if (err<1 && errno!=EINTR){
00529                     if (err<0) perror("read");
00530                     break;
00531                 }
00532                 if (inbuf[inbuf_pos]=='\n'){
00533                     inbuf[inbuf_pos++]=0;
00534                     SoundCmd((unsigned char *)inbuf,inbuf_pos);
00535                     inbuf_pos=0;
00536                 }
00537                 else {
00538                     inbuf_pos++;
00539                     if (inbuf_pos>=1024){
00540                         fprintf(stderr,"Input buffer overflow!\n");
00541                         inbuf_pos=0;
00542                     }
00543                 }
00544             }
00545         }
00546     }
00547 
00548     return 0;
00549 }