most of Q2's keyboard handling ported over - what this means: keypad is now separatel...
[divverent/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         shm=&sn;
194         memset((dma_t*)shm,0,sizeof(*shm));
195         shm->splitbuffer = 0;
196         shm->channels=stereo+1;
197         shm->submission_chunk=snd_pcm_hw_params_get_period_size(hw, 0); // don't mix less than this #
198         shm->samplepos=0;                       // in mono samples
199         shm->samplebits=bps;
200         buffer_size = snd_pcm_hw_params_get_buffer_size(hw); 
201         shm->samples=buffer_size*shm->channels; // mono samples in buffer
202         shm->speed=rate;
203         shm->buffer=(unsigned char*)mmap_areas->addr;
204         Con_Printf("%5d stereo\n", shm->channels - 1);
205         Con_Printf("%5d samples\n", shm->samples);
206         Con_Printf("%5d samplepos\n", shm->samplepos);
207         Con_Printf("%5d samplebits\n", shm->samplebits);
208         Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
209         Con_Printf("%5d speed\n", shm->speed);
210         Con_Printf("0x%x dma buffer\n", (int)shm->buffer);
211         Con_Printf("%5d total_channels\n", total_channels);
212
213         snd_inited=1;
214         return 1;
215  error:
216         snd_pcm_close(pcm);
217         return 0;
218 }
219
220 static inline int
221 get_hw_ptr(void)
222 {
223         size_t app_ptr;
224         snd_pcm_sframes_t delay;
225         int hw_ptr;
226
227         if (snd_pcm_state (pcm) != SND_PCM_STATE_RUNNING)
228                 return 0;
229         app_ptr = snd_pcm_mmap_offset (pcm);
230         snd_pcm_delay (pcm, &delay);
231         hw_ptr = app_ptr - delay;
232         if (hw_ptr < 0)
233                 hw_ptr += buffer_size;
234         return hw_ptr;
235 }
236
237 int SNDDMA_GetDMAPos(void)
238 {
239         int hw_ptr;
240
241         if (!snd_inited) return 0;
242
243         hw_ptr = get_hw_ptr();
244         hw_ptr *= shm->channels;
245         shm->samplepos = hw_ptr;
246         return shm->samplepos;
247 }
248
249 void SNDDMA_Shutdown(void)
250 {
251         if (snd_inited)
252         {
253                 snd_pcm_close(pcm);
254                 snd_inited = 0;
255         }
256 }
257
258 /*
259 ==============
260 SNDDMA_Submit
261
262 Send sound to device if buffer isn't really the dma buffer
263 ===============
264 */
265 void SNDDMA_Submit(void)
266 {
267         int count = paintedtime - soundtime;
268         int avail;
269         int missed;
270         int state;
271         int hw_ptr;
272         int offset;
273
274         state = snd_pcm_state (pcm);
275
276         switch (state) {
277         case SND_PCM_STATE_PREPARED:
278                 snd_pcm_mmap_forward (pcm, count);
279                 snd_pcm_start (pcm);
280                 break;
281         case SND_PCM_STATE_RUNNING:
282                 hw_ptr = get_hw_ptr();
283                 missed = hw_ptr - shm->samplepos / shm->channels;
284                 if (missed < 0)
285                         missed += buffer_size;
286                 count -= missed;
287                 offset = snd_pcm_mmap_offset (pcm);
288                 if (offset > hw_ptr)
289                         count -= (offset - hw_ptr);
290                 else
291                         count -= (buffer_size - hw_ptr + offset);
292                 if (count < 0) {
293                         snd_pcm_rewind (pcm, -count);
294                 } else {
295                         avail = snd_pcm_avail_update(pcm);
296                         if (avail < 0)
297                                 avail = buffer_size;
298                         if (count > avail)
299                                 count = avail;
300                         snd_pcm_mmap_forward (pcm, count);
301                 }
302                 break;
303         default:
304                 break;
305         }
306 }
307
308 void *S_LockBuffer(void)
309 {
310         return shm->buffer;
311 }
312
313 void S_UnlockBuffer(void)
314 {
315 }
316
317
318 void S_Open(void)
319 {
320 }
321
322 void S_Close(void)
323 {
324 }