]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - snd_alsa.c
implemented 7.1 audio, only works with SDL (attempted ALSA support but ALSA doesn...
[xonotic/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, j;
43         int                                     width;
44         int                                     channels;
45         unsigned int            rate;
46         snd_pcm_hw_params_t     *hw;
47         snd_pcm_sw_params_t     *sw;
48         snd_pcm_uframes_t       frag_size;
49
50         snd_pcm_hw_params_alloca (&hw);
51         snd_pcm_sw_params_alloca (&sw);
52
53 // COMMANDLINEOPTION: Linux ALSA Sound: -sndbits <number> sets sound precision to 8 or 16 bit (email me if you want others added)
54         width = 2;
55         if ((i=COM_CheckParm("-sndbits")) != 0)
56         {
57                 j = atoi(com_argv[i+1]);
58                 if (j == 16 || j == 8)
59                         width = j / 8;
60                 else
61                         Con_Printf("Error: invalid sample bits: %d\n", j);
62         }
63
64 // COMMANDLINEOPTION: Linux ALSA Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
65         rate = 44100;
66         if ((i=COM_CheckParm("-sndspeed")) != 0)
67         {
68                 j = atoi(com_argv[i+1]);
69                 if (j >= 1)
70                         rate = j;
71                 else
72                         Con_Printf("Error: invalid sample rate: %d\n", rate);
73         }
74
75         for (channels = 8;channels >= 1;channels--)
76         {
77                 if ((channels & 1) && channels != 1)
78                         continue;
79 // COMMANDLINEOPTION: Linux ALSA Sound: -sndmono sets sound output to mono
80                 if ((i=COM_CheckParm("-sndmono")) != 0)
81                         if (channels != 1)
82                                 continue;
83 // COMMANDLINEOPTION: Linux ALSA Sound: -sndstereo sets sound output to stereo
84                 if ((i=COM_CheckParm("-sndstereo")) != 0)
85                         if (channels != 2)
86                                 continue;
87
88 // COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
89                 if (channels == 8)
90                         pcmname = "surround71";
91                 else if (channels == 6)
92                         pcmname = "surround51";
93                 else if (channels == 4)
94                         pcmname = "surround40";
95                 else
96                         pcmname = "default";
97                 if ((i=COM_CheckParm("-sndpcm"))!=0)
98                         pcmname = com_argv[i+1];
99
100                 Con_Printf ("ALSA: Trying PCM %s.\n", pcmname);
101
102                 err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
103                 if (0 > err)
104                 {
105                         Con_Printf ("Error: audio open error: %s\n", snd_strerror (err));
106                         continue;
107                 }
108
109                 err = snd_pcm_hw_params_any (pcm, hw);
110                 if (0 > err)
111                 {
112                         Con_Printf ("ALSA: error setting hw_params_any. %s\n", snd_strerror (err));
113                         snd_pcm_close (pcm);
114                         continue;
115                 }
116
117                 err = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
118                 if (0 > err)
119                 {
120                         Con_Printf ("ALSA: Failure to set interleaved mmap PCM access. %s\n", snd_strerror (err));
121                         snd_pcm_close (pcm);
122                         continue;
123                 }
124
125                 err = snd_pcm_hw_params_set_format (pcm, hw, width == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16);
126                 if (0 > err)
127                 {
128                         Con_Printf ("ALSA: desired bits %i not supported by driver.  %s\n", width * 8, snd_strerror (err));
129                         snd_pcm_close (pcm);
130                         continue;
131                 }
132
133                 err = snd_pcm_hw_params_set_channels (pcm, hw, channels);
134                 if (0 > err)
135                 {
136                         Con_Printf ("ALSA: no usable channels. %s\n", snd_strerror (err));
137                         snd_pcm_close (pcm);
138                         continue;
139                 }
140
141                 err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
142                 if (0 > err)
143                 {
144                         Con_Printf ("ALSA: desired rate %i not supported by driver. %s\n", rate, snd_strerror (err));
145                         snd_pcm_close (pcm);
146                         continue;
147                 }
148
149                 frag_size = 64 * width * rate / 11025;
150                 err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
151                 if (0 > err)
152                 {
153                         Con_Printf ("ALSA: unable to set period size near %i. %s\n", (int) frag_size, snd_strerror (err));
154                         snd_pcm_close (pcm);
155                         continue;
156                 }
157                 err = snd_pcm_hw_params (pcm, hw);
158                 if (0 > err)
159                 {
160                         Con_Printf ("ALSA: unable to install hw params: %s\n", snd_strerror (err));
161                         snd_pcm_close (pcm);
162                         continue;
163                 }
164                 err = snd_pcm_sw_params_current (pcm, sw);
165                 if (0 > err)
166                 {
167                         Con_Printf ("ALSA: unable to determine current sw params. %s\n", snd_strerror (err));
168                         snd_pcm_close (pcm);
169                         continue;
170                 }
171                 err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
172                 if (0 > err)
173                 {
174                         Con_Printf ("ALSA: unable to set playback threshold. %s\n", snd_strerror (err));
175                         snd_pcm_close (pcm);
176                         continue;
177                 }
178                 err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
179                 if (0 > err)
180                 {
181                         Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", snd_strerror (err));
182                         snd_pcm_close (pcm);
183                         continue;
184                 }
185                 err = snd_pcm_sw_params (pcm, sw);
186                 if (0 > err)
187                 {
188                         Con_Printf ("ALSA: unable to install sw params. %s\n", snd_strerror (err));
189                         snd_pcm_close (pcm);
190                         continue;
191                 }
192
193                 err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
194                 if (0 > err)
195                 {
196                         Con_Printf ("ALSA: unable to get buffer size. %s\n", snd_strerror (err));
197                         snd_pcm_close (pcm);
198                         continue;
199                 }
200
201                 memset( (void*) shm, 0, sizeof(*shm) );
202                 shm->format.channels = channels;
203                 shm->format.width = width;
204                 shm->format.speed = rate;
205                 shm->samplepos = 0;
206                 shm->sampleframes = buffer_size;
207                 shm->samples = shm->sampleframes * shm->format.channels;
208                 SNDDMA_GetDMAPos ();            // sets shm->buffer
209
210                 snd_inited = 1;
211                 return true;
212         }
213         return false;
214 }
215
216 int SNDDMA_GetDMAPos (void)
217 {
218         const snd_pcm_channel_area_t *areas;
219         snd_pcm_uframes_t offset;
220         snd_pcm_uframes_t nframes = shm->sampleframes;
221
222         if (!snd_inited)
223                 return 0;
224
225         snd_pcm_avail_update (pcm);
226         snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
227         offset *= shm->format.channels;
228         nframes *= shm->format.channels;
229         shm->samplepos = offset;
230         shm->buffer = (unsigned char *)areas->addr;
231         return shm->samplepos;
232 }
233
234 void SNDDMA_Shutdown (void)
235 {
236         if (snd_inited) {
237                 snd_pcm_close (pcm);
238                 snd_inited = 0;
239         }
240 }
241
242 /*
243         SNDDMA_Submit
244
245         Send sound to device if buffer isn't really the dma buffer
246 */
247 void SNDDMA_Submit (void)
248 {
249         int                     state;
250         int                     count = paintedtime - soundtime;
251         const snd_pcm_channel_area_t *areas;
252         snd_pcm_uframes_t nframes;
253         snd_pcm_uframes_t offset;
254
255         nframes = count / shm->format.channels;
256
257         snd_pcm_avail_update (pcm);
258         snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
259
260         state = snd_pcm_state (pcm);
261
262         switch (state) {
263                 case SND_PCM_STATE_PREPARED:
264                         snd_pcm_mmap_commit (pcm, offset, nframes);
265                         snd_pcm_start (pcm);
266                         break;
267                 case SND_PCM_STATE_RUNNING:
268                         snd_pcm_mmap_commit (pcm, offset, nframes);
269                         break;
270                 default:
271                         break;
272         }
273 }
274
275 void *S_LockBuffer(void)
276 {
277         return shm->buffer;
278 }
279
280 void S_UnlockBuffer(void)
281 {
282 }