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