2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 *(at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <alsa/asoundlib.h>
23 #include "../client/client.h"
24 #include "../client/snd_loc.h"
26 #define BUFFER_SAMPLES 4096
27 #define SUBMISSION_CHUNK BUFFER_SAMPLES / 2
29 static snd_pcm_t
*pcm_handle
;
30 static snd_pcm_hw_params_t
*hw_params
;
32 static struct sndinfo
* si
;
34 static int sample_bytes
;
35 static int buffer_bytes
;
44 * The sample rates which will be attempted.
46 static int RATES
[] = {
47 44100, 22050, 11025, 8000
51 * Initialize ALSA pcm device, and bind it to sndinfo.
53 qboolean
SNDDMA_Init(void){
61 sndbits
= Cvar_Get("sndbits", "16", CVAR_ARCHIVE
);
62 sndspeed
= Cvar_Get("sndspeed", "0", CVAR_ARCHIVE
);
63 sndchannels
= Cvar_Get("sndchannels", "2", CVAR_ARCHIVE
);
64 snddevice
= Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE
);
67 if(!strcmp(snddevice
->string
, "/dev/dsp")) //silly oss default
68 snddevice
->string
= "default";
70 if((err
= snd_pcm_open(&pcm_handle
, snddevice
->string
,
71 SND_PCM_STREAM_PLAYBACK
, SND_PCM_NONBLOCK
)) < 0)
73 Com_Printf("ALSA: cannot open device %s(%s)\n",
74 snddevice
->string
, snd_strerror(err
));
78 if((err
= snd_pcm_hw_params_malloc(&hw_params
)) < 0){
79 Com_Printf("ALSA: cannot allocate hw params(%s)\n",
84 if((err
= snd_pcm_hw_params_any(pcm_handle
, hw_params
)) < 0){
85 Com_Printf("ALSA: cannot init hw params(%s)\n", snd_strerror(err
));
86 snd_pcm_hw_params_free(hw_params
);
90 if((err
= snd_pcm_hw_params_set_access
91 (pcm_handle
, hw_params
, SND_PCM_ACCESS_RW_INTERLEAVED
)) < 0)
93 Com_Printf("ALSA: cannot set access(%s)\n", snd_strerror(err
));
94 snd_pcm_hw_params_free(hw_params
);
98 dma
.samplebits
= (int)sndbits
->value
;
99 if(dma
.samplebits
!= 8){ //try 16 by default
100 dma
.samplebits
= 16; //ensure this is set for other calculations
102 if((err
= snd_pcm_hw_params_set_format(pcm_handle
, hw_params
,
103 SND_PCM_FORMAT_S16
)) < 0){
104 Com_Printf("ALSA: 16 bit not supported, trying 8\n");
108 if(dma
.samplebits
== 8){ //or 8 if specifically asked to
109 if((err
= snd_pcm_hw_params_set_format(pcm_handle
, hw_params
,
110 SND_PCM_FORMAT_U8
)) < 0){
111 Com_Printf("ALSA: cannot set format(%s)\n", snd_strerror(err
));
112 snd_pcm_hw_params_free(hw_params
);
117 dma
.speed
= (int)sndspeed
->value
;
118 if(dma
.speed
){ //try specified rate
121 if((err
= snd_pcm_hw_params_set_rate_near(pcm_handle
, hw_params
, &r
, &dir
)) < 0)
122 Com_Printf("ALSA: cannot set rate %d(%s)\n", r
, snd_strerror(err
));
123 else { //rate succeeded, but is perhaps slightly different
125 Com_Printf("ALSA: rate %d not supported, using %d\n", sndspeed
->value
, r
);
129 if(!dma
.speed
){ //or all available ones
130 for(i
= 0; i
< sizeof(RATES
); i
++){
134 if((err
= snd_pcm_hw_params_set_rate_near(pcm_handle
, hw_params
, &r
, &dir
)) < 0)
135 Com_Printf("ALSA: cannot set rate %d(%s)\n", r
, snd_strerror(err
));
136 else { //rate succeeded, but is perhaps slightly different
139 Com_Printf("ALSA: rate %d not supported, using %d\n", RATES
[i
], r
);
144 if(!dma
.speed
){ //failed
145 Com_Printf("ALSA: cannot set rate\n");
146 snd_pcm_hw_params_free(hw_params
);
150 dma
.channels
= sndchannels
->value
;
151 if(dma
.channels
< 1 || dma
.channels
> 2)
152 dma
.channels
= 2; //ensure either stereo or mono
156 if((err
= snd_pcm_hw_params_set_channels(pcm_handle
, hw_params
,
159 Com_Printf("ALSA: cannot set channels %d(%s)\n",
160 sndchannels
->value
, snd_strerror(err
));
161 snd_pcm_hw_params_free(hw_params
);
165 p
= BUFFER_SAMPLES
/ dma
.channels
;
166 if((err
= snd_pcm_hw_params_set_period_size_near(pcm_handle
, hw_params
,
168 Com_Printf("ALSA: cannot set period size (%s)\n", snd_strerror(err
));
169 snd_pcm_hw_params_free(hw_params
);
172 else { //rate succeeded, but is perhaps slightly different
174 Com_Printf("ALSA: period %d not supported, using %d\n", (BUFFER_SAMPLES
/dma
.channels
), p
);
177 if((err
= snd_pcm_hw_params(pcm_handle
, hw_params
)) < 0){ //set params
178 Com_Printf("ALSA: cannot set params(%s)\n", snd_strerror(err
));
179 snd_pcm_hw_params_free(hw_params
);
183 sample_bytes
= dma
.samplebits
/ 8;
184 buffer_bytes
= BUFFER_SAMPLES
* sample_bytes
;
186 dma
.buffer
= malloc(buffer_bytes
); //allocate pcm frame buffer
187 memset(dma
.buffer
, 0, buffer_bytes
);
191 dma
.samples
= BUFFER_SAMPLES
;
192 dma
.submission_chunk
= SUBMISSION_CHUNK
;
194 snd_pcm_prepare(pcm_handle
);
200 * Returns the current sample position, if sound is running.
202 int SNDDMA_GetDMAPos(void){
205 return dma
.samplepos
;
207 Com_Printf("Sound not inizialized\n");
212 * Closes the ALSA pcm device and frees the dma buffer.
214 void SNDDMA_Shutdown(void){
217 snd_pcm_drop(pcm_handle
);
218 snd_pcm_close(pcm_handle
);
226 * Writes the dma buffer to the ALSA pcm device.
228 void SNDDMA_Submit(void){
235 s
= dma
.samplepos
* sample_bytes
;
236 start
= (void *)&dma
.buffer
[s
];
238 frames
= dma
.submission_chunk
/ dma
.channels
;
240 if((w
= snd_pcm_writei(pcm_handle
, start
, frames
)) < 0){ //write to card
241 snd_pcm_prepare(pcm_handle
); //xrun occured
245 dma
.samplepos
+= w
* dma
.channels
; //mark progress
247 if(dma
.samplepos
>= dma
.samples
)
248 dma
.samplepos
= 0; //wrap buffer
252 * Callback provided by the engine in case we need it. We don't.
254 void SNDDMA_BeginPainting(void){}