1/* 2Copyright (C) 1996-1997 Id Software, Inc. 3 4This program is free software; you can redistribute it and/or 5modify it under the terms of the GNU General Public License 6as published by the Free Software Foundation; either version 2 7of the License, or (at your option) any later version. 8 9This program is distributed in the hope that it will be useful, 10but WITHOUT ANY WARRANTY; without even the implied warranty of 11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 13See the GNU General Public License for more details. 14 15You should have received a copy of the GNU General Public License 16along with this program; if not, write to the Free Software 17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 19*/ 20// snd_mix.c -- portable code to mix sounds for snd_dma.c 21 22#include "quakedef.h" 23 24#ifdef _WIN32 25#include "winquake.h" 26#else 27#define DWORD unsigned long 28#endif 29 30#define PAINTBUFFER_SIZE 512 31 32typedef union { 33 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; 34 int intbuf[PAINTBUFFER_SIZE * sizeof(portable_samplepair_t) / sizeof(int)]; 35} portableOrInt_t; 36 37portableOrInt_t paintbuffer; 38int snd_scaletable[32][256]; 39int *snd_p, snd_linear_count, snd_vol; 40short *snd_out; 41 42void Snd_WriteLinearBlastStereo16 (void); 43 44extern void SNDDMA_ReportWrite(size_t lengthBytes); 45#if !id386 46void Snd_WriteLinearBlastStereo16 (void) 47{ 48 int i; 49 int val; 50 51 for (i=0 ; i<snd_linear_count ; i+=2) 52 { 53 val = (snd_p[i]*snd_vol)>>8; 54 if (val > 0x7fff) 55 snd_out[i] = 0x7fff; 56 else if (val < (short)0x8000) 57 snd_out[i] = (short)0x8000; 58 else 59 snd_out[i] = val; 60 61 val = (snd_p[i+1]*snd_vol)>>8; 62 if (val > 0x7fff) 63 snd_out[i+1] = 0x7fff; 64 else if (val < (short)0x8000) 65 snd_out[i+1] = (short)0x8000; 66 else 67 snd_out[i+1] = val; 68 } 69 SNDDMA_ReportWrite(snd_linear_count << 1); 70} 71#endif 72 73void S_TransferStereo16 (int endtime) 74{ 75 int lpos; 76 int lpaintedtime; 77 DWORD *pbuf; 78#ifdef _WIN32 79 int reps; 80 DWORD dwSize,dwSize2; 81 DWORD *pbuf2; 82 HRESULT hresult; 83#endif 84 85 snd_vol = (int)(volume.value*256); 86 87 snd_p = paintbuffer.intbuf; 88 lpaintedtime = paintedtime; 89 90#ifdef _WIN32 91 if (pDSBuf) 92 { 93 reps = 0; 94 95 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, 96 &pbuf2, &dwSize2, 0)) != DS_OK) 97 { 98 if (hresult != DSERR_BUFFERLOST) 99 { 100 Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); 101 S_Shutdown (); 102 S_Startup (); 103 return; 104 } 105 106 if (++reps > 10000) 107 { 108 Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); 109 S_Shutdown (); 110 S_Startup (); 111 return; 112 } 113 } 114 } 115 else 116#endif 117 { 118 pbuf = (DWORD *)shm->buffer; 119 } 120 121 while (lpaintedtime < endtime) 122 { 123 // handle recirculating buffer issues 124 lpos = lpaintedtime & ((shm->samples>>1)-1); 125 126 snd_out = (short *) pbuf + (lpos<<1); 127 128 snd_linear_count = (shm->samples>>1) - lpos; 129 if (lpaintedtime + snd_linear_count > endtime) 130 snd_linear_count = endtime - lpaintedtime; 131 132 snd_linear_count <<= 1; 133 134 // write a linear blast of samples 135 Snd_WriteLinearBlastStereo16 (); 136 137 snd_p += snd_linear_count; 138 lpaintedtime += (snd_linear_count>>1); 139 } 140 141#ifdef _WIN32 142 if (pDSBuf) 143 pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); 144#endif 145} 146 147void S_TransferPaintBuffer(int endtime) 148{ 149 int out_idx; 150 int count; 151 int out_mask; 152 int *p; 153 int step; 154 int val; 155 int snd_vol; 156 DWORD *pbuf; 157#ifdef _WIN32 158 int reps; 159 DWORD dwSize,dwSize2; 160 DWORD *pbuf2; 161 HRESULT hresult; 162#endif 163 164 if (shm->samplebits == 16 && shm->channels == 2) 165 { 166 S_TransferStereo16 (endtime); 167 return; 168 } 169 170 p = paintbuffer.intbuf; 171 count = (endtime - paintedtime) * shm->channels; 172 out_mask = shm->samples - 1; 173 out_idx = paintedtime * shm->channels & out_mask; 174 step = 3 - shm->channels; 175 snd_vol = (int)(volume.value*256); 176 177#ifdef _WIN32 178 if (pDSBuf) 179 { 180 reps = 0; 181 182 while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, 183 &pbuf2,&dwSize2, 0)) != DS_OK) 184 { 185 if (hresult != DSERR_BUFFERLOST) 186 { 187 Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n"); 188 S_Shutdown (); 189 S_Startup (); 190 return; 191 } 192 193 if (++reps > 10000) 194 { 195 Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n"); 196 S_Shutdown (); 197 S_Startup (); 198 return; 199 } 200 } 201 } 202 else 203#endif 204 { 205 pbuf = (DWORD *)shm->buffer; 206 } 207 208 if (shm->samplebits == 16) 209 { 210 short *out = (short *) pbuf; 211 while (count--) 212 { 213 val = (*p * snd_vol) >> 8; 214 p+= step; 215 if (val > 0x7fff) 216 val = 0x7fff; 217 else if (val < (short)0x8000) 218 val = (short)0x8000; 219 out[out_idx] = val; 220 out_idx = (out_idx + 1) & out_mask; 221 } 222 } 223 else if (shm->samplebits == 8) 224 { 225 unsigned char *out = (unsigned char *) pbuf; 226 while (count--) 227 { 228 val = (*p * snd_vol) >> 8; 229 p+= step; 230 if (val > 0x7fff) 231 val = 0x7fff; 232 else if (val < (short)0x8000) 233 val = (short)0x8000; 234 out[out_idx] = (val>>8) + 128; 235 out_idx = (out_idx + 1) & out_mask; 236 } 237 } 238 239#ifdef _WIN32 240 if (pDSBuf) { 241 DWORD dwNewpos, dwWrite; 242 int il = paintedtime; 243 int ir = endtime - paintedtime; 244 245 ir += il; 246 247 pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); 248 249 pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite); 250 251// if ((dwNewpos >= il) && (dwNewpos <= ir)) 252// Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos); 253 } 254#endif 255} 256 257 258/* 259=============================================================================== 260 261CHANNEL MIXING 262 263=============================================================================== 264*/ 265 266void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); 267void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); 268 269void S_PaintChannels(int endtime) 270{ 271 int i; 272 int end; 273 channel_t *ch; 274 sfxcache_t *sc; 275 int ltime, count; 276 277 while (paintedtime < endtime) 278 { 279 // if paintbuffer is smaller than DMA buffer 280 end = endtime; 281 if (endtime - paintedtime > PAINTBUFFER_SIZE) 282 end = paintedtime + PAINTBUFFER_SIZE; 283 284 // clear the paint buffer 285 Q_memset(paintbuffer.paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); 286 287 // paint in the channels. 288 ch = channels; 289 for (i=0; i<total_channels ; i++, ch++) 290 { 291 if (!ch->sfx) 292 continue; 293 if (!ch->leftvol && !ch->rightvol) 294 continue; 295 sc = S_LoadSound (ch->sfx); 296 if (!sc) 297 continue; 298 299 ltime = paintedtime; 300 301 while (ltime < end) 302 { // paint up to end 303 if (ch->end < end) 304 count = ch->end - ltime; 305 else 306 count = end - ltime; 307 308 if (count > 0) 309 { 310 if (sc->width == 1) 311 SND_PaintChannelFrom8(ch, sc, count); 312 else 313 SND_PaintChannelFrom16(ch, sc, count); 314 315 ltime += count; 316 } 317 318 // if at end of loop, restart 319 if (ltime >= ch->end) 320 { 321 if (sc->loopstart >= 0) 322 { 323 ch->pos = sc->loopstart; 324 ch->end = ltime + sc->length - ch->pos; 325 } 326 else 327 { // channel just stopped 328 ch->sfx = NULL; 329 break; 330 } 331 } 332 } 333 334 } 335 336 // transfer out according to DMA format 337 S_TransferPaintBuffer(end); 338 paintedtime = end; 339 } 340} 341 342void SND_InitScaletable (void) 343{ 344 int i, j; 345 346 for (i=0 ; i<32 ; i++) 347 for (j=0 ; j<256 ; j++) 348 snd_scaletable[i][j] = ((signed char)j) * i * 8; 349} 350 351 352#if !id386 353 354void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) 355{ 356 int data; 357 int *lscale, *rscale; 358 unsigned char *sfx; 359 int i; 360 361 if (ch->leftvol > 255) 362 ch->leftvol = 255; 363 if (ch->rightvol > 255) 364 ch->rightvol = 255; 365 366 lscale = snd_scaletable[ch->leftvol >> 3]; 367 rscale = snd_scaletable[ch->rightvol >> 3]; 368 sfx = sc->data.uc + ch->pos; 369 370 for (i=0 ; i<count ; i++) 371 { 372 data = sfx[i]; 373 paintbuffer.paintbuffer[i].left += lscale[data]; 374 paintbuffer.paintbuffer[i].right += rscale[data]; 375 } 376 377 ch->pos += count; 378} 379 380#endif // !id386 381 382 383void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) 384{ 385 int data; 386 int left, right; 387 int leftvol, rightvol; 388 signed short *sfx; 389 int i; 390 391 leftvol = ch->leftvol; 392 rightvol = ch->rightvol; 393 sfx = sc->data.ss + ch->pos; 394 395 for (i=0 ; i<count ; i++) 396 { 397 data = sfx[i]; 398 left = (data * leftvol) >> 8; 399 right = (data * rightvol) >> 8; 400 paintbuffer.paintbuffer[i].left += left; 401 paintbuffer.paintbuffer[i].right += right; 402 } 403 404 ch->pos += count; 405} 406 407