Crossfire Client, Branch
R11627
|
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(¶ms); 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 }