0,0 → 1,398 |
/* |
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; |
} |
|