/* | |
Copyright (C) 1996-1997 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 "quakedef.h" | |
#ifdef _WIN32 | |
#include "winquake.h" | |
#else | |
#define DWORD unsigned long | |
#endif | |
#define PAINTBUFFER_SIZE 512 | |
portable_samplepair_t paintbuffer[PAINTBUFFER_SIZE]; | |
int snd_scaletable[32][256]; | |
int *snd_p, snd_linear_count, snd_vol; | |
short *snd_out; | |
void Snd_WriteLinearBlastStereo16 (void); | |
#if !id386 | |
void Snd_WriteLinearBlastStereo16 (void) | |
{ | |
int i; | |
int val; | |
for (i=0 ; i<snd_linear_count ; i+=2) | |
{ | |
val = (snd_p[i]*snd_vol)>>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]*snd_vol)>>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 | |
void S_TransferStereo16 (int endtime) | |
{ | |
int lpos; | |
int lpaintedtime; | |
DWORD *pbuf; | |
#ifdef _WIN32 | |
int reps; | |
DWORD dwSize,dwSize2; | |
DWORD *pbuf2; | |
HRESULT hresult; | |
#endif | |
snd_vol = volume.value*256; | |
snd_p = (int *) paintbuffer; | |
lpaintedtime = paintedtime; | |
#ifdef _WIN32 | |
if (pDSBuf) | |
{ | |
reps = 0; | |
while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, | |
&pbuf2, &dwSize2, 0)) != DS_OK) | |
{ | |
if (hresult != DSERR_BUFFERLOST) | |
{ | |
Con_Printf ("S_TransferStereo16: DS::Lock Sound Buffer Failed\n"); | |
S_Shutdown (); | |
S_Startup (); | |
return; | |
} | |
if (++reps > 10000) | |
{ | |
Con_Printf ("S_TransferStereo16: DS: couldn't restore buffer\n"); | |
S_Shutdown (); | |
S_Startup (); | |
return; | |
} | |
} | |
} | |
else | |
#endif | |
{ | |
pbuf = (DWORD *)shm->buffer; | |
} | |
while (lpaintedtime < endtime) | |
{ | |
// handle recirculating buffer issues | |
lpos = lpaintedtime & ((shm->samples>>1)-1); | |
snd_out = (short *) pbuf + (lpos<<1); | |
snd_linear_count = (shm->samples>>1) - lpos; | |
if (lpaintedtime + snd_linear_count > endtime) | |
snd_linear_count = endtime - lpaintedtime; | |
snd_linear_count <<= 1; | |
// write a linear blast of samples | |
Snd_WriteLinearBlastStereo16 (); | |
snd_p += snd_linear_count; | |
lpaintedtime += (snd_linear_count>>1); | |
} | |
#ifdef _WIN32 | |
if (pDSBuf) | |
pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); | |
#endif | |
} | |
void S_TransferPaintBuffer(int endtime) | |
{ | |
int out_idx; | |
int count; | |
int out_mask; | |
int *p; | |
int step; | |
int val; | |
int snd_vol; | |
DWORD *pbuf; | |
#ifdef _WIN32 | |
int reps; | |
DWORD dwSize,dwSize2; | |
DWORD *pbuf2; | |
HRESULT hresult; | |
#endif | |
if (shm->samplebits == 16 && shm->channels == 2) | |
{ | |
S_TransferStereo16 (endtime); | |
return; | |
} | |
p = (int *) paintbuffer; | |
count = (endtime - paintedtime) * shm->channels; | |
out_mask = shm->samples - 1; | |
out_idx = paintedtime * shm->channels & out_mask; | |
step = 3 - shm->channels; | |
snd_vol = volume.value*256; | |
#ifdef _WIN32 | |
if (pDSBuf) | |
{ | |
reps = 0; | |
while ((hresult = pDSBuf->lpVtbl->Lock(pDSBuf, 0, gSndBufSize, &pbuf, &dwSize, | |
&pbuf2,&dwSize2, 0)) != DS_OK) | |
{ | |
if (hresult != DSERR_BUFFERLOST) | |
{ | |
Con_Printf ("S_TransferPaintBuffer: DS::Lock Sound Buffer Failed\n"); | |
S_Shutdown (); | |
S_Startup (); | |
return; | |
} | |
if (++reps > 10000) | |
{ | |
Con_Printf ("S_TransferPaintBuffer: DS: couldn't restore buffer\n"); | |
S_Shutdown (); | |
S_Startup (); | |
return; | |
} | |
} | |
} | |
else | |
#endif | |
{ | |
pbuf = (DWORD *)shm->buffer; | |
} | |
if (shm->samplebits == 16) | |
{ | |
short *out = (short *) pbuf; | |
while (count--) | |
{ | |
val = (*p * snd_vol) >> 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 (shm->samplebits == 8) | |
{ | |
unsigned char *out = (unsigned char *) pbuf; | |
while (count--) | |
{ | |
val = (*p * snd_vol) >> 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; | |
} | |
} | |
#ifdef _WIN32 | |
if (pDSBuf) { | |
DWORD dwNewpos, dwWrite; | |
int il = paintedtime; | |
int ir = endtime - paintedtime; | |
ir += il; | |
pDSBuf->lpVtbl->Unlock(pDSBuf, pbuf, dwSize, NULL, 0); | |
pDSBuf->lpVtbl->GetCurrentPosition(pDSBuf, &dwNewpos, &dwWrite); | |
// if ((dwNewpos >= il) && (dwNewpos <= ir)) | |
// Con_Printf("%d-%d p %d c\n", il, ir, dwNewpos); | |
} | |
#endif | |
} | |
/* | |
=============================================================================== | |
CHANNEL MIXING | |
=============================================================================== | |
*/ | |
void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int endtime); | |
void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int endtime); | |
void S_PaintChannels(int endtime) | |
{ | |
int i; | |
int end; | |
channel_t *ch; | |
sfxcache_t *sc; | |
int ltime, count; | |
while (paintedtime < endtime) | |
{ | |
// if paintbuffer is smaller than DMA buffer | |
end = endtime; | |
if (endtime - paintedtime > PAINTBUFFER_SIZE) | |
end = paintedtime + PAINTBUFFER_SIZE; | |
// clear the paint buffer | |
Q_memset(paintbuffer, 0, (end - paintedtime) * sizeof(portable_samplepair_t)); | |
// paint in the channels. | |
ch = channels; | |
for (i=0; i<total_channels ; i++, ch++) | |
{ | |
if (!ch->sfx) | |
continue; | |
if (!ch->leftvol && !ch->rightvol) | |
continue; | |
sc = S_LoadSound (ch->sfx); | |
if (!sc) | |
continue; | |
ltime = paintedtime; | |
while (ltime < end) | |
{ // paint up to end | |
if (ch->end < end) | |
count = ch->end - ltime; | |
else | |
count = end - ltime; | |
if (count > 0) | |
{ | |
if (sc->width == 1) | |
SND_PaintChannelFrom8(ch, sc, count); | |
else | |
SND_PaintChannelFrom16(ch, sc, count); | |
ltime += count; | |
} | |
// if at end of loop, restart | |
if (ltime >= ch->end) | |
{ | |
if (sc->loopstart >= 0) | |
{ | |
ch->pos = sc->loopstart; | |
ch->end = ltime + sc->length - ch->pos; | |
} | |
else | |
{ // channel just stopped | |
ch->sfx = NULL; | |
break; | |
} | |
} | |
} | |
} | |
// transfer out according to DMA format | |
S_TransferPaintBuffer(end); | |
paintedtime = end; | |
} | |
} | |
void SND_InitScaletable (void) | |
{ | |
int i, j; | |
for (i=0 ; i<32 ; i++) | |
for (j=0 ; j<256 ; j++) | |
snd_scaletable[i][j] = ((signed char)j) * i * 8; | |
} | |
#if !id386 | |
void SND_PaintChannelFrom8 (channel_t *ch, sfxcache_t *sc, int count) | |
{ | |
int data; | |
int *lscale, *rscale; | |
unsigned char *sfx; | |
int i; | |
if (ch->leftvol > 255) | |
ch->leftvol = 255; | |
if (ch->rightvol > 255) | |
ch->rightvol = 255; | |
lscale = snd_scaletable[ch->leftvol >> 3]; | |
rscale = snd_scaletable[ch->rightvol >> 3]; | |
sfx = (signed char *)sc->data + ch->pos; | |
for (i=0 ; i<count ; i++) | |
{ | |
data = sfx[i]; | |
paintbuffer[i].left += lscale[data]; | |
paintbuffer[i].right += rscale[data]; | |
} | |
ch->pos += count; | |
} | |
#endif // !id386 | |
void SND_PaintChannelFrom16 (channel_t *ch, sfxcache_t *sc, int count) | |
{ | |
int data; | |
int left, right; | |
int leftvol, rightvol; | |
signed short *sfx; | |
int i; | |
leftvol = ch->leftvol; | |
rightvol = ch->rightvol; | |
sfx = (signed short *)sc->data + ch->pos; | |
for (i=0 ; i<count ; i++) | |
{ | |
data = sfx[i]; | |
left = (data * leftvol) >> 8; | |
right = (data * rightvol) >> 8; | |
paintbuffer[i].left += left; | |
paintbuffer[i].right += right; | |
} | |
ch->pos += count; | |
} | |