/* * Copyright (C) 1997-2001 Id Software, Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ /* snd_mix.c -- portable code to mix sounds for snd_dma.c */ #include "client.h" #include "snd_loc.h" #define PAINTBUFFER_SIZE 2048 portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; int snd_scaletable[32][256]; int *snd_p, snd_linear_count, snd_vol; short *snd_out; void S_WriteLinearBlastStereo16(void); #if !((defined __unix__) && defined __i386__) || defined C_ONLY #if !id386 void S_WriteLinearBlastStereo16(void) { int i; int val; for (i = 0; i < snd_linear_count; i += 2) { val = snd_p[i] >> 8; if (val > 0x7fff) snd_out[i] = 0x7fff; else if (val < (short)0x8000) snd_out[i] = (short)0x8000; else snd_out[i] = val; val = snd_p[i + 1] >> 8; if (val > 0x7fff) snd_out[i + 1] = 0x7fff; else if (val < (short)0x8000) snd_out[i + 1] = (short)0x8000; else snd_out[i + 1] = val; } } #endif #endif void S_TransferStereo16(unsigned long *pbuf, int endtime) { int lpos; int lpaintedtime; snd_p = (int *)paintbuffer; lpaintedtime = paintedtime; while (lpaintedtime < endtime) { /* handle recirculating buffer issues */ lpos = lpaintedtime & ((dma.samples >> 1) - 1); snd_out = (short *)pbuf + (lpos << 1); snd_linear_count = (dma.samples >> 1) - lpos; if (lpaintedtime + snd_linear_count > endtime) snd_linear_count = endtime - lpaintedtime; snd_linear_count <<= 1; /* write a linear blast of samples */ S_WriteLinearBlastStereo16(); snd_p += snd_linear_count; lpaintedtime += (snd_linear_count >> 1); } } /* * =================== S_TransferPaintBuffer * * =================== */ void S_TransferPaintBuffer(int endtime) { int out_idx; int count; int out_mask; int *p; int step; int val; unsigned long *pbuf; pbuf = (unsigned long *)dma.buffer; if (s_testsound->value) { int i; int count; /* write a fixed sine wave */ count = (endtime - paintedtime); for (i = 0; i < count; i++) paintbuffer[i].left = paintbuffer[i].right = sin((paintedtime + i) * 0.1) * 20000 * 256; } if (dma.samplebits == 16 && dma.channels == 2) { /* optimized case */ S_TransferStereo16(pbuf, endtime); } else { /* general case */ p = (int *)paintbuffer; count = (endtime - paintedtime) * dma.channels; out_mask = dma.samples - 1; out_idx = paintedtime * dma.channels & out_mask; step = 3 - dma.channels; if (dma.samplebits == 16) { short *out = (short *)pbuf; while (count--) { val = *p >> 8; p += step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = val; out_idx = (out_idx + 1) & out_mask; } } else if (dma.samplebits == 8) { unsigned char *out = (unsigned char *)pbuf; while (count--) { val = *p >> 8; p += step; if (val > 0x7fff) val = 0x7fff; else if (val < (short)0x8000) val = (short)0x8000; out[out_idx] = (val >> 8) + 128; out_idx = (out_idx + 1) & out_mask; } } } } /* * * ============================================================================ * === * * CHANNEL MIXING * * ============== * ================================================================ */ void S_PaintChannelFrom8(channel_t * ch, sfxcache_t * sc, int endtime, int offset); void S_PaintChannelFrom16(channel_t * ch, sfxcache_t * sc, int endtime, int offset); void S_PaintChannels(int endtime) { int i; int end; channel_t *ch; sfxcache_t *sc; int ltime , count; playsound_t *ps; snd_vol = s_volume->value * 256; /* Com_Printf ("%i to %i\n", paintedtime, endtime); */ while (paintedtime < endtime) { /* if paintbuffer is smaller than DMA buffer */ end = endtime; if (endtime - paintedtime > PAINTBUFFER_SIZE) end = paintedtime + PAINTBUFFER_SIZE; /* start any playsounds */ while (1) { ps = s_pendingplays.next; if (ps == &s_pendingplays) break; /* no more pending sounds */ if (ps->begin <= paintedtime) { S_IssuePlaysound(ps); continue; } if (ps->begin < end) end = ps->begin; /* stop here */ break; } /* clear the paint buffer */ if (s_rawend < paintedtime) { /* Com_Printf ("clear\n"); */ memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); } else { /* copy from the streaming sound source */ int s; int stop; stop = (end < s_rawend) ? end : s_rawend; for (i = paintedtime; i < stop; i++) { s = i & (MAX_RAW_SAMPLES - 1); paintbuffer[i - paintedtime] = s_rawsamples[s]; } /* if (i != end) */ /* Com_Printf ("partial stream\n"); */ /* else */ /* Com_Printf ("full stream\n"); */ for (; i < end; i++) { paintbuffer[i - paintedtime].left = paintbuffer[i - paintedtime].right = 0; } } /* paint in the channels. */ ch = channels; for (i = 0; i < MAX_CHANNELS; i++, ch++) { ltime = paintedtime; while (ltime < end) { if (!ch->sfx || (!ch->leftvol && !ch->rightvol)) break; /* max painting is to the end of the buffer */ count = end - ltime; /* might be stopped by running out of data */ if (ch->end - ltime < count) count = ch->end - ltime; sc = S_LoadSound(ch->sfx); if (!sc) break; if (count > 0 && ch->sfx) { if (sc->width == 1) /* FIXME; 8 bit asm is * wrong now */ S_PaintChannelFrom8(ch, sc, count, ltime - paintedtime); else S_PaintChannelFrom16(ch, sc, count, ltime - paintedtime); ltime += count; } /* if at end of loop, restart */ if (ltime >= ch->end) { if (ch->autosound) { /* autolooping sounds * always go back to * start */ ch->pos = 0; ch->end = ltime + sc->length; } else if (sc->loopstart >= 0) { ch->pos = sc->loopstart; ch->end = ltime + sc->length - ch->pos; } else { /* channel just stopped */ ch->sfx = NULL; } } } } /* transfer out according to DMA format */ S_TransferPaintBuffer(end); paintedtime = end; } } void S_InitScaletable(void) { int i , j; int scale; s_volume->modified = false; for (i = 0; i < 32; i++) { scale = i * 8 * 256 * s_volume->value; for (j = 0; j < 256; j++) snd_scaletable[i][j] = ((signed char)j) * scale; } } #if !((defined (__unix__)) && defined __i386__) || defined C_ONLY #if !id386 void S_PaintChannelFrom8(channel_t * ch, sfxcache_t * sc, int count, int offset) { int data; int *lscale, *rscale; byte *sfx; int i; portable_samplepair_t *samp; if (ch->leftvol > 255) ch->leftvol = 255; if (ch->rightvol > 255) ch->rightvol = 255; /* ZOID-- >>11 has been changed to >>3, >>11 didn't make much sense */ /* as it would always be zero. */ lscale = snd_scaletable[ch->leftvol >> 3]; rscale = snd_scaletable[ch->rightvol >> 3]; sfx = (byte *)sc->data + ch->pos; samp = &paintbuffer[offset]; for (i = 0; i < count; i++, samp++) { data = sfx[i]; samp->left += lscale[data]; samp->right += rscale[data]; } ch->pos += count; } #endif #endif void S_PaintChannelFrom16(channel_t * ch, sfxcache_t * sc, int count, int offset) { int data; int left , right; int leftvol , rightvol; signed short *sfx; int i; portable_samplepair_t *samp; leftvol = ch->leftvol * snd_vol; rightvol = ch->rightvol * snd_vol; sfx = (signed short *)sc->data + ch->pos; samp = &paintbuffer[offset]; for (i = 0; i < count; i++, samp++) { data = sfx[i]; left = (data * leftvol) >> 8; right = (data * rightvol) >> 8; samp->left += left; samp->right += right; } ch->pos += count; }