Crossfire Client, Branches  R11627
alsa9.c
Go to the documentation of this file.
1 static char *rcsid_cfsndserv_c =
2  "$Id: alsa9.c 6716 2007-06-27 18:57:31Z akirschbaum $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2001-2005, Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire-devel@real-time.com
23 */
24 
25 
26 /* This file is only used for alsa 0.9 and later sound. Alsa sound code is
27  * sufficiently different from the rest that trying to keep it common didn't
28  * make much sense.
29  */
30 
31 #define SOUND_DEBUG
32 
33 /* Debugs the actual writing of data - this generally isn't that interesting,
34  * and generates a lot of messages which tends to obscure the more interesting
35  * ones.
36  */
37 /*#define SOUND_DEBUG_WRITES */
38 
39 #include <config.h>
40 
41 #include <stdio.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <time.h>
45 
46 #ifdef HAVE_FCNTL_H
47 #include <fcntl.h>
48 #endif
49 
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
53 
54 #include <math.h>
55 
56 #ifdef HAVE_SYS_IOCTL_H
57 #include <sys/ioctl.h>
58 #endif
59 
60 #ifdef HAVE_SYS_SELECT_H
61 #include <sys/select.h>
62 #endif
63 
64 #ifdef HAVE_STRING_H
65 #include <string.h>
66 #endif
67 
68 #include <stdlib.h>
69 #include <ctype.h>
70 #include <errno.h>
71 
72 #include "newclient.h"
73 #include "soundsdef.h"
74 
75 
76 #include <alsa/asoundlib.h>
77 #include <alsa/pcm_plugin.h>
78 
79 
80 #define ALSA_PCM_NEW_HW_PARAMS_API
81 #define AUDIODEV "default:0,0"
82 snd_pcm_t *handle = NULL;
83 int sndbuf_pos=0;
84 
85 #define ALSA9_ERROR(str,err) { \
86  fprintf(stderr,"ALSA9 Error: %s %s\n",str,snd_strerror(err)); }
87 
88 #define CONFIG_FILE "/.crossfire/sndconfig"
89 
90 
91 #define SOUND_DECREASE 0.1
92 
93 /* mixer variables */
94 int *sounds_in_buffer=NULL;
95 int current_buffer=0; /* Next buffer we will write out */
96 int first_free_buffer=0; /* So we know when to stop playing sounds */
97 
98 int soundfd=0;
99 
100 /* sound device parameters */
102 
105  const char *audiodev;
106 } settings={0,1, 0, 8000,200,2048,4,AUDIODEV};
107 
108 #include "common.c"
109 
110 
111 snd_pcm_hw_params_t *params;
112 static snd_pcm_uframes_t chunk_size = 0;
113 int err = 0;
114 
115 void alsa_audio_close(void) { snd_pcm_close(handle); }
116 
117 int init_audio(void) {
118  snd_pcm_sw_params_t *sw_params;
119  unsigned int format;
120 
121  printf("cfsndserv compiled for ALSA9 sound system\n");
122  fflush(stdout);
123 
124  /* open the PCM device */
125  if ((err = snd_pcm_open(&handle,AUDIODEV,SND_PCM_STREAM_PLAYBACK,0)) <0) {
126  ALSA9_ERROR("init_audio(): ",err);
127  exit(1);
128  }
129 
130  atexit(alsa_audio_close);
131 
132  /* allocate and zero out params */
133  snd_pcm_hw_params_alloca(&params);
134 
135  if ((err = snd_pcm_hw_params_any(handle,params)) <0) {
136  ALSA9_ERROR("init_audio(): ",err);
137  exit(1);
138  }
139 
140  /* set the access mode (interleaved) */
141  if ((err = snd_pcm_hw_params_set_access(handle,params,SND_PCM_ACCESS_RW_INTERLEAVED)) <0) {
142  ALSA9_ERROR("init_audio(): ",err);
143  exit(1);
144  }
145 
146  /* set the format */
147 
148  if (settings.bit8)
149  format = settings.sign?SND_PCM_FORMAT_S8:SND_PCM_FORMAT_U8;
150  else
151  format = SND_PCM_FORMAT_U16;
152 
153 
154  if ((err = snd_pcm_hw_params_set_format(handle,params,format))<0) {
155  ALSA9_ERROR("init_audio(): ",err);
156  exit(1);
157  }
158 
159  /* set the number of channels */
160  if ((err = snd_pcm_hw_params_set_channels(handle,params,settings.stereo?2:1))<0) {
161  ALSA9_ERROR("init_audio(): ",err);
162  exit(1);
163  }
164 
165  stereo = settings.stereo?1:0;
166 
167  /* set the rate (our frequency, or closest match) */
168  unsigned int r = (unsigned int)settings.frequency;
169  if (r == 0) r = 41100;
170  int dir = 0;
171  frequency = snd_pcm_hw_params_set_rate_near(handle,params,&r,&dir);
172 
173  /* get sample size */
174  sample_size = (snd_pcm_format_physical_width(format) * (settings.stereo?2:1)) / 8;
175  #ifdef SOUND_DEBUG
176  printf("init_audio(): sample_size = %d\n",sample_size);
177  fflush(stdout);
178  #endif
179 
180 
181  /* apply the settings */
182  if ((err = snd_pcm_hw_params(handle,params))<0) {
183  ALSA9_ERROR("init_audio(): ",err);
184  exit(1);
185  }
186 
187  err=snd_pcm_nonblock(handle, 1);
188  if (err < 0) {
189  ALSA9_ERROR("nonblock setting error: %s", err);
190  exit(1);
191  }
192 
193  if ((err = snd_pcm_sw_params_malloc (&sw_params)) < 0) {
194  fprintf (stderr, "cannot allocate software parameters structure (%s)\n",
195  snd_strerror (err));
196  exit (1);
197  }
198  if ((err = snd_pcm_sw_params_current (handle, sw_params)) < 0) {
199  fprintf (stderr, "cannot initialize software parameters structure (%s)\n",
200  snd_strerror (err));
201  exit (1);
202  }
203  if ((err = snd_pcm_sw_params_set_avail_min (handle, sw_params, 4096)) < 0) {
204  fprintf (stderr, "cannot set minimum available count (%s)\n",
205  snd_strerror (err));
206  exit (1);
207  }
208  if ((err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, 0U)) < 0) {
209  fprintf (stderr, "cannot set start mode (%s)\n",
210  snd_strerror (err));
211  exit (1);
212  }
213  if ((err = snd_pcm_sw_params (handle, sw_params)) < 0) {
214  fprintf (stderr, "cannot set software parameters (%s)\n",
215  snd_strerror (err));
216  exit (1);
217  }
218 
219  /* Zerolevel=0x80 seems to work best for both 8 and 16 bit audio */
220  if (sign) zerolevel=0;
221  else zerolevel=0x80;
222 
223  snd_pcm_hw_params_get_period_size(params, &chunk_size, 0);
224 
225  return 0;
226 }
227 
228 void alsa_recover(int e) {
229  /* Recover from various errors */
230  if (e == -EAGAIN) {
231  return;
232  } else if (e == -EPIPE) {
233  err = snd_pcm_prepare(handle);
234  if (err < 0) {
235  ALSA9_ERROR("alsa_recover(): Unable to recover from underrun. ",err);
236  return;
237  }
238  } else if (e == -ESTRPIPE) {
239  while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1);
240  if (err < 0) {
241  err = snd_pcm_prepare(handle);
242  if (err < 0) {
243  ALSA9_ERROR("alsa_recover(): Unable to recover from suspend. ",err);
244  return;
245  }
246  }
247  } else ALSA9_ERROR("alsa_recover(): ",e);
248 
249 }
250 
251 
252 
253 /* Does the actual task of writing the data to the socket.
254  * The ALSA write logic is a bit odd, in that the count you pass in
255  * is not the number of bytes you are writing, but the number of
256  * samples you are writing. Thus, if you have stereo with 1 byte/channel,
257  * you'd divide the number by 2. If you have 16 bit audio with stereo,
258  * you'd divide the number by 4.
259  */
260 int audio_play(int buffer, int off) {
261  int count = (settings.buflen - off) / sample_size;
262 
263  if (count > chunk_size) count=chunk_size;
264 
265 #ifdef SOUND_DEBUG_WRITES
266  printf("audio play - writing starting at %d, %d bytes, off=%d\n",
267  settings.buflen*buffer+off,count, off);
268  fflush(stdout);
269 #endif
270 
271  err = snd_pcm_writei(handle,buffers+settings.buflen*buffer+off, count);
272 
273  if (err < 0) {
274  alsa_recover(err);
275  return 0;
276  } else {
277  return err * sample_size;
278  }
279 }
280 
281 
282 
283 
284 
285 
286 
287 /* Plays sound 'soundnum'. soundtype is 0 for normal sounds, 1 for
288  * spell_sounds. This might get extended in the future. x,y are offset
289  * (assumed from player) to play sound. This information is used to
290  * determine value and left vs right speaker balance.
291  * This doesn't really play a sound, rather it just addes it to
292  * the buffer to be played later on.
293  */
294 
295 void play_sound(int soundnum, int soundtype, int x, int y)
296 {
297  Sound_Info *si;
298  int buf,off;
299  int i;
300  unsigned left_ratio,right_ratio;
301  double dist;
302 
303  buf=current_buffer;
304  if (buf>=settings.buffers) buf=1;
305 
306  if (buf == 0) buf++;
307 
308  /* check if the buffer isn't full */
309 #ifdef SOUND_DEBUG
310  fprintf(stderr,"Sounds in buffer %i: %i\n",buf,sounds_in_buffer[buf]);
311 #endif
312  if (sounds_in_buffer[buf]>settings.simultaneously) return;
313 
314  if (soundnum>=MAX_SOUNDS || soundnum<0) {
315  fprintf(stderr,"Invalid sound number: %d\n", soundnum);
316  return;
317  }
318 
319  if (soundtype < SOUND_NORMAL || soundtype == 0) soundtype = SOUND_NORMAL;
320 
321  if (soundtype==SOUND_NORMAL) {
322  si = &normal_sounds[soundnum];
323  }
324  else if (soundtype==SOUND_SPELL) {
325  si = &spell_sounds[soundnum];
326  }
327  else {
328  fprintf(stderr,"Unknown soundtype: %d\n", soundtype);
329  return;
330  }
331 
332  if (!si->filename) {
333  fprintf(stderr,"Sound %d (type %d) is not defined\n", soundnum, soundtype);
334  return;
335  }
336 
337  /*
338  * Load the sound if it is not loaded yet.
339  *
340  */
341  if (!si->data){
342  FILE *f;
343  struct stat sbuf;
344 #ifdef SOUND_DEBUG
345  fprintf(stderr,"Loading file: %s\n",si->filename);
346 #endif
347  if (stat(si->filename,&sbuf)){
348  perror(si->filename);
349  return;
350  }
351  si->size=sbuf.st_size;
352  if (si->size <=0 ) return;
354  fprintf(stderr,"Sound %s too long (%i > %i)\n",si->filename,si->size,
356  return;
357  }
358  si->data=(unsigned char *)malloc(si->size);
359  f=fopen(si->filename,"r");
360  if (!f){
361  perror(si->filename);
362  return;
363  }
364  fread(si->data,1,si->size,f);
365  fclose(f);
366  }
367 
368  /* calculate volume multiplers */
369  dist=sqrt(x*x+y*y);
370 #ifdef SOUND_DEBUG
371  fprintf(stderr,"Playing sound %i (%s), volume %i, x,y=%d,%d, dist=%f\n",soundnum,si->symbolic,si->volume,x,y, dist);
372 #endif
373  right_ratio=left_ratio=((1<<16)*si->volume)/(100*(1+SOUND_DECREASE*dist));
374 
375  if (stereo){
376  double diff;
377  if (dist)
378  diff=(1.0-fabs((double)x/dist));
379  else
380  diff=1;
381 #ifdef SOUND_DEBUG
382  printf("diff: %f\n",diff);
383  fflush(stdout);
384 #endif
385  if (x<0) right_ratio*=diff;
386  else left_ratio*=diff;
387  }
388 
389 #ifdef SOUND_DEBUG
390  fprintf(stderr,"Ratio: %i, %i\n",left_ratio,right_ratio);
391 #endif
392 
393  /* insert the sound to the buffers */
394  sounds_in_buffer[buf]++;
395  off=0;
396  for(i=0;i<si->size;i++){
397  int dat=si->data[i]-0x80;
398  if (settings.bit8) {
399  if (!stereo){
400  buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
401  }
402  else{
403  buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
404  buffers[buf*settings.buflen+off+1]+=(dat*right_ratio)>>16;
405  }
406  }
407  else{ /* 16 bit output */
408  if (!stereo){
409 #ifdef WORDS_BIGENDIAN
410  buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff;
411  buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
412  }
413  else{
414  buffers[buf*settings.buflen+off+1]+=((dat*left_ratio)>>8)&0xff;
415  buffers[buf*settings.buflen+off]+=(dat*left_ratio)>>16;
416  buffers[buf*settings.buflen+off+3]+=((dat*right_ratio)>>8)&0xff;
417  buffers[buf*settings.buflen+off+2]+=(dat*right_ratio)>>16;
418  }
419 #else
420  buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff;
421  buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16;
422  }
423  else{
424  buffers[buf*settings.buflen+off]+=((dat*left_ratio)>>8)&0xff;
425  buffers[buf*settings.buflen+off+1]+=(dat*left_ratio)>>16;
426  buffers[buf*settings.buflen+off+2]+=((dat*right_ratio)>>8)&0xff;
427  buffers[buf*settings.buflen+off+3]+=(dat*right_ratio)>>16;
428  }
429 #endif
430  }
431 
432  off+=sample_size;
433 
434  if (off>=settings.buflen){
435  off=0;
436  buf++;
437  if (buf>=settings.buffers) {
438  buf=0;
439  }
440  }
441  }
442 #ifdef SOUND_DEBUG
443  fprintf(stderr,"Added %d bytes, last buffer=%d, lastpos=%d\n",
444  si->size, buf, off);
445 #endif
446  /* This write did not wrap the buffers */
447  if (buf+1 > current_buffer) {
448  if ((buf+1 > first_free_buffer) && (first_free_buffer >= current_buffer))
449  first_free_buffer = buf+1;
450  } else { /* Buffers did wrap */
451  if (((buf+1 > first_free_buffer) && (first_free_buffer < current_buffer)) ||
453  first_free_buffer = buf+1;
454  }
456 
457 
458 }
459 
460 
461 int main(int argc, char *argv[])
462 {
463  int infd;
464  char inbuf[1024];
465  int inbuf_pos=0, sfd, frames, maxframes;
466  fd_set inset;
467  struct timeval timeout;
468 
469  printf ("%s\n",rcsid_cfsndserv_c);
470 
471  fflush(stdout);
472 
473  if (read_settings()) write_settings();
474  if (init_sounds()) return 1;
475 
476  infd=fileno(stdin);
477  FD_ZERO(&inset);
478  FD_SET(infd,&inset);
479 
480  /* need to know max amount of space */
481  maxframes = snd_pcm_avail_update (handle);
482 
483  while(1){
484  timeout.tv_sec=0;
485  timeout.tv_usec=10000;
486  FD_SET(infd,&inset);
487 
488  sfd = select(FD_SETSIZE,&inset,NULL,NULL,&timeout);
489 
490  /* ALSA9 doesn't provide us with an fd to use to make
491  * sure we have space for writing. So instead, we use
492  * a timeout with select and if there is space, make send
493  * more data.
494  */
495  frames = snd_pcm_avail_update (handle);
496 /* fprintf(stderr,"frames=%d\n", frames);*/
497  while (((settings.buflen+frames)> maxframes) || (frames == -EPIPE)) {
498 
500  int wrote;
501 
502  if (frames == -EPIPE) snd_pcm_prepare(handle);
504 #ifdef SOUND_DEBUG_WRITES
505  printf("play_sound(): wrote %d\n",wrote);
506  fflush(stdout);
507 #endif
508  if (wrote < settings.buflen-sndbuf_pos) {
509  sndbuf_pos+=wrote;
510  }
511  else{
512  /* clean the buffer */
515  sndbuf_pos=0;
516  current_buffer++;
518  }
519  } else
520  break;
521  frames = snd_pcm_avail_update (handle);
522  }
523 
524 
525  if (sfd > 0) {
526  if (FD_ISSET(infd,&inset)){
527  int err=read(infd,inbuf+inbuf_pos,1);
528  if (err<1 && errno!=EINTR){
529  if (err<0) perror("read");
530  break;
531  }
532  if (inbuf[inbuf_pos]=='\n'){
533  inbuf[inbuf_pos++]=0;
534  SoundCmd((unsigned char *)inbuf,inbuf_pos);
535  inbuf_pos=0;
536  }
537  else {
538  inbuf_pos++;
539  if (inbuf_pos>=1024){
540  fprintf(stderr,"Input buffer overflow!\n");
541  inbuf_pos=0;
542  }
543  }
544  }
545  }
546  }
547 
548  return 0;
549 }
#define MAX_SOUNDS
Definition: cfsndserv.c:104
unsigned char volume
Definition: cfsndserv.c:122
int audio_play(int buffer, int off)
Definition: alsa9.c:260
#define SOUND_DECREASE
Definition: alsa9.c:91
int write_settings(void)
Definition: cfsndserv.c:877
int init_audio(void)
Definition: alsa9.c:117
Sound_Info spell_sounds[MAX_SOUNDS]
Definition: cfsndserv.c:127
const char * audiodev
Definition: alsa9.c:105
snd_pcm_hw_params_t * params
Definition: alsa9.c:111
int buffers
Definition: alsa9.c:104
int current_buffer
Definition: alsa9.c:95
int stereo
Definition: alsa9.c:101
char * filename
Definition: cfsndserv.c:120
int init_sounds(void)
Definition: sound.c:60
int err
Definition: alsa9.c:113
Sound_Info normal_sounds[MAX_SOUNDS]
Definition: cfsndserv.c:127
int stereo
Definition: alsa9.c:104
void alsa_audio_close(void)
Definition: alsa9.c:115
int soundfd
Definition: alsa9.c:98
#define SOUND_NORMAL
Definition: newclient.h:267
int read_settings(void)
Definition: cfsndserv.c:903
int sndbuf_pos
Definition: alsa9.c:83
char * symbolic
Definition: cfsndserv.c:121
#define ALSA9_ERROR(str, err)
Definition: alsa9.c:85
void alsa_recover(int e)
Definition: alsa9.c:228
#define AUDIODEV
Definition: alsa9.c:81
int frequency
Definition: alsa9.c:104
int main(int argc, char *argv[])
Definition: alsa9.c:461
snd_pcm_t * handle
Definition: alsa9.c:82
#define SOUND_SPELL
Definition: newclient.h:268
int first_free_buffer
Definition: alsa9.c:96
int frequency
Definition: alsa9.c:101
int simultaneously
Definition: alsa9.c:104
void play_sound(int soundnum, int soundtype, int x, int y)
Definition: alsa9.c:295
unsigned char * data
Definition: cfsndserv.c:124
char * buffers
Definition: cfsndserv.c:133
int * sounds_in_buffer
Definition: alsa9.c:94
static char * rcsid_cfsndserv_c
Definition: alsa9.c:1
struct sound_settings settings
int buflen
Definition: alsa9.c:104
void SoundCmd(unsigned char *data, int len)
Definition: sound.c:130
int zerolevel
Definition: alsa9.c:101
int sign
Definition: alsa9.c:101
static snd_pcm_uframes_t chunk_size
Definition: alsa9.c:112
int sample_size
Definition: alsa9.c:101