]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_alsa_0_9.c
cleaned up R_RenderView setup code a bit, and shuffled SCR_CalcRefdef stuff to SCR_Up...
[xonotic/darkplaces.git] / snd_alsa_0_9.c
1 /*
2         snd_alsa_0_9.c
3
4         Support for ALSA 0.9 sound driver (cvs development version)
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 "quakedef.h"
39 #include "sound.h"
40 #include "console.h"
41
42 static int snd_inited;
43
44 static snd_pcm_t *pcm;
45 static const snd_pcm_channel_area_t *mmap_areas;
46 static char *pcmname = NULL;
47 size_t buffer_size;
48
49 qboolean SNDDMA_Init(void)
50 {
51         int err,i;
52         int rate=-1,bps=-1,stereo=-1,frag_size;
53         snd_pcm_hw_params_t *hw;
54         snd_pcm_sw_params_t *sw;
55         snd_pcm_hw_params_alloca(&hw);
56         snd_pcm_sw_params_alloca(&sw);
57
58         if ((i=COM_CheckParm("-sndpcm"))!=0) {
59                 pcmname=com_argv[i+1];
60         }
61         if ((i=COM_CheckParm("-sndbits")) != 0) {
62                 bps = atoi(com_argv[i+1]);
63                 if (bps != 16 && bps != 8) {
64                         Con_Printf("Error: invalid sample bits: %d\n", i);
65                         return 0;
66                 }
67         }
68         if ((i=COM_CheckParm("-sndspeed")) != 0) {
69                 rate = atoi(com_argv[i+1]);
70                 if (rate!=44100 && rate!=22050 && rate!=11025) {
71                         Con_Printf("Error: invalid sample rate: %d\n", rate);
72                         return 0;
73                 }
74         }
75         if ((i=COM_CheckParm("-sndmono")) != 0) {
76                 stereo=0;
77         }
78         if ((i=COM_CheckParm("-sndstereo")) != 0) {
79                 stereo=1;
80         }
81         if (!pcmname)
82                 pcmname = "plug:0,0";
83         if ((err=snd_pcm_open(&pcm, pcmname,
84                              SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK))<0) {
85                 Con_Printf("Error: audio open error: %s\n", snd_strerror(err));
86                 return 0;
87         }
88
89         Con_Printf("Using PCM %s.\n", pcmname);
90         snd_pcm_hw_params_any(pcm, hw);
91
92
93         switch (rate) {
94         case -1:
95                 if (snd_pcm_hw_params_set_rate_near(pcm, hw, 44100, 0) >= 0) {
96                         frag_size = 256; /* assuming stereo 8 bit */
97                         rate = 44100;
98                 } else if (snd_pcm_hw_params_set_rate_near(pcm, hw, 22050, 0) >= 0) {
99                         frag_size = 128; /* assuming stereo 8 bit */
100                         rate = 22050;
101                 } else if (snd_pcm_hw_params_set_rate_near(pcm, hw, 11025, 0) >= 0) {
102                         frag_size = 64; /* assuming stereo 8 bit */
103                         rate = 11025;
104                 } else {
105                         Con_Printf("ALSA: no useable rates\n");
106                         goto error;
107                 }
108                 break;
109         case 11025:
110         case 22050:
111         case 44100:
112                 if (snd_pcm_hw_params_set_rate_near(pcm, hw, rate, 0) >= 0) {
113                         frag_size = 64 * rate / 11025; /* assuming stereo 8 bit */
114                         break;
115                 }
116                 /* Fall through */
117         default:
118                 Con_Printf("ALSA: desired rate not supported\n");
119                 goto error;
120         }
121
122         switch (bps) {
123         case -1:
124                 if (snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_S16_LE) >= 0) {
125                         bps = 16;
126                 } else if (snd_pcm_hw_params_set_format(pcm, hw, SND_PCM_FORMAT_U8) >= 0) {
127                         bps = 8;
128                 } else {
129                         Con_Printf("ALSA: no useable formats\n");
130                         goto error;
131                 }
132                 break;
133         case 8:
134         case 16:
135                  if (snd_pcm_hw_params_set_format(pcm, hw,
136                                                   bps == 8 ? SND_PCM_FORMAT_U8 :
137                                                   SND_PCM_FORMAT_S16) >= 0) {
138                          break;
139                  }
140                 /* Fall through */
141         default:
142                 Con_Printf("ALSA: desired format not supported\n");
143                 goto error;
144         }
145
146         if (snd_pcm_hw_params_set_access(pcm, hw,
147                                          SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
148                 Con_Printf("ALSA: interleaved is not supported\n");
149                 goto error;
150         }
151
152         switch (stereo) {
153         case -1:
154                 if (snd_pcm_hw_params_set_channels(pcm, hw, 2) >= 0) {
155                         stereo = 1;
156                 } else if (snd_pcm_hw_params_set_channels(pcm, hw, 1) >= 0) {
157                         stereo = 0;
158                 } else {
159                         Con_Printf("ALSA: no useable channels\n");
160                         goto error;
161                 }
162                 break;
163         case 0:
164         case 1:
165                  if (snd_pcm_hw_params_set_channels(pcm, hw, stereo ? 2 : 1) >= 0)
166                          break;
167                  /* Fall through */
168         default:
169                 Con_Printf("ALSA: desired channels not supported\n");
170                 goto error;
171         }
172
173         snd_pcm_hw_params_set_period_size_near(pcm, hw, frag_size, 0);
174
175         err = snd_pcm_hw_params(pcm, hw);
176         if (err < 0) {
177                 Con_Printf("ALSA: unable to install hw params\n");
178                 goto error;
179         }
180
181         snd_pcm_sw_params_current(pcm, sw);
182         snd_pcm_sw_params_set_start_mode(pcm, sw, SND_PCM_START_EXPLICIT);
183         snd_pcm_sw_params_set_xrun_mode(pcm, sw, SND_PCM_XRUN_NONE);
184
185         err = snd_pcm_sw_params(pcm, sw);
186         if (err < 0) {
187                 Con_Printf("ALSA: unable to install sw params\n");
188                 goto error;
189         }
190
191         mmap_areas = snd_pcm_mmap_running_areas(pcm);
192
193         memset((dma_t*)shm,0,sizeof(*shm));
194         shm->channels=stereo+1;
195         shm->samplepos=0;                       // in mono samples
196         shm->samplebits=bps;
197         buffer_size = snd_pcm_hw_params_get_buffer_size(hw);
198         shm->samples=buffer_size*shm->channels; // mono samples in buffer
199         shm->speed=rate;
200         shm->buffer=(unsigned char*)mmap_areas->addr;
201         Con_Printf("%5d stereo\n", shm->channels - 1);
202         Con_Printf("%5d samples\n", shm->samples);
203         Con_Printf("%5d samplepos\n", shm->samplepos);
204         Con_Printf("%5d samplebits\n", shm->samplebits);
205         Con_Printf("%5d speed\n", shm->speed);
206         Con_Printf("0x%x dma buffer\n", (int)shm->buffer);
207         Con_Printf("%5d total_channels\n", total_channels);
208
209         snd_inited=1;
210         return 1;
211  error:
212         snd_pcm_close(pcm);
213         return 0;
214 }
215
216 static inline int
217 get_hw_ptr(void)
218 {
219         size_t app_ptr;
220         snd_pcm_sframes_t delay;
221         int hw_ptr;
222
223         if (snd_pcm_state (pcm) != SND_PCM_STATE_RUNNING)
224                 return 0;
225         app_ptr = snd_pcm_mmap_offset (pcm);
226         snd_pcm_delay (pcm, &delay);
227         hw_ptr = app_ptr - delay;
228         if (hw_ptr < 0)
229                 hw_ptr += buffer_size;
230         return hw_ptr;
231 }
232
233 int SNDDMA_GetDMAPos(void)
234 {
235         int hw_ptr;
236
237         if (!snd_inited) return 0;
238
239         hw_ptr = get_hw_ptr();
240         hw_ptr *= shm->channels;
241         shm->samplepos = hw_ptr;
242         return shm->samplepos;
243 }
244
245 void SNDDMA_Shutdown(void)
246 {
247         if (snd_inited)
248         {
249                 snd_pcm_close(pcm);
250                 snd_inited = 0;
251         }
252 }
253
254 /*
255 ==============
256 SNDDMA_Submit
257
258 Send sound to device if buffer isn't really the dma buffer
259 ===============
260 */
261 void SNDDMA_Submit(void)
262 {
263         int count = paintedtime - soundtime;
264         int avail;
265         int missed;
266         int state;
267         int hw_ptr;
268         int offset;
269
270         state = snd_pcm_state (pcm);
271
272         switch (state) {
273         case SND_PCM_STATE_PREPARED:
274                 snd_pcm_mmap_forward (pcm, count);
275                 snd_pcm_start (pcm);
276                 break;
277         case SND_PCM_STATE_RUNNING:
278                 hw_ptr = get_hw_ptr();
279                 missed = hw_ptr - shm->samplepos / shm->channels;
280                 if (missed < 0)
281                         missed += buffer_size;
282                 count -= missed;
283                 offset = snd_pcm_mmap_offset (pcm);
284                 if (offset > hw_ptr)
285                         count -= (offset - hw_ptr);
286                 else
287                         count -= (buffer_size - hw_ptr + offset);
288                 if (count < 0) {
289                         snd_pcm_rewind (pcm, -count);
290                 } else {
291                         avail = snd_pcm_avail_update(pcm);
292                         if (avail < 0)
293                                 avail = buffer_size;
294                         if (count > avail)
295                                 count = avail;
296                         snd_pcm_mmap_forward (pcm, count);
297                 }
298                 break;
299         default:
300                 break;
301         }
302 }
303
304 void *S_LockBuffer(void)
305 {
306         return shm->buffer;
307 }
308
309 void S_UnlockBuffer(void)
310 {
311 }
312