Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

linux_snd.c (7233B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #include <unistd.h>
     23 #include <fcntl.h>
     24 #include <stdlib.h>
     25 #include <sys/types.h>
     26 #include <sys/ioctl.h>
     27 #include <sys/mman.h>
     28 #include <sys/shm.h>
     29 #include <sys/wait.h>
     30 #ifdef __linux__ // rb0101023 - guard this
     31 #include <linux/soundcard.h>
     32 #endif
     33 #ifdef __FreeBSD__ // rb0101023 - added
     34 #include <sys/soundcard.h>
     35 #endif
     36 #include <stdio.h>
     37 
     38 #include "../game/q_shared.h"
     39 #include "../client/snd_local.h"
     40 
     41 int audio_fd;
     42 int snd_inited=0;
     43 
     44 cvar_t *sndbits;
     45 cvar_t *sndspeed;
     46 cvar_t *sndchannels;
     47 
     48 cvar_t *snddevice;
     49 
     50 /* Some devices may work only with 48000 */
     51 static int tryrates[] = { 22050, 11025, 44100, 48000, 8000 };
     52 
     53 static qboolean use_custom_memset = qfalse;
     54 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371 
     55 void Snd_Memset (void* dest, const int val, const size_t count)
     56 {
     57   int *pDest;
     58   int i, iterate;
     59 
     60   if (!use_custom_memset)
     61   {
     62     Com_Memset(dest,val,count);
     63     return;
     64   }
     65   iterate = count / sizeof(int);
     66   pDest = (int*)dest;
     67   for(i=0; i<iterate; i++)
     68   {
     69     pDest[i] = val;
     70   }
     71 }
     72 
     73 qboolean SNDDMA_Init(void)
     74 {
     75 	int rc;
     76     int fmt;
     77 	int tmp;
     78     int i;
     79     // char *s; // bk001204 - unused
     80 	struct audio_buf_info info;
     81 	int caps;
     82 	extern uid_t saved_euid;
     83 
     84 	if (snd_inited)
     85 		return 1;
     86 
     87 	if (!snddevice) {
     88 		sndbits = Cvar_Get("sndbits", "16", CVAR_ARCHIVE);
     89 		sndspeed = Cvar_Get("sndspeed", "0", CVAR_ARCHIVE);
     90 		sndchannels = Cvar_Get("sndchannels", "2", CVAR_ARCHIVE);
     91 		snddevice = Cvar_Get("snddevice", "/dev/dsp", CVAR_ARCHIVE);
     92 	}
     93 
     94 	// open /dev/dsp, confirm capability to mmap, and get size of dma buffer
     95 	if (!audio_fd) {
     96 		seteuid(saved_euid);
     97 
     98 		audio_fd = open(snddevice->string, O_RDWR);
     99 
    100 		seteuid(getuid());
    101 
    102 		if (audio_fd < 0) {
    103 			perror(snddevice->string);
    104 			Com_Printf("Could not open %s\n", snddevice->string);
    105 			return 0;
    106 			}
    107 	}
    108 
    109 	if (ioctl(audio_fd, SNDCTL_DSP_GETCAPS, &caps) == -1) {
    110 		perror(snddevice->string);
    111         Com_Printf("Sound driver too old\n");
    112 		close(audio_fd);
    113 		return 0;
    114 	}
    115 
    116 	if (!(caps & DSP_CAP_TRIGGER) || !(caps & DSP_CAP_MMAP)) {
    117 		Com_Printf("Sorry but your soundcard can't do this\n");
    118 		close(audio_fd);
    119 		return 0;
    120 	}
    121 
    122 
    123 	/* SNDCTL_DSP_GETOSPACE moved to be called later */
    124     
    125 	// set sample bits & speed
    126   dma.samplebits = (int)sndbits->value;
    127 	if (dma.samplebits != 16 && dma.samplebits != 8) {
    128         ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
    129         if (fmt & AFMT_S16_LE) 
    130 			dma.samplebits = 16;
    131         else if (fmt & AFMT_U8) 
    132 			dma.samplebits = 8;
    133     }
    134 
    135 	dma.speed = (int)sndspeed->value;
    136 	if (!dma.speed) {
    137         for (i=0 ; i<sizeof(tryrates)/4 ; i++)
    138             if (!ioctl(audio_fd, SNDCTL_DSP_SPEED, &tryrates[i])) 
    139 				break;
    140         dma.speed = tryrates[i];
    141     }
    142 
    143 	dma.channels = (int)sndchannels->value;
    144 	if (dma.channels < 1 || dma.channels > 2)
    145 		dma.channels = 2;
    146         
    147 /*  mmap() call moved forward */
    148 
    149 	tmp = 0;
    150 	if (dma.channels == 2)
    151 		tmp = 1;
    152     rc = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp);
    153     if (rc < 0) {
    154 		perror(snddevice->string);
    155         Com_Printf("Could not set %s to stereo=%d", snddevice->string, dma.channels);
    156 		close(audio_fd);
    157         return 0;
    158     }
    159 
    160 	if (tmp)
    161 		dma.channels = 2;
    162 	else
    163 		dma.channels = 1;
    164 
    165     rc = ioctl(audio_fd, SNDCTL_DSP_SPEED, &dma.speed);
    166     if (rc < 0) {
    167 		perror(snddevice->string);
    168         Com_Printf("Could not set %s speed to %d", snddevice->string, dma.speed);
    169 		close(audio_fd);
    170         return 0;
    171     }
    172 
    173     if (dma.samplebits == 16) {
    174         rc = AFMT_S16_LE;
    175         rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
    176         if (rc < 0) {
    177 			perror(snddevice->string);
    178 			Com_Printf("Could not support 16-bit data.  Try 8-bit.\n");
    179 			close(audio_fd);
    180 			return 0;
    181 		}
    182     } else if (dma.samplebits == 8) {
    183         rc = AFMT_U8;
    184         rc = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &rc);
    185         if (rc < 0) {
    186 			perror(snddevice->string);
    187 			Com_Printf("Could not support 8-bit data.\n");
    188 			close(audio_fd);
    189 			return 0;
    190 		}
    191     } else {
    192 		perror(snddevice->string);
    193 		Com_Printf("%d-bit sound not supported.", dma.samplebits);
    194 		close(audio_fd);
    195 		return 0;
    196 	}
    197 
    198     if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info)==-1) {   
    199         perror("GETOSPACE");
    200 		Com_Printf("Um, can't do GETOSPACE?\n");
    201 		close(audio_fd);
    202 		return 0;
    203     }
    204 
    205 	dma.samples = info.fragstotal * info.fragsize / (dma.samplebits/8);
    206 	dma.submission_chunk = 1;
    207 
    208 	// memory map the dma buffer
    209 
    210   // TTimo 2001/10/08 added PROT_READ to the mmap
    211   // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=371
    212   // checking Alsa bug, doesn't allow dma alloc with PROT_READ?
    213 
    214 	if (!dma.buffer)
    215 		dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
    216 			* info.fragsize, PROT_WRITE|PROT_READ, MAP_FILE|MAP_SHARED, audio_fd, 0);
    217 
    218   if (dma.buffer == MAP_FAILED)
    219   {
    220     Com_Printf("Could not mmap dma buffer PROT_WRITE|PROT_READ\n");
    221     Com_Printf("trying mmap PROT_WRITE (with associated better compatibility / less performance code)\n");
    222 		dma.buffer = (unsigned char *) mmap(NULL, info.fragstotal
    223 			* info.fragsize, PROT_WRITE, MAP_FILE|MAP_SHARED, audio_fd, 0);
    224     // NOTE TTimo could add a variable to force using regular memset on systems that are known to be safe
    225     use_custom_memset = qtrue;
    226   }
    227 
    228 	if (dma.buffer == MAP_FAILED) {
    229 		perror(snddevice->string);
    230 		Com_Printf("Could not mmap %s\n", snddevice->string);
    231 		close(audio_fd);
    232 		return 0;
    233 	}
    234 
    235 	// toggle the trigger & start her up
    236 
    237   tmp = 0;
    238   rc  = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
    239 	if (rc < 0) {
    240 		perror(snddevice->string);
    241 		Com_Printf("Could not toggle.\n");
    242 		close(audio_fd);
    243 		return 0;
    244 	}
    245 
    246   tmp = PCM_ENABLE_OUTPUT;
    247   rc = ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &tmp);
    248 	if (rc < 0) {
    249 		perror(snddevice->string);
    250 		Com_Printf("Could not toggle.\n");
    251 		close(audio_fd);
    252 
    253 		return 0;
    254 	}
    255 
    256 	snd_inited = 1;
    257 	return 1;
    258 }
    259 
    260 int SNDDMA_GetDMAPos(void)
    261 {
    262 	struct count_info count;
    263 
    264 	if (!snd_inited) return 0;
    265 
    266 	if (ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &count) == -1) {
    267 		perror(snddevice->string);
    268 		Com_Printf("Uh, sound dead.\n");
    269 		close(audio_fd);
    270 		snd_inited = 0;
    271 		return 0;
    272 	}
    273 	return count.ptr / (dma.samplebits / 8);
    274 }
    275 
    276 void SNDDMA_Shutdown(void)
    277 {
    278 }
    279 
    280 /*
    281 ==============
    282 SNDDMA_Submit
    283 
    284 Send sound to device if buffer isn't really the dma buffer
    285 ===============
    286 */
    287 void SNDDMA_Submit(void)
    288 {
    289 }
    290 
    291 void SNDDMA_BeginPainting (void)
    292 {
    293 }