Still 378 items.
[divverent/darkplaces.git] / snd_alsa.c
1 /*
2         snd_alsa.c
3
4         Support for the ALSA 1.0.1 sound driver
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 */
28
29 #include <alsa/asoundlib.h>
30
31 #include "quakedef.h"
32
33 static int                      snd_inited;
34 static int                      snd_blocked = 0;
35 static snd_pcm_uframes_t buffer_size;
36
37 static const char  *pcmname = NULL;
38 static snd_pcm_t   *pcm;
39
40 qboolean SNDDMA_Init (void)
41 {
42         int                                      err, i;
43         int                                      bps = -1, stereo = -1;
44         unsigned int             rate = 0;
45         snd_pcm_hw_params_t     *hw;
46         snd_pcm_sw_params_t     *sw;
47         snd_pcm_uframes_t        frag_size;
48
49         snd_pcm_hw_params_alloca (&hw);
50         snd_pcm_sw_params_alloca (&sw);
51
52         if ((i=COM_CheckParm("-sndpcm"))!=0)
53                 pcmname=com_argv[i+1];
54         if (!pcmname)
55                 pcmname = "default";
56
57         if ((i=COM_CheckParm("-sndbits")) != 0)
58         {
59                 bps = atoi(com_argv[i+1]);
60                 if (bps != 16 && bps != 8)
61                 {
62                         Con_Printf("Error: invalid sample bits: %d\n", bps);
63                         return false;
64                 }
65         }
66
67         if ((i=COM_CheckParm("-sndspeed")) != 0)
68         {
69                 rate = atoi(com_argv[i+1]);
70                 if (rate!=44100 && rate!=22050 && rate!=11025)
71                 {
72                         Con_Printf("Error: invalid sample rate: %d\n", rate);
73                         return false;
74                 }
75         }
76
77         if ((i=COM_CheckParm("-sndmono")) != 0)
78                 stereo=0;
79         if ((i=COM_CheckParm("-sndstereo")) != 0)
80                 stereo=1;
81
82         err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK,
83                                                   SND_PCM_NONBLOCK);
84         if (0 > err) {
85                 Sys_Printf ("Error: audio open error: %s\n", snd_strerror (err));
86                 return 0;
87         }
88         Sys_Printf ("ALSA: Using PCM %s.\n", pcmname);
89
90         err = snd_pcm_hw_params_any (pcm, hw);
91         if (0 > err) {
92                 Sys_Printf ("ALSA: error setting hw_params_any. %s\n",
93                                         snd_strerror (err));
94                 goto error;
95         }
96
97         err = snd_pcm_hw_params_set_access (pcm, hw,
98                                                                                   SND_PCM_ACCESS_MMAP_INTERLEAVED);
99         if (0 > err) {
100                 Sys_Printf ("ALSA: Failure to set noninterleaved PCM access. %s\n"
101                                         "Note: Interleaved is not supported\n",
102                                         snd_strerror (err));
103                 goto error;
104         }
105
106         switch (bps) {
107                 case -1:
108                         err = snd_pcm_hw_params_set_format (pcm, hw,
109                                                                                                   SND_PCM_FORMAT_S16);
110                         if (0 <= err) {
111                                 bps = 16;
112                         } else if (0 <= (err = snd_pcm_hw_params_set_format (pcm, hw,
113                                                                                                                  SND_PCM_FORMAT_U8))) {
114                                 bps = 8;
115                         } else {
116                                 Sys_Printf ("ALSA: no useable formats. %s\n",
117                                                         snd_strerror (err));
118                                 goto error;
119                         }
120                         break;
121                 case 8:
122                 case 16:
123                         err = snd_pcm_hw_params_set_format (pcm, hw, bps == 8 ?
124                                                                                                   SND_PCM_FORMAT_U8 :
125                                                                                                   SND_PCM_FORMAT_S16);
126                         if (0 > err) {
127                                 Sys_Printf ("ALSA: no usable formats. %s\n",
128                                                         snd_strerror (err));
129                                 goto error;
130                         }
131                         break;
132                 default:
133                         Sys_Printf ("ALSA: desired format not supported\n");
134                         goto error;
135         }
136
137         switch (stereo) {
138                 case -1:
139                         err = snd_pcm_hw_params_set_channels (pcm, hw, 2);
140                         if (0 <= err) {
141                                 stereo = 1;
142                         } else if (0 <= (err = snd_pcm_hw_params_set_channels (pcm, hw,
143                                                                                                                                          1))) {
144                                 stereo = 0;
145                         } else {
146                                 Sys_Printf ("ALSA: no usable channels. %s\n",
147                                                         snd_strerror (err));
148                                 goto error;
149                         }
150                         break;
151                 case 0:
152                 case 1:
153                         err = snd_pcm_hw_params_set_channels (pcm, hw, stereo ? 2 : 1);
154                         if (0 > err) {
155                                 Sys_Printf ("ALSA: no usable channels. %s\n",
156                                                         snd_strerror (err));
157                                 goto error;
158                         }
159                         break;
160                 default:
161                         Sys_Printf ("ALSA: desired channels not supported\n");
162                         goto error;
163         }
164
165         switch (rate) {
166                 case 0:
167                         rate = 44100;
168                         err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
169                         if (0 <= err) {
170                                 frag_size = 32 * bps;
171                         } else {
172                                 rate = 22050;
173                                 err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
174                                 if (0 <= err) {
175                                         frag_size = 16 * bps;
176                                 } else {
177                                         rate = 11025;
178                                         err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate,
179                                                                                                                          0);
180                                         if (0 <= err) {
181                                                 frag_size = 8 * bps;
182                                         } else {
183                                                 Sys_Printf ("ALSA: no usable rates. %s\n",
184                                                                         snd_strerror (err));
185                                                 goto error;
186                                         }
187                                 }
188                         }
189                         break;
190                 case 11025:
191                 case 22050:
192                 case 44100:
193                         err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
194                         if (0 > err) {
195                                 Sys_Printf ("ALSA: desired rate %i not supported. %s\n", rate,
196                                                         snd_strerror (err));
197                                 goto error;
198                         }
199                         frag_size = 8 * bps * rate / 11025;
200                         break;
201                 default:
202                         Sys_Printf ("ALSA: desired rate %i not supported.\n", rate);
203                         goto error;
204         }
205
206         err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
207         if (0 > err) {
208                 Sys_Printf ("ALSA: unable to set period size near %i. %s\n",
209                                         (int) frag_size, snd_strerror (err));
210                 goto error;
211         }
212         err = snd_pcm_hw_params (pcm, hw);
213         if (0 > err) {
214                 Sys_Printf ("ALSA: unable to install hw params: %s\n",
215                                         snd_strerror (err));
216                 goto error;
217         }
218         err = snd_pcm_sw_params_current (pcm, sw);
219         if (0 > err) {
220                 Sys_Printf ("ALSA: unable to determine current sw params. %s\n",
221                                         snd_strerror (err));
222                 goto error;
223         }
224         err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
225         if (0 > err) {
226                 Sys_Printf ("ALSA: unable to set playback threshold. %s\n",
227                                         snd_strerror (err));
228                 goto error;
229         }
230         err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
231         if (0 > err) {
232                 Sys_Printf ("ALSA: unable to set playback stop threshold. %s\n",
233                                         snd_strerror (err));
234                 goto error;
235         }
236         err = snd_pcm_sw_params (pcm, sw);
237         if (0 > err) {
238                 Sys_Printf ("ALSA: unable to install sw params. %s\n",
239                                         snd_strerror (err));
240                 goto error;
241         }
242
243         shm->format.channels = stereo + 1;
244         shm->samplepos = 0;
245         shm->format.width = bps / 8;
246
247         err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
248         if (0 > err) {
249                 Sys_Printf ("ALSA: unable to get buffer size. %s\n",
250                                         snd_strerror (err));
251                 goto error;
252         }
253
254         shm->samples = buffer_size * shm->format.channels;              // mono samples in buffer
255         shm->format.speed = rate;
256         SNDDMA_GetDMAPos ();            // sets shm->buffer
257
258         snd_inited = 1;
259         return true;
260
261 error:
262         snd_pcm_close (pcm);
263         return false;
264 }
265
266 int SNDDMA_GetDMAPos (void)
267 {
268         const snd_pcm_channel_area_t *areas;
269         snd_pcm_uframes_t offset;
270         snd_pcm_uframes_t nframes = shm->samples/shm->format.channels;
271
272         if (!snd_inited)
273                 return 0;
274
275         snd_pcm_avail_update (pcm);
276         snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
277         offset *= shm->format.channels;
278         nframes *= shm->format.channels;
279         shm->samplepos = offset;
280         shm->buffer = areas->addr;
281         return shm->samplepos;
282 }
283
284 void SNDDMA_Shutdown (void)
285 {
286         if (snd_inited) {
287                 snd_pcm_close (pcm);
288                 snd_inited = 0;
289         }
290 }
291
292 /*
293         SNDDMA_Submit
294
295         Send sound to device if buffer isn't really the dma buffer
296 */
297 void SNDDMA_Submit (void)
298 {
299         int                     state;
300         int                     count = paintedtime - soundtime;
301         const snd_pcm_channel_area_t *areas;
302         snd_pcm_uframes_t nframes;
303         snd_pcm_uframes_t offset;
304
305         if (snd_blocked)
306                 return;
307
308         nframes = count / shm->format.channels;
309
310         snd_pcm_avail_update (pcm);
311         snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
312
313         state = snd_pcm_state (pcm);
314
315         switch (state) {
316                 case SND_PCM_STATE_PREPARED:
317                         snd_pcm_mmap_commit (pcm, offset, nframes);
318                         snd_pcm_start (pcm);
319                         break;
320                 case SND_PCM_STATE_RUNNING:
321                         snd_pcm_mmap_commit (pcm, offset, nframes);
322                         break;
323                 default:
324                         break;
325         }
326 }
327
328 void *S_LockBuffer(void)
329 {
330         return shm->buffer;
331 }
332
333 void S_UnlockBuffer(void)
334 {
335 }