eliminated snd_inited and sound_started variables, now uses shm != NULL checks instead
[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
87 // COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
88                 if (channels == 8)
89                         pcmname = "surround71";
90                 else if (channels == 6)
91                         pcmname = "surround51";
92                 else if (channels == 4)
93                         pcmname = "surround40";
94                 else
95                         pcmname = "default";
96                 if ((i=COM_CheckParm("-sndpcm"))!=0)
97                         pcmname = com_argv[i+1];
98
99                 Con_Printf ("ALSA: Trying PCM %s.\n", pcmname);
100
101                 err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
102                 if (0 > err)
103                 {
104                         Con_Printf ("Error: audio open error: %s\n", snd_strerror (err));
105                         continue;
106                 }
107
108                 err = snd_pcm_hw_params_any (pcm, hw);
109                 if (0 > err)
110                 {
111                         Con_Printf ("ALSA: error setting hw_params_any. %s\n", snd_strerror (err));
112                         snd_pcm_close (pcm);
113                         continue;
114                 }
115
116                 err = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
117                 if (0 > err)
118                 {
119                         Con_Printf ("ALSA: Failure to set interleaved mmap PCM access. %s\n", snd_strerror (err));
120                         snd_pcm_close (pcm);
121                         continue;
122                 }
123
124                 err = snd_pcm_hw_params_set_format (pcm, hw, width == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16);
125                 if (0 > err)
126                 {
127                         Con_Printf ("ALSA: desired bits %i not supported by driver.  %s\n", width * 8, snd_strerror (err));
128                         snd_pcm_close (pcm);
129                         continue;
130                 }
131
132                 err = snd_pcm_hw_params_set_channels (pcm, hw, channels);
133                 if (0 > err)
134                 {
135                         Con_Printf ("ALSA: no usable channels. %s\n", snd_strerror (err));
136                         snd_pcm_close (pcm);
137                         continue;
138                 }
139
140                 err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
141                 if (0 > err)
142                 {
143                         Con_Printf ("ALSA: desired rate %i not supported by driver. %s\n", rate, snd_strerror (err));
144                         snd_pcm_close (pcm);
145                         continue;
146                 }
147
148                 frag_size = 64 * width * rate / 11025;
149                 err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
150                 if (0 > err)
151                 {
152                         Con_Printf ("ALSA: unable to set period size near %i. %s\n", (int) frag_size, snd_strerror (err));
153                         snd_pcm_close (pcm);
154                         continue;
155                 }
156                 err = snd_pcm_hw_params (pcm, hw);
157                 if (0 > err)
158                 {
159                         Con_Printf ("ALSA: unable to install hw params: %s\n", snd_strerror (err));
160                         snd_pcm_close (pcm);
161                         continue;
162                 }
163                 err = snd_pcm_sw_params_current (pcm, sw);
164                 if (0 > err)
165                 {
166                         Con_Printf ("ALSA: unable to determine current sw params. %s\n", snd_strerror (err));
167                         snd_pcm_close (pcm);
168                         continue;
169                 }
170                 err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
171                 if (0 > err)
172                 {
173                         Con_Printf ("ALSA: unable to set playback threshold. %s\n", snd_strerror (err));
174                         snd_pcm_close (pcm);
175                         continue;
176                 }
177                 err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
178                 if (0 > err)
179                 {
180                         Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", snd_strerror (err));
181                         snd_pcm_close (pcm);
182                         continue;
183                 }
184                 err = snd_pcm_sw_params (pcm, sw);
185                 if (0 > err)
186                 {
187                         Con_Printf ("ALSA: unable to install sw params. %s\n", snd_strerror (err));
188                         snd_pcm_close (pcm);
189                         continue;
190                 }
191
192                 err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
193                 if (0 > err)
194                 {
195                         Con_Printf ("ALSA: unable to get buffer size. %s\n", snd_strerror (err));
196                         snd_pcm_close (pcm);
197                         continue;
198                 }
199
200                 memset( (void*) shm, 0, sizeof(*shm) );
201                 shm->format.channels = channels;
202                 shm->format.width = width;
203                 shm->format.speed = rate;
204                 shm->samplepos = 0;
205                 shm->sampleframes = buffer_size;
206                 shm->samples = shm->sampleframes * shm->format.channels;
207                 SNDDMA_GetDMAPos ();            // sets shm->buffer
208
209                 return true;
210         }
211         return false;
212 }
213
214 int SNDDMA_GetDMAPos (void)
215 {
216         const snd_pcm_channel_area_t *areas;
217         snd_pcm_uframes_t offset;
218         snd_pcm_uframes_t nframes = shm->sampleframes;
219
220         if (!shm)
221                 return 0;
222
223         snd_pcm_avail_update (pcm);
224         snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
225         offset *= shm->format.channels;
226         nframes *= shm->format.channels;
227         shm->samplepos = offset;
228         shm->buffer = (unsigned char *)areas->addr;
229         return shm->samplepos;
230 }
231
232 void SNDDMA_Shutdown (void)
233 {
234         snd_pcm_close (pcm);
235 }
236
237 /*
238         SNDDMA_Submit
239
240         Send sound to device if buffer isn't really the dma buffer
241 */
242 void SNDDMA_Submit (void)
243 {
244         int                     state;
245         int                     count = paintedtime - soundtime;
246         const snd_pcm_channel_area_t *areas;
247         snd_pcm_uframes_t nframes;
248         snd_pcm_uframes_t offset;
249
250         nframes = count / shm->format.channels;
251
252         snd_pcm_avail_update (pcm);
253         snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
254
255         state = snd_pcm_state (pcm);
256
257         switch (state) {
258                 case SND_PCM_STATE_PREPARED:
259                         snd_pcm_mmap_commit (pcm, offset, nframes);
260                         snd_pcm_start (pcm);
261                         break;
262                 case SND_PCM_STATE_RUNNING:
263                         snd_pcm_mmap_commit (pcm, offset, nframes);
264                         break;
265                 default:
266                         break;
267         }
268 }
269
270 void *S_LockBuffer(void)
271 {
272         return shm->buffer;
273 }
274
275 void S_UnlockBuffer(void)
276 {
277 }