6 Copyright (C) 1999,2000 contributors of the QuakeForge project
7 Please see the file "AUTHORS" for a list of contributors
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.
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.
18 See the GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to:
23 Free Software Foundation, Inc.
24 59 Temple Place - Suite 330
25 Boston, MA 02111-1307, USA
36 #include <sys/soundcard.h>
37 #include <linux/soundcard.h>
38 #include <sys/asoundlib.h>
41 # define MAP_FAILED ((void*)-1)
46 static int snd_inited;
48 static snd_pcm_t *pcm_handle;
49 static struct snd_pcm_channel_info cinfo;
50 static struct snd_pcm_channel_params params;
51 static struct snd_pcm_channel_setup setup;
52 static snd_pcm_mmap_control_t *mmap_control = NULL;
53 static char *mmap_data = NULL;
54 static int card=-1,dev=-1;
56 int check_card(int card)
59 snd_ctl_hw_info_t info;
62 if ((rc = snd_ctl_open(&handle, card)) < 0) {
63 Con_Printf("Error: control open (%i): %s\n", card, snd_strerror(rc));
66 if ((rc = snd_ctl_hw_info(handle, &info)) < 0) {
67 Con_Printf("Error: control hardware info (%i): %s\n", card,
69 snd_ctl_close(handle);
72 snd_ctl_close(handle);
74 for (dev = 0; dev < info.pcmdevs; dev++) {
75 if ((rc=snd_pcm_open(&pcm_handle,card,dev,
77 | SND_PCM_OPEN_NONBLOCK))==0) {
82 if (dev>=0 && dev <info.pcmdevs) {
83 if ((rc=snd_pcm_open(&pcm_handle,card,dev,
85 | SND_PCM_OPEN_NONBLOCK))==0) {
93 qboolean SNDDMA_Init(void)
97 int rate,format,bps,stereo,frag_size;
100 mask = snd_cards_mask();
102 Con_Printf("No sound cards detected\n");
105 if ((i=COM_CheckParm("-sndcard"))!=0) {
106 card=atoi(com_argv[i+1]);
108 if ((i=COM_CheckParm("-snddev"))!=0) {
109 dev=atoi(com_argv[i+1]);
112 for (card=0; card<SND_CARDS; card++) {
113 if (!(mask & (1<<card)))
129 if ((rc=snd_pcm_open(&pcm_handle,card,dev,
130 SND_PCM_OPEN_PLAYBACK
131 | SND_PCM_OPEN_NONBLOCK))<0) {
132 Con_Printf("Error: audio open error: %s\n", snd_strerror(rc));
138 Con_Printf("Error: audio open error: %s\n", snd_strerror(rc));
142 Con_Printf("Using card %d, device %d.\n", card, dev);
143 memset(&cinfo, 0, sizeof(cinfo));
144 cinfo.channel = SND_PCM_CHANNEL_PLAYBACK;
145 snd_pcm_channel_info(pcm_handle, &cinfo);
146 Con_Printf("%08x %08x %08x\n",cinfo.flags,cinfo.formats,cinfo.rates);
147 if (cinfo.rates & SND_PCM_RATE_44100) {
149 frag_size=512; /* assuming stereo 8 bit */
150 } else if (cinfo.rates & SND_PCM_RATE_22050) {
152 frag_size=256; /* assuming stereo 8 bit */
153 } else if (cinfo.rates & SND_PCM_RATE_11025) {
155 frag_size=128; /* assuming stereo 8 bit */
157 Con_Printf("ALSA: desired rates not supported\n");
160 if (cinfo.formats & SND_PCM_FMT_S16_LE) {
161 format=SND_PCM_SFMT_S16_LE;
164 } else if (cinfo.formats & SND_PCM_FMT_U8) {
165 format=SND_PCM_SFMT_U8;
168 Con_Printf("ALSA: desired formats not supported\n");
171 if (cinfo.max_voices>=2) {
178 // err_msg="audio flush";
179 // if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0)
181 err_msg="audio munmap";
182 if ((rc=snd_pcm_munmap(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0)
185 memset(¶ms, 0, sizeof(params));
186 params.channel = SND_PCM_CHANNEL_PLAYBACK;
187 params.mode = SND_PCM_MODE_BLOCK;
188 params.format.interleave=1;
189 params.format.format=format;
190 params.format.rate=rate;
191 params.format.voices=stereo+1;
192 params.start_mode = SND_PCM_START_GO;
193 params.stop_mode = SND_PCM_STOP_ROLLOVER;
194 params.buf.block.frag_size=frag_size;
195 params.buf.block.frags_min=1;
196 params.buf.block.frags_max=-1;
197 // err_msg="audio flush";
198 // if ((rc=snd_pcm_channel_flush(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0)
200 err_msg="audio params";
201 if ((rc=snd_pcm_channel_params(pcm_handle, ¶ms))<0)
204 err_msg="audio mmap";
205 if ((rc=snd_pcm_mmap(pcm_handle, SND_PCM_CHANNEL_PLAYBACK, &mmap_control, (void **)&mmap_data))<0)
207 err_msg="audio prepare";
208 if ((rc=snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0)
211 memset(&setup, 0, sizeof(setup));
212 setup.mode = SND_PCM_MODE_BLOCK;
213 setup.channel = SND_PCM_CHANNEL_PLAYBACK;
214 err_msg="audio setup";
215 if ((rc=snd_pcm_channel_setup(pcm_handle, &setup))<0)
219 memset((dma_t*)shm,0,sizeof(*shm));
220 shm->splitbuffer = 0;
221 shm->channels=setup.format.voices;
222 shm->submission_chunk=128; // don't mix less than this #
223 shm->samplepos=0; // in mono samples
224 shm->samplebits=setup.format.format==SND_PCM_SFMT_S16_LE?16:8;
225 shm->samples=setup.buf.block.frags*setup.buf.block.frag_size/(shm->samplebits/8); // mono samples in buffer
226 shm->speed=setup.format.rate;
227 shm->buffer=(unsigned char*)mmap_data;
228 Con_Printf("%5d stereo\n", shm->channels - 1);
229 Con_Printf("%5d samples\n", shm->samples);
230 Con_Printf("%5d samplepos\n", shm->samplepos);
231 Con_Printf("%5d samplebits\n", shm->samplebits);
232 Con_Printf("%5d submission_chunk\n", shm->submission_chunk);
233 Con_Printf("%5d speed\n", shm->speed);
234 Con_Printf("0x%x dma buffer\n", (int)shm->buffer);
235 Con_Printf("%5d total_channels\n", total_channels);
240 Con_Printf("Error: %s: %s\n", err_msg, snd_strerror(rc));
242 snd_pcm_close(pcm_handle);
246 int SNDDMA_GetDMAPos(void)
248 if (!snd_inited) return 0;
249 shm->samplepos=(mmap_control->status.frag_io+1)*setup.buf.block.frag_size/(shm->samplebits/8);
250 return shm->samplepos;
253 void SNDDMA_Shutdown(void)
257 snd_pcm_close(pcm_handle);
266 Send sound to device if buffer isn't really the dma buffer
269 void SNDDMA_Submit(void)
271 int count=paintedtime-soundtime;
275 count+=setup.buf.block.frag_size-1;
276 count/=setup.buf.block.frag_size;
277 s=soundtime/setup.buf.block.frag_size;
280 mmap_control->fragments[i % setup.buf.block.frags].data=1;
281 switch (mmap_control->status.status) {
282 case SND_PCM_STATUS_PREPARED:
283 if ((rc=snd_pcm_channel_go(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) {
284 fprintf(stderr, "unable to start playback. %s\n",
289 case SND_PCM_STATUS_RUNNING:
291 case SND_PCM_STATUS_UNDERRUN:
292 if ((rc=snd_pcm_plugin_prepare(pcm_handle, SND_PCM_CHANNEL_PLAYBACK))<0) {
293 fprintf(stderr, "underrun: playback channel prepare error. %s\n",