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 }