|
Crossfire Client, Trunk
R18666
|
00001 static char *rcsid_sound_src_alsa9_c = 00002 "$Id: alsa9.c 13995 2010-10-14 04:01:47Z kbulgrien $"; 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 00032 #define SOUND_DEBUG 00033 00034 /* Debugs the actual writing of data - this generally isn't that interesting, 00035 * and generates a lot of messages which tends to obscure the more interesting 00036 * ones. 00037 */ 00038 /*#define SOUND_DEBUG_WRITES */ 00039 00040 #include <config.h> 00041 00042 #include <stdio.h> 00043 #include <sys/types.h> 00044 #include <sys/stat.h> 00045 #include <time.h> 00046 00047 #ifdef HAVE_FCNTL_H 00048 #include <fcntl.h> 00049 #endif 00050 00051 #ifdef HAVE_UNISTD_H 00052 #include <unistd.h> 00053 #endif 00054 00055 #include <math.h> 00056 00057 #ifdef HAVE_SYS_IOCTL_H 00058 #include <sys/ioctl.h> 00059 #endif 00060 00061 #ifdef HAVE_SYS_SELECT_H 00062 #include <sys/select.h> 00063 #endif 00064 00065 #ifdef HAVE_STRING_H 00066 #include <string.h> 00067 #endif 00068 00069 #include <stdlib.h> 00070 #include <ctype.h> 00071 #include <errno.h> 00072 00073 #include "newclient.h" 00074 #include "client-types.h" 00075 #include "def_sounds.h" 00076 #include "sndproto.h" 00077 #include "common.h" 00078 00079 #include <alsa/asoundlib.h> 00080 #include <alsa/pcm_plugin.h> 00081 00082 00083 #define ALSA_PCM_NEW_HW_PARAMS_API 00084 #define AUDIODEV "default:0,0" 00085 snd_pcm_t *handle = NULL; 00086 int sndbuf_pos=0; 00087 00088 #define ALSA9_ERROR(str,err) { \ 00089 fprintf(stderr,"ALSA9 Error: %s %s\n",str,snd_strerror(err)); } 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_settings settings = { 0, 1, 0, 8000, 200, 2048, 4, AUDIODEV }; 00101 00102 snd_pcm_hw_params_t *params; 00103 static snd_pcm_uframes_t chunk_size = 0; 00104 int err = 0; 00105 00106 void alsa_audio_close(void) { snd_pcm_close(handle); } 00107 00113 int init_audio(void) { 00114 snd_pcm_sw_params_t *sw_params; 00115 unsigned int format; 00116 00117 printf("cfsndserv for the ALSA9 sound system\n"); 00118 printf("supports sound effects only (no background music).\n"); 00119 fflush(stdout); 00120 00121 #ifdef SOUND_DEBUG 00122 fprintf(stderr, "init_audio: ALSA9\n"); 00123 fflush(stderr); 00124 #endif 00125 00126 /* open the PCM device */ 00127 if ((err = snd_pcm_open(&handle,AUDIODEV,SND_PCM_STREAM_PLAYBACK,0)) <0) { 00128 ALSA9_ERROR("init_audio(): ",err); 00129 exit(1); 00130 } 00131 00132 atexit(alsa_audio_close); 00133 00134 /* allocate and zero out params */ 00135 snd_pcm_hw_params_alloca(¶ms); 00136 00137 if ((err = snd_pcm_hw_params_any(handle,params)) <0) { 00138 ALSA9_ERROR("init_audio(): ",err); 00139 exit(1); 00140 } 00141 00142 /* set the access mode (interleaved) */ 00143 if ((err = snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED)) <0) { 00144 ALSA9_ERROR("init_audio(): ",err); 00145 exit(1); 00146 } 00147 00148 /* set the format */ 00149 00150 if (settings.bit8) 00151 format = settings.sign?SND_PCM_FORMAT_S8:SND_PCM_FORMAT_U8; 00152 else 00153 format = SND_PCM_FORMAT_U16; 00154 00155 00156 if ((err = snd_pcm_hw_params_set_format(handle,params,format))<0) { 00157 ALSA9_ERROR("init_audio(): ",err); 00158 exit(1); 00159 } 00160 00161 /* set the number of channels */ 00162 if ((err = snd_pcm_hw_params_set_channels(handle,params,settings.stereo?2:1))<0) { 00163 ALSA9_ERROR("init_audio(): ",err); 00164 exit(1); 00165 } 00166 00167 stereo = settings.stereo?1:0; 00168 00169 /* set the rate (our frequency, or closest match) */ 00170 unsigned int r = (unsigned int)settings.frequency; 00171 if (r == 0) r = 41100; 00172 int dir = 0; 00173 frequency = snd_pcm_hw_params_set_rate_near(handle,params,&r,&dir); 00174 00175 /* get sample size */ 00176 sample_size = (snd_pcm_format_physical_width(format) * (settings.stereo?2:1)) / 8; 00177 #ifdef SOUND_DEBUG 00178 printf("init_audio(): sample_size = %d\n",sample_size); 00179 fflush(stdout); 00180 #endif 00181 00182 00183 /* apply the settings */ 00184 if ((err = snd_pcm_hw_params(handle,params))<0) { 00185 ALSA9_ERROR("init_audio(): ",err); 00186 exit(1); 00187 } 00188 00189 err=snd_pcm_nonblock(handle, 1); 00190 if (err < 0) { 00191 ALSA9_ERROR("nonblock setting error: %s", err); 00192 exit(1); 00193 } 00194 00195 if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) { 00196 fprintf (stderr, "cannot allocate software parameters structure (%s)\n", 00197 snd_strerror (err)); 00198 exit (1); 00199 } 00200 if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) { 00201 fprintf (stderr, "cannot initialize software parameters structure (%s)\n", 00202 snd_strerror (err)); 00203 exit (1); 00204 } 00205 if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, 4096)) < 0) { 00206 fprintf (stderr, "cannot set minimum available count (%s)\n", 00207 snd_strerror (err)); 00208 exit (1); 00209 } 00210 if ((err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, 0U)) < 0) { 00211 fprintf (stderr, "cannot set start mode (%s)\n", 00212 snd_strerror (err)); 00213 exit (1); 00214 } 00215 if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) { 00216 fprintf (stderr, "cannot set software parameters (%s)\n", 00217 snd_strerror (err)); 00218 exit (1); 00219 } 00220 00221 /* Zerolevel=0x80 seems to work best for both 8 and 16 bit audio */ 00222 if (sign) zerolevel=0; 00223 else zerolevel=0x80; 00224 00225 snd_pcm_hw_params_get_period_size(params, &chunk_size, 0); 00226 00227 return 0; 00228 } 00229 00230 void alsa_recover(int e) { 00231 /* Recover from various errors */ 00232 if (e == -EAGAIN) { 00233 return; 00234 } else if (e == -EPIPE) { 00235 err = snd_pcm_prepare(handle); 00236 if (err < 0) { 00237 ALSA9_ERROR("alsa_recover(): Unable to recover from underrun. ",err); 00238 return; 00239 } 00240 } else if (e == -ESTRPIPE) { 00241 while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); 00242 if (err < 0) { 00243 err = snd_pcm_prepare(handle); 00244 if (err < 0) { 00245 ALSA9_ERROR("alsa_recover(): Unable to recover from suspend. ",err); 00246 return; 00247 } 00248 } 00249 } else ALSA9_ERROR("alsa_recover(): ",e); 00250 00251 } 00252 00253 00254 00268 int audio_play(int buffer, int off) { 00269 int count = (settings.buflen - off) / sample_size; 00270 00271 #ifdef SOUND_DEBUG 00272 fprintf(stderr, "audio_play: ALSA9\n"); 00273 fflush(stderr); 00274 #endif 00275 00276 if (count > chunk_size) count=chunk_size; 00277 00278 #ifdef SOUND_DEBUG_WRITES 00279 printf("audio play - writing starting at %d, %d bytes, off=%d\n", 00280 settings.buflen*buffer+off,count, off); 00281 fflush(stdout); 00282 #endif 00283 00284 err = snd_pcm_writei(handle,buffers+settings.buflen*buffer+off, count); 00285 00286 if (err < 0) { 00287 alsa_recover(err); 00288 return 0; 00289 } else { 00290 return err * sample_size; 00291 } 00292 } 00293 00294 /* Plays sound 'soundnum'. soundtype is 0 for normal sounds, 1 for 00295 * spell_sounds. This might get extended in the future. x,y are offset 00296 * (assumed from player) to play sound. This information is used to 00297 * determine value and left vs right speaker balance. 00298 * This doesn't really play a sound, rather it just addes it to 00299 * the buffer to be played later on. 00300 */ 00301 00302 void play_sound(int soundnum, int soundtype, int x, int y) 00303 { 00304 Sound_Info *si; 00305 int buf,off; 00306 int i; 00307 unsigned left_ratio,right_ratio; 00308 double dist; 00309 00310 #ifdef SOUND_DEBUG 00311 fprintf(stderr, "play_sound: ALSA9\n"); 00312 fflush(stderr); 00313 #endif 00314 00315 buf=current_buffer; 00316 if (buf>=settings.buffers) buf=1; 00317 00318 if (buf == 0) buf++; 00319 00320 /* check if the buffer isn't full */ 00321 #ifdef SOUND_DEBUG 00322 fprintf(stderr,"Sounds in buffer %i: %i\n",buf,sounds_in_buffer[buf]); 00323 #endif 00324 if (sounds_in_buffer[buf]>settings.simultaneously) return; 00325 00326 if (soundnum>=MAX_SOUNDS || soundnum<0) { 00327 fprintf(stderr,"Invalid sound number: %d\n", soundnum); 00328 return; 00329 } 00330 00331 if (soundtype < SOUND_NORMAL || soundtype == 0) soundtype = SOUND_NORMAL; 00332 00333 if (soundtype==SOUND_NORMAL) { 00334 si = &normal_sounds[soundnum]; 00335 } 00336 else if (soundtype==SOUND_SPELL) { 00337 si = &spell_sounds[soundnum]; 00338 } 00339 else { 00340 fprintf(stderr,"Unknown soundtype: %d\n", soundtype); 00341 return; 00342 } 00343 00344 if (!si->filename) { 00345 fprintf(stderr,"Sound %d (type %d) is not defined\n", soundnum, soundtype); 00346 return; 00347 } 00348 00349 /* 00350 * Load the sound if it is not loaded yet. 00351 * 00352 */ 00353 if (!si->data){ 00354 FILE *f; 00355 struct stat sbuf; 00356 #ifdef SOUND_DEBUG 00357 fprintf(stderr,"Loading file: %s\n",si->filename); 00358 #endif 00359 if (stat(si->filename,&sbuf)){ 00360 perror(si->filename); 00361 return; 00362 } 00363 si->size=sbuf.st_size; 00364 if (si->size <=0 ) return; 00365 if (si->size*sample_size > settings.buflen*(settings.buffers-1) ){ 00366 fprintf(stderr,"Sound %s too long (%i > %i)\n",si->filename,si->size, 00367 settings.buflen*(settings.buffers-1)/sample_size); 00368 return; 00369 } 00370 si->data=(unsigned char *)malloc(si->size); 00371 f=fopen(si->filename,"r"); 00372 if (!f){ 00373 perror(si->filename); 00374 return; 00375 } 00376 fread(si->data,1,si->size,f); 00377 fclose(f); 00378 } 00379 00380 /* calculate volume multiplers */ 00381 dist=sqrt(x*x+y*y); 00382 #ifdef SOUND_DEBUG 00383 fprintf(stderr,"Playing sound %i (%s), volume %i, x,y=%d,%d, dist=%f\n",soundnum,si->symbolic,si->volume,x,y, dist); 00384 #endif 00385 right_ratio=left_ratio=((1<<16)*si->volume)/(100*(1+SOUND_DECREASE*dist)); 00386 00387 if (stereo){ 00388 double diff; 00389 if (dist) 00390 diff=(1.0-fabs((double)x/dist)); 00391 else 00392 diff=1; 00393 #ifdef SOUND_DEBUG 00394 printf("diff: %f\n",diff); 00395 fflush(stdout); 00396 #endif 00397 if (x<0) right_ratio*=diff; 00398 else left_ratio*=diff; 00399 } 00400 00401 #ifdef SOUND_DEBUG 00402 fprintf(stderr,"Ratio: %i, %i\n",left_ratio,right_ratio); 00403 #endif 00404 00405 /* insert the sound to the buffers */ 00406 sounds_in_buffer[buf]++; 00407 off=0; 00408 for(i=0;i<si->size;i++){ 00409 int dat=si->data[i]-0x80; 00410 if (settings.bit8) { 00411 if (!stereo){ 00412 buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16; 00413 } 00414 else{ 00415 buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16; 00416 buffers[buf*settings.buflen+off+1]+=(dat*right_ratio)>>16; 00417 } 00418 } 00419 else{ /* 16 bit output */ 00420 if (!stereo){ 00421 #ifdef WORDS_BIGENDIAN 00422 buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff; 00423 buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16; 00424 } 00425 else{ 00426 buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff; 00427 buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16; 00428 buffers[buf*settings.buflen+off+3]+=((dat*right_ratio)>>8)&0xff; 00429 buffers[buf*settings.buflen+off+2]+=(dat*right_ratio)>>16; 00430 } 00431 #else 00432 buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff; 00433 buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16; 00434 } 00435 else{ 00436 buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff; 00437 buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16; 00438 buffers[buf*settings.buflen+off+2]+=((dat*right_ratio)>>8)&0xff; 00439 buffers[buf*settings.buflen+off+3]+=(dat*right_ratio)>>16; 00440 } 00441 #endif 00442 } 00443 00444 off+=sample_size; 00445 00446 if (off>=settings.buflen){ 00447 off=0; 00448 buf++; 00449 if (buf>=settings.buffers) { 00450 buf=0; 00451 } 00452 } 00453 } 00454 #ifdef SOUND_DEBUG 00455 fprintf(stderr,"Added %d bytes, last buffer=%d, lastpos=%d\n", 00456 si->size, buf, off); 00457 #endif 00458 /* This write did not wrap the buffers */ 00459 if (buf+1 > current_buffer) { 00460 if ((buf+1 > first_free_buffer) && (first_free_buffer >= current_buffer)) 00461 first_free_buffer = buf+1; 00462 } else { /* Buffers did wrap */ 00463 if (((buf+1 > first_free_buffer) && (first_free_buffer < current_buffer)) || 00464 (first_free_buffer >= current_buffer)) 00465 first_free_buffer = buf+1; 00466 } 00467 if (first_free_buffer >= settings.buffers) first_free_buffer=0; 00468 00469 00470 } 00471 00479 void play_music(const char *name) { 00480 00481 #ifdef SOUND_DEBUG 00482 fprintf(stderr, "play_music: no music support for this sound system.\n"); 00483 fflush(stderr); 00484 #endif 00485 00486 return; 00487 } 00488 00489 int main(int argc, char *argv[]) 00490 { 00491 int infd; 00492 char inbuf[1024]; 00493 int inbuf_pos=0, sfd, frames, maxframes; 00494 fd_set inset; 00495 struct timeval timeout; 00496 00497 printf ("%s\n",rcsid_sound_src_alsa9_c); 00498 fflush(stdout); 00499 00500 if (read_settings()) write_settings(); 00501 if (init_sounds()) return 1; 00502 00503 infd=fileno(stdin); 00504 FD_ZERO(&inset); 00505 FD_SET(infd,&inset); 00506 00507 /* need to know max amount of space */ 00508 maxframes = snd_pcm_avail_update (handle); 00509 00510 while(1){ 00511 timeout.tv_sec=0; 00512 timeout.tv_usec=10000; 00513 FD_SET(infd,&inset); 00514 00515 sfd = select(FD_SETSIZE,&inset,NULL,NULL,&timeout); 00516 00517 /* ALSA9 doesn't provide us with an fd to use to make 00518 * sure we have space for writing. So instead, we use 00519 * a timeout with select and if there is space, make send 00520 * more data. 00521 */ 00522 frames = snd_pcm_avail_update (handle); 00523 /* fprintf(stderr,"frames=%d\n", frames);*/ 00524 while (((settings.buflen+frames)> maxframes) || (frames == -EPIPE)) { 00525 00526 if (current_buffer != first_free_buffer) { 00527 int wrote; 00528 00529 if (frames == -EPIPE) snd_pcm_prepare(handle); 00530 wrote = audio_play(current_buffer, sndbuf_pos); 00531 #ifdef SOUND_DEBUG_WRITES 00532 printf("play_sound(): wrote %d\n",wrote); 00533 fflush(stdout); 00534 #endif 00535 if (wrote < settings.buflen-sndbuf_pos) { 00536 sndbuf_pos+=wrote; 00537 } 00538 else{ 00539 /* clean the buffer */ 00540 memset(buffers+settings.buflen*current_buffer,zerolevel,settings.buflen); 00541 sounds_in_buffer[current_buffer]=0; 00542 sndbuf_pos=0; 00543 current_buffer++; 00544 if (current_buffer>=settings.buffers) current_buffer=0; 00545 } 00546 } else 00547 break; 00548 frames = snd_pcm_avail_update (handle); 00549 } 00550 00551 00552 if (sfd > 0) { 00553 if (FD_ISSET(infd,&inset)){ 00554 int err=read(infd,inbuf+inbuf_pos,1); 00555 if (err<1 && errno!=EINTR){ 00556 if (err<0) perror("read"); 00557 break; 00558 } 00559 if (inbuf[inbuf_pos]=='\n'){ 00560 inbuf[inbuf_pos++]=0; 00561 StdinCmd((char *)inbuf, inbuf_pos); 00562 inbuf_pos=0; 00563 } 00564 else { 00565 inbuf_pos++; 00566 if (inbuf_pos>=1024){ 00567 fprintf(stderr,"Input buffer overflow!\n"); 00568 inbuf_pos=0; 00569 } 00570 } 00571 } 00572 } 00573 } 00574 00575 return 0; 00576 }
1.7.6.1