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