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