]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_alsa_0_6.c
removed unused 'minlight' option from dlight struct, and made muzzleflash light maller
[xonotic/darkplaces.git] / snd_alsa_0_6.c
1 /*
2         snd_alsa_0_6.c
3
4         (description)
5
6         Copyright (C) 1999,2000  contributors of the QuakeForge project
7         Please see the file "AUTHORS" for a list of contributors
8
9         This program is free software; you can redistribute it and/or
10         modify it under the terms of the GNU General Public License
11         as published by the Free Software Foundation; either version 2
12         of the License, or (at your option) any later version.
13
14         This program is distributed in the hope that it will be useful,
15         but WITHOUT ANY WARRANTY; without even the implied warranty of
16         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18         See the GNU General Public License for more details.
19
20         You should have received a copy of the GNU General Public License
21         along with this program; if not, write to:
22
23                 Free Software Foundation, Inc.
24                 59 Temple Place - Suite 330
25                 Boston, MA  02111-1307, USA
26
27         $Id$
28 */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdio.h>
35
36 #include <sys/asoundlib.h>
37
38 #include "qtypes.h"
39 #include "sound.h"
40 #include "qargs.h"
41 #include "console.h"
42
43 static int snd_inited;
44
45 static snd_pcm_t *pcm_handle;
46 static snd_pcm_params_info_t cpinfo;
47 static snd_pcm_params_t params;
48 static snd_pcm_setup_t setup;
49 static snd_pcm_channel_area_t mmap_stopped_areas[2];
50 static snd_pcm_channel_area_t mmap_running_areas[2];
51 static int card=-1,dev=-1,subdev=-1;;
52
53 int check_card(int card)
54 {
55         snd_ctl_t *handle;
56         snd_ctl_hw_info_t info;
57         int rc;
58
59         if ((rc = snd_ctl_hw_open(&handle, 0, card)) < 0) {
60                 Con_Printf("Error: control open (%i): %s\n", card, snd_strerror(rc));
61                 return rc;
62         }
63         if ((rc = snd_ctl_hw_info(handle, &info)) < 0) {
64                 Con_Printf("Error: control hardware info (%i): %s\n", card,
65                                    snd_strerror(rc));
66                 snd_ctl_close(handle);
67                 return rc;
68         }
69         snd_ctl_close(handle);
70         if (dev==-1) {
71                 for (dev = 0; dev < info.pcmdevs; dev++) {
72                         if ((rc=snd_pcm_hw_open_subdevice(&pcm_handle,card,dev,subdev,
73                                                                  SND_PCM_STREAM_PLAYBACK,
74                                                                  SND_PCM_NONBLOCK))==0) {
75                                 return 0;
76                         }
77                 }
78         } else {
79                 if (dev>=0 && dev <info.pcmdevs) {
80                         if ((rc=snd_pcm_hw_open_subdevice(&pcm_handle,card,dev,subdev,
81                                                                  SND_PCM_STREAM_PLAYBACK,
82                                                                  SND_PCM_NONBLOCK))==0) {
83                                 return 0;
84                         }
85                 }
86         }
87         return 1;
88 }
89
90 qboolean SNDDMA_Init(void)
91 {
92         int rc=0,i;
93         char *err_msg="";
94         int rate=-1,format=-1,bps,stereo=-1,frag_size,frame_size;
95         unsigned int mask;
96
97         mask = snd_cards_mask();
98         if (!mask) {
99                 Con_Printf("No sound cards detected\n");
100                 return 0;
101         }
102         if ((i=COM_CheckParm("-sndcard"))!=0) {
103                 card=atoi(com_argv[i+1]);
104         }
105         if ((i=COM_CheckParm("-snddev"))!=0) {
106                 dev=atoi(com_argv[i+1]);
107         }
108         if ((i=COM_CheckParm("-sndbits")) != 0) {
109                 i = atoi(com_argv[i+1]);
110                 if (i==16) {
111                         format = SND_PCM_SFMT_S16_LE;
112                 } else if (i==8) {
113                         format = SND_PCM_SFMT_U8;
114                 } else {
115                         Con_Printf("Error: invalid sample bits: %d\n", i);
116                         return 0;
117                 }
118         }
119         if ((i=COM_CheckParm("-sndspeed")) != 0) {
120                 rate = atoi(com_argv[i+1]);
121                 if (rate!=44100 && rate!=22050 && rate!=11025) {
122                         Con_Printf("Error: invalid sample rate: %d\n", rate);
123                         return 0;
124                 }
125         }
126         if ((i=COM_CheckParm("-sndmono")) != 0) {
127                 stereo=0;
128         }
129         if (card==-1) {
130                 for (card=0; card<SND_CARDS; card++) {
131                         if (!(mask & (1<<card)))
132                                 continue;
133                         rc=check_card(card);
134                         if (rc<0)
135                                 return 0;
136                         if (!rc)
137                                 goto dev_openned;
138                 }
139         } else {
140                 if (dev==-1) {
141                         rc=check_card(card);
142                         if (rc<0)
143                                 return 0;
144                         if (!rc)
145                                 goto dev_openned;
146                 } else {
147                         if ((rc=snd_pcm_hw_open_subdevice(&pcm_handle,card,dev,subdev,
148                                                                  SND_PCM_STREAM_PLAYBACK,
149                                                                  SND_PCM_NONBLOCK))<0) {
150                                 Con_Printf("Error: audio open error: %s\n", snd_strerror(rc));
151                                 return 0;
152                         }
153                         goto dev_openned;
154                 }
155         }
156         Con_Printf("Error: audio open error: %s\n", snd_strerror(rc));
157         return 0;
158
159  dev_openned:
160         Con_Printf("Using card %d, device %d.\n", card, dev);
161         memset(&cpinfo, 0, sizeof(cpinfo));
162         snd_pcm_params_info(pcm_handle, &cpinfo);
163         Con_Printf("%08x %08x %08x\n",cpinfo.flags,cpinfo.formats,cpinfo.rates);
164         if ((rate==-1 || rate==44100) && cpinfo.rates & SND_PCM_RATE_44100) {
165                 rate=44100;
166                 frag_size=256;  /* assuming stereo 8 bit */
167         } else if ((rate==-1 || rate==22050) && cpinfo.rates & SND_PCM_RATE_22050) {
168                 rate=22050;
169                 frag_size=128;  /* assuming stereo 8 bit */
170         } else if ((rate==-1 || rate==11025) && cpinfo.rates & SND_PCM_RATE_11025) {
171                 rate=11025;
172                 frag_size=64;   /* assuming stereo 8 bit */
173         } else {
174                 Con_Printf("ALSA: desired rates not supported\n");
175                 goto error_2;
176         }
177         if ((format==-1 || format==SND_PCM_SFMT_S16_LE) && cpinfo.formats & SND_PCM_FMT_S16_LE) {
178                 format=SND_PCM_SFMT_S16_LE;
179                 bps=16;
180                 frame_size=2;
181         } else if ((format==-1 || format==SND_PCM_SFMT_U8) && cpinfo.formats & SND_PCM_FMT_U8) {
182                 format=SND_PCM_SFMT_U8;
183                 bps=8;
184                 frame_size=1;
185         } else {
186                 Con_Printf("ALSA: desired formats not supported\n");
187                 goto error_2;
188         }
189         //XXX can't support non-interleaved stereo
190         if (stereo && (cpinfo.flags & SND_PCM_INFO_INTERLEAVED) && cpinfo.max_channels>=2) {
191                 stereo=1;
192                 frame_size*=2;
193         } else {
194                 stereo=0;
195         }
196
197         memset(&params, 0, sizeof(params));
198         //XXX can't support non-interleaved stereo
199         params.xfer_mode = stereo ? SND_PCM_XFER_INTERLEAVED
200                                                           : SND_PCM_XFER_NONINTERLEAVED;
201         params.mmap_shape = stereo ? SND_PCM_XFER_INTERLEAVED
202                                                                 : SND_PCM_XFER_NONINTERLEAVED;
203         params.format.sfmt=format;
204         params.format.rate=rate;
205         params.format.channels=stereo+1;
206         params.start_mode = SND_PCM_START_EXPLICIT;
207         params.xrun_mode = SND_PCM_XRUN_NONE;
208
209         params.buffer_size = (2<<16) / frame_size;
210         params.frag_size=frag_size;
211         params.avail_min = frag_size;
212
213         params.boundary = params.buffer_size;
214
215         while (1) {
216                 err_msg="audio params";
217                 if ((rc=snd_pcm_params(pcm_handle, &params))<0)
218                         goto error;
219
220                 memset(&setup, 0, sizeof(setup));
221                 err_msg="audio setup";
222                 if ((rc=snd_pcm_setup(pcm_handle, &setup))<0)
223                         goto error;
224                 if (setup.buffer_size == params.buffer_size)
225                         break;
226                 params.boundary = params.buffer_size = setup.buffer_size;
227         }
228
229         err_msg="audio mmap";
230         if ((rc=snd_pcm_mmap(pcm_handle))<0)
231                 goto error;
232         if ((rc=snd_pcm_mmap_get_areas(pcm_handle, mmap_stopped_areas, mmap_running_areas)))
233                 goto error;
234         err_msg="audio prepare";
235         if ((rc=snd_pcm_prepare(pcm_handle))<0)
236                 goto error;
237
238         shm=&sn;
239         memset((dma_t*)shm,0,sizeof(*shm));
240     shm->splitbuffer = 0;
241         shm->channels=setup.format.channels;
242         shm->submission_chunk=frag_size;                        // don't mix less than this #
243         shm->samplepos=0;                                                       // in mono samples
244         shm->samplebits=setup.format.sfmt==SND_PCM_SFMT_S16_LE?16:8;
245         shm->samples=setup.buffer_size*shm->channels;   // mono samples in buffer
246         shm->speed=setup.format.rate;
247         shm->buffer=(unsigned char*)mmap_running_areas->addr;
248     Con_Printf("%5d stereo\n", shm->channels - 1);
249     Con_Printf("%5d samples\n", shm->samples);
250     Con_Printf("%5d samplepos\n", shm->samplepos);
251     Con_Printf("%5d samplebits\n", shm->samplebits);
252     Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
253     Con_Printf("%5d speed\n", shm->speed);
254     Con_Printf("0x%x dma buffer\n", (int)shm->buffer);
255         Con_Printf("%5d total_channels\n", total_channels);
256
257         snd_inited=1;
258         return 1;
259  error:
260         Con_Printf("Error: %s: %s\n", err_msg, snd_strerror(rc));
261  error_2:
262         snd_pcm_close(pcm_handle);
263         return 0;
264 }
265
266 static inline int
267 get_hw_ptr()
268 {
269         size_t app_ptr;
270         ssize_t delay;
271         int hw_ptr;
272
273         if (snd_pcm_state (pcm_handle) != SND_PCM_STATE_RUNNING)
274                 return 0;
275         app_ptr = snd_pcm_mmap_offset (pcm_handle);
276         snd_pcm_delay (pcm_handle, &delay);
277         hw_ptr = app_ptr - delay;
278         return hw_ptr;
279 }
280
281 int SNDDMA_GetDMAPos(void)
282 {
283         int hw_ptr;
284
285         if (!snd_inited) return 0;
286
287         hw_ptr = get_hw_ptr();
288         hw_ptr *= shm->channels;
289         shm->samplepos = hw_ptr;
290         return shm->samplepos;
291 }
292
293 void SNDDMA_Shutdown(void)
294 {
295         if (snd_inited)
296         {
297                 snd_pcm_close(pcm_handle);
298                 snd_inited = 0;
299         }
300 }
301
302 /*
303 ==============
304 SNDDMA_Submit
305
306 Send sound to device if buffer isn't really the dma buffer
307 ===============
308 */
309 void SNDDMA_Submit(void)
310 {
311         int count = paintedtime - soundtime;
312         int avail;
313         int missed;
314         int state;
315         int hw_ptr;
316         int offset;
317
318         state = snd_pcm_state (pcm_handle);
319
320         switch (state) {
321         case SND_PCM_STATE_PREPARED:
322                 snd_pcm_mmap_forward (pcm_handle, count);
323                 snd_pcm_start (pcm_handle);
324                 break;
325         case SND_PCM_STATE_RUNNING:
326                 hw_ptr = get_hw_ptr();
327                 missed = hw_ptr - shm->samplepos / shm->channels;
328                 count -= missed;
329                 offset = snd_pcm_mmap_offset (pcm_handle);
330                 if (offset > hw_ptr)
331                         count -= (offset - hw_ptr);
332                 else
333                         count -= (setup.buffer_size - hw_ptr + offset);
334                 if (count < 0) {
335                         snd_pcm_rewind (pcm_handle, -count);
336                 } else if (count > 0) {
337                         avail = snd_pcm_avail_update(pcm_handle);
338                         if (avail > 0 && count > avail) {
339                                 snd_pcm_mmap_forward (pcm_handle, avail);
340                                 count -= avail;
341                         }
342                         snd_pcm_mmap_forward (pcm_handle, count);
343                 }
344                 break;
345         default:
346                 break;
347         }
348 }
349