openmohaa/code/client/snd_mix.c

800 lines
20 KiB
C
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// snd_mix.c -- portable code to mix sounds for snd_dma.c
#include "client.h"
#include "snd_local.h"
#if idppc_altivec && !defined(MACOS_X)
#include <altivec.h>
#endif
static portable_samplepair_t paintbuffer[ PAINTBUFFER_SIZE ];
static int snd_vol;
int* snd_p;
int snd_linear_count;
short* snd_out;
#if !id386 // if configured not to use asm
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 < -32768 )
snd_out[ i ] = -32768;
else
snd_out[ i ] = val;
val = snd_p[ i + 1 ] >> 8;
if( val > 0x7fff )
snd_out[ i + 1 ] = 0x7fff;
else if( val < -32768 )
snd_out[ i + 1 ] = -32768;
else
snd_out[ i + 1 ] = val;
}
}
#elif defined(__GNUC__)
// uses snd_mixa.s
void S_WriteLinearBlastStereo16( void );
#else
__declspec( naked ) void S_WriteLinearBlastStereo16( void )
{
__asm {
push edi
push ebx
mov ecx, ds:dword ptr[ snd_linear_count ]
mov ebx, ds : dword ptr[ snd_p ]
mov edi, ds : dword ptr[ snd_out ]
LWLBLoopTop :
mov eax, ds : dword ptr[ -8 + ebx + ecx * 4 ]
sar eax, 8
cmp eax, 07FFFh
jg LClampHigh
cmp eax, 0FFFF8000h
jnl LClampDone
mov eax, 0FFFF8000h
jmp LClampDone
LClampHigh :
mov eax, 07FFFh
LClampDone :
mov edx, ds : dword ptr[ -4 + ebx + ecx * 4 ]
sar edx, 8
cmp edx, 07FFFh
jg LClampHigh2
cmp edx, 0FFFF8000h
jnl LClampDone2
mov edx, 0FFFF8000h
jmp LClampDone2
LClampHigh2 :
mov edx, 07FFFh
LClampDone2 :
shl edx, 16
and eax, 0FFFFh
or edx, eax
mov ds : dword ptr[ -4 + edi + ecx * 2 ], edx
sub ecx, 2
jnz LWLBLoopTop
pop ebx
pop edi
ret
}
}
#endif
void S_TransferStereo16( unsigned long *pbuf, int endtime )
{
int lpos;
int ls_paintedtime;
snd_p = ( int * )paintbuffer;
ls_paintedtime = s_paintedtime;
while( ls_paintedtime < endtime )
{
// handle recirculating buffer issues
lpos = ls_paintedtime & ( ( dma.samples >> 1 ) - 1 );
snd_out = ( short * )pbuf + ( lpos << 1 );
snd_linear_count = ( dma.samples >> 1 ) - lpos;
if( ls_paintedtime + snd_linear_count > endtime )
snd_linear_count = endtime - ls_paintedtime;
snd_linear_count <<= 1;
// write a linear blast of samples
S_WriteLinearBlastStereo16();
snd_p += snd_linear_count;
ls_paintedtime += ( snd_linear_count >> 1 );
if( CL_VideoRecording() )
CL_WriteAVIAudioFrame( ( byte * )snd_out, 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->integer ) {
int i;
// write a fixed sine wave
count = ( endtime - s_paintedtime );
for( i = 0; i<count; i++ )
paintbuffer[ i ].left = paintbuffer[ i ].right = sin( ( s_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 - s_paintedtime ) * dma.channels;
out_mask = dma.samples - 1;
out_idx = s_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 < -32768 )
val = -32768;
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 < -32768 )
val = -32768;
out[ out_idx ] = ( val >> 8 ) + 128;
out_idx = ( out_idx + 1 ) & out_mask;
}
}
}
}
/*
===============================================================================
CHANNEL MIXING
===============================================================================
*/
#if idppc_altivec
static void S_PaintChannelFrom16_altivec( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data, aoff, boff;
int leftvol, rightvol;
int i, j;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
float ooff, fdata[ 2 ], fdiv, fleftvol, frightvol;
if( sc->soundChannels <= 0 ) {
return;
}
samp = &paintbuffer[ bufferOffset ];
if( ch->doppler ) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
if( sc->soundChannels == 2 ) {
sampleOffset *= sc->soundChannels;
if( sampleOffset & 1 ) {
sampleOffset &= ~1;
}
}
chunk = sc->soundData;
while( sampleOffset >= SND_CHUNK_SIZE ) {
chunk = chunk->next;
sampleOffset -= SND_CHUNK_SIZE;
if( !chunk ) {
chunk = sc->soundData;
}
}
if( !ch->doppler || ch->dopplerScale == 1.0f ) {
vector signed short volume_vec;
vector unsigned int volume_shift;
int vectorCount, samplesLeft, chunkSamplesLeft;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samples = chunk->sndChunk;
( ( short * )&volume_vec )[ 0 ] = leftvol;
( ( short * )&volume_vec )[ 1 ] = leftvol;
( ( short * )&volume_vec )[ 4 ] = leftvol;
( ( short * )&volume_vec )[ 5 ] = leftvol;
( ( short * )&volume_vec )[ 2 ] = rightvol;
( ( short * )&volume_vec )[ 3 ] = rightvol;
( ( short * )&volume_vec )[ 6 ] = rightvol;
( ( short * )&volume_vec )[ 7 ] = rightvol;
volume_shift = vec_splat_u32( 8 );
i = 0;
while( i < count ) {
/* Try to align destination to 16-byte boundary */
while( i < count && ( ( ( unsigned long )&samp[ i ] & 0x1f ) || ( ( count - i ) < 8 ) || ( ( SND_CHUNK_SIZE - sampleOffset ) < 8 ) ) ) {
data = samples[ sampleOffset++ ];
samp[ i ].left += ( data * leftvol ) >> 8;
if( sc->soundChannels == 2 ) {
data = samples[ sampleOffset++ ];
}
samp[ i ].right += ( data * rightvol ) >> 8;
if( sampleOffset == SND_CHUNK_SIZE ) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
i++;
}
/* Destination is now aligned. Process as many 8-sample
chunks as we can before we run out of room from the current
sound chunk. We do 8 per loop to avoid extra source data reads. */
samplesLeft = count - i;
chunkSamplesLeft = SND_CHUNK_SIZE - sampleOffset;
if( samplesLeft > chunkSamplesLeft )
samplesLeft = chunkSamplesLeft;
vectorCount = samplesLeft / 8;
if( vectorCount )
{
vector unsigned char tmp;
vector short s0, s1, sampleData0, sampleData1;
vector signed int merge0, merge1;
vector signed int d0, d1, d2, d3;
vector unsigned char samplePermute0 =
VECCONST_UINT8( 0, 1, 4, 5, 0, 1, 4, 5, 2, 3, 6, 7, 2, 3, 6, 7 );
vector unsigned char samplePermute1 =
VECCONST_UINT8( 8, 9, 12, 13, 8, 9, 12, 13, 10, 11, 14, 15, 10, 11, 14, 15 );
vector unsigned char loadPermute0, loadPermute1;
// Rather than permute the vectors after we load them to do the sample
// replication and rearrangement, we permute the alignment vector so
// we do everything in one step below and avoid data shuffling.
tmp = vec_lvsl( 0, &samples[ sampleOffset ] );
loadPermute0 = vec_perm( tmp, tmp, samplePermute0 );
loadPermute1 = vec_perm( tmp, tmp, samplePermute1 );
s0 = *( vector short * )&samples[ sampleOffset ];
while( vectorCount )
{
/* Load up source (16-bit) sample data */
s1 = *( vector short * )&samples[ sampleOffset + 7 ];
/* Load up destination sample data */
d0 = *( vector signed int * )&samp[ i ];
d1 = *( vector signed int * )&samp[ i + 2 ];
d2 = *( vector signed int * )&samp[ i + 4 ];
d3 = *( vector signed int * )&samp[ i + 6 ];
sampleData0 = vec_perm( s0, s1, loadPermute0 );
sampleData1 = vec_perm( s0, s1, loadPermute1 );
merge0 = vec_mule( sampleData0, volume_vec );
merge0 = vec_sra( merge0, volume_shift ); /* Shift down to proper range */
merge1 = vec_mulo( sampleData0, volume_vec );
merge1 = vec_sra( merge1, volume_shift );
d0 = vec_add( merge0, d0 );
d1 = vec_add( merge1, d1 );
merge0 = vec_mule( sampleData1, volume_vec );
merge0 = vec_sra( merge0, volume_shift ); /* Shift down to proper range */
merge1 = vec_mulo( sampleData1, volume_vec );
merge1 = vec_sra( merge1, volume_shift );
d2 = vec_add( merge0, d2 );
d3 = vec_add( merge1, d3 );
/* Store destination sample data */
*( vector signed int * )&samp[ i ] = d0;
*( vector signed int * )&samp[ i + 2 ] = d1;
*( vector signed int * )&samp[ i + 4 ] = d2;
*( vector signed int * )&samp[ i + 6 ] = d3;
i += 8;
vectorCount--;
s0 = s1;
sampleOffset += 8;
}
if( sampleOffset == SND_CHUNK_SIZE ) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
}
}
}
else {
fleftvol = ch->leftvol*snd_vol;
frightvol = ch->rightvol*snd_vol;
ooff = sampleOffset;
samples = chunk->sndChunk;
for( i = 0; i<count; i++ ) {
aoff = ooff;
ooff = ooff + ch->dopplerScale * sc->soundChannels;
boff = ooff;
fdata[ 0 ] = fdata[ 1 ] = 0;
for( j = aoff; j<boff; j += sc->soundChannels ) {
if( j == SND_CHUNK_SIZE ) {
chunk = chunk->next;
if( !chunk ) {
chunk = sc->soundData;
}
samples = chunk->sndChunk;
ooff -= SND_CHUNK_SIZE;
}
if( sc->soundChannels == 2 ) {
fdata[ 0 ] += samples[ j&( SND_CHUNK_SIZE - 1 ) ];
fdata[ 1 ] += samples[ ( j + 1 )&( SND_CHUNK_SIZE - 1 ) ];
}
else {
fdata[ 0 ] += samples[ j&( SND_CHUNK_SIZE - 1 ) ];
fdata[ 1 ] += samples[ j&( SND_CHUNK_SIZE - 1 ) ];
}
}
fdiv = 256 * ( boff - aoff ) / sc->soundChannels;
samp[ i ].left += ( fdata[ 0 ] * fleftvol ) / fdiv;
samp[ i ].right += ( fdata[ 1 ] * frightvol ) / fdiv;
}
}
}
#endif
static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data, aoff, boff;
int leftvol, rightvol;
int i, j;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
float ooff, fdata[ 2 ], fdiv, fleftvol, frightvol;
if( sc->soundChannels <= 0 ) {
return;
}
samp = &paintbuffer[ bufferOffset ];
if( ch->doppler ) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
if( sc->soundChannels == 2 ) {
sampleOffset *= sc->soundChannels;
if( sampleOffset & 1 ) {
sampleOffset &= ~1;
}
}
chunk = sc->soundData;
while( sampleOffset >= SND_CHUNK_SIZE ) {
chunk = chunk->next;
sampleOffset -= SND_CHUNK_SIZE;
if( !chunk ) {
chunk = sc->soundData;
}
}
if( !ch->doppler || ch->dopplerScale == 1.0f ) {
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samples = chunk->sndChunk;
for( i = 0; i<count; i++ ) {
data = samples[ sampleOffset++ ];
samp[ i ].left += ( data * leftvol ) >> 8;
if( sc->soundChannels == 2 ) {
data = samples[ sampleOffset++ ];
}
samp[ i ].right += ( data * rightvol ) >> 8;
if( sampleOffset == SND_CHUNK_SIZE ) {
chunk = chunk->next;
samples = chunk->sndChunk;
sampleOffset = 0;
}
}
}
else {
fleftvol = ch->leftvol*snd_vol;
frightvol = ch->rightvol*snd_vol;
ooff = sampleOffset;
samples = chunk->sndChunk;
for( i = 0; i<count; i++ ) {
aoff = ooff;
ooff = ooff + ch->dopplerScale * sc->soundChannels;
boff = ooff;
fdata[ 0 ] = fdata[ 1 ] = 0;
for( j = aoff; j<boff; j += sc->soundChannels ) {
if( j == SND_CHUNK_SIZE ) {
chunk = chunk->next;
if( !chunk ) {
chunk = sc->soundData;
}
samples = chunk->sndChunk;
ooff -= SND_CHUNK_SIZE;
}
if( sc->soundChannels == 2 ) {
fdata[ 0 ] += samples[ j&( SND_CHUNK_SIZE - 1 ) ];
fdata[ 1 ] += samples[ ( j + 1 )&( SND_CHUNK_SIZE - 1 ) ];
}
else {
fdata[ 0 ] += samples[ j&( SND_CHUNK_SIZE - 1 ) ];
fdata[ 1 ] += samples[ j&( SND_CHUNK_SIZE - 1 ) ];
}
}
fdiv = 256 * ( boff - aoff ) / sc->soundChannels;
samp[ i ].left += ( fdata[ 0 ] * fleftvol ) / fdiv;
samp[ i ].right += ( fdata[ 1 ] * frightvol ) / fdiv;
}
}
}
static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
#if idppc_altivec
if( com_altivec->integer ) {
// must be in a seperate function or G3 systems will crash.
S_PaintChannelFrom16_altivec( ch, sc, count, sampleOffset, bufferOffset );
return;
}
#endif
S_PaintChannelFrom16_scalar( ch, sc, count, sampleOffset, bufferOffset );
}
void S_PaintChannelFromWavelet( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
int i;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
i = 0;
samp = &paintbuffer[ bufferOffset ];
chunk = sc->soundData;
while( sampleOffset >= ( SND_CHUNK_SIZE_FLOAT * 4 ) ) {
chunk = chunk->next;
sampleOffset -= ( SND_CHUNK_SIZE_FLOAT * 4 );
i++;
}
if( i != sfxScratchIndex || sfxScratchPointer != sc ) {
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
sfxScratchIndex = i;
sfxScratchPointer = sc;
}
samples = sfxScratchBuffer;
for( i = 0; i<count; i++ ) {
data = samples[ sampleOffset++ ];
samp[ i ].left += ( data * leftvol ) >> 8;
samp[ i ].right += ( data * rightvol ) >> 8;
if( sampleOffset == SND_CHUNK_SIZE * 2 ) {
chunk = chunk->next;
decodeWavelet( chunk, sfxScratchBuffer );
sfxScratchIndex++;
sampleOffset = 0;
}
}
}
void S_PaintChannelFromADPCM( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
int i;
portable_samplepair_t *samp;
sndBuffer *chunk;
short *samples;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
i = 0;
samp = &paintbuffer[ bufferOffset ];
chunk = sc->soundData;
if( ch->doppler ) {
sampleOffset = sampleOffset*ch->oldDopplerScale;
}
while( sampleOffset >= ( SND_CHUNK_SIZE * 4 ) ) {
chunk = chunk->next;
sampleOffset -= ( SND_CHUNK_SIZE * 4 );
i++;
}
if( i != sfxScratchIndex || sfxScratchPointer != sc ) {
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
sfxScratchIndex = i;
sfxScratchPointer = sc;
}
samples = sfxScratchBuffer;
for( i = 0; i<count; i++ ) {
data = samples[ sampleOffset++ ];
samp[ i ].left += ( data * leftvol ) >> 8;
samp[ i ].right += ( data * rightvol ) >> 8;
if( sampleOffset == SND_CHUNK_SIZE * 4 ) {
chunk = chunk->next;
S_AdpcmGetSamples( chunk, sfxScratchBuffer );
sampleOffset = 0;
sfxScratchIndex++;
}
}
}
void S_PaintChannelFromMuLaw( channel_t *ch, sfx_t *sc, int count, int sampleOffset, int bufferOffset ) {
int data;
int leftvol, rightvol;
int i;
portable_samplepair_t *samp;
sndBuffer *chunk;
byte *samples;
float ooff;
leftvol = ch->leftvol*snd_vol;
rightvol = ch->rightvol*snd_vol;
samp = &paintbuffer[ bufferOffset ];
chunk = sc->soundData;
while( sampleOffset >= ( SND_CHUNK_SIZE * 2 ) ) {
chunk = chunk->next;
sampleOffset -= ( SND_CHUNK_SIZE * 2 );
if( !chunk ) {
chunk = sc->soundData;
}
}
if( !ch->doppler ) {
samples = ( byte * )chunk->sndChunk + sampleOffset;
for( i = 0; i<count; i++ ) {
data = mulawToShort[ *samples ];
samp[ i ].left += ( data * leftvol ) >> 8;
samp[ i ].right += ( data * rightvol ) >> 8;
samples++;
if( chunk != NULL && samples == ( byte * )chunk->sndChunk + ( SND_CHUNK_SIZE * 2 ) ) {
chunk = chunk->next;
samples = ( byte * )chunk->sndChunk;
}
}
}
else {
ooff = sampleOffset;
samples = ( byte * )chunk->sndChunk;
for( i = 0; i<count; i++ ) {
data = mulawToShort[ samples[ ( int )( ooff ) ] ];
ooff = ooff + ch->dopplerScale;
samp[ i ].left += ( data * leftvol ) >> 8;
samp[ i ].right += ( data * rightvol ) >> 8;
if( ooff >= SND_CHUNK_SIZE * 2 ) {
chunk = chunk->next;
if( !chunk ) {
chunk = sc->soundData;
}
samples = ( byte * )chunk->sndChunk;
ooff = 0.0;
}
}
}
}
/*
===================
S_PaintChannels
===================
*/
void S_PaintChannels( int endtime ) {
int i;
int end;
int stream;
channel_t *ch;
sfx_t *sc;
int ltime, count;
int sampleOffset;
if( s_muted->integer )
snd_vol = 0;
else
snd_vol = s_volume->value * 255;
//Com_Printf ("%i to %i\n", s_paintedtime, endtime);
while( s_paintedtime < endtime ) {
// if paintbuffer is smaller than DMA buffer
// we may need to fill it multiple times
end = endtime;
if( endtime - s_paintedtime > PAINTBUFFER_SIZE ) {
end = s_paintedtime + PAINTBUFFER_SIZE;
}
// clear the paint buffer and mix any raw samples...
Com_Memset( paintbuffer, 0, sizeof( paintbuffer ) );
for( stream = 0; stream < MAX_RAW_STREAMS; stream++ ) {
if( s_rawend[ stream ] >= s_paintedtime ) {
// copy from the streaming sound source
const portable_samplepair_t *rawsamples = s_rawsamples[ stream ];
const int stop = ( end < s_rawend[ stream ] ) ? end : s_rawend[ stream ];
for( i = s_paintedtime; i < stop; i++ ) {
const int s = i&( MAX_RAW_SAMPLES - 1 );
paintbuffer[ i - s_paintedtime ].left += rawsamples[ s ].left;
paintbuffer[ i - s_paintedtime ].right += rawsamples[ s ].right;
}
}
}
// paint in the channels.
ch = s_channels;
2023-05-07 20:38:08 +02:00
for( i = 0; i < MAX_SOUNDCHANNELS; i++, ch++ ) {
2016-03-27 11:49:47 +02:00
if( !ch->thesfx || ( ch->leftvol<0.25 && ch->rightvol<0.25 ) ) {
continue;
}
ltime = s_paintedtime;
sc = ch->thesfx;
if( sc->soundData == NULL || sc->soundLength == 0 ) {
continue;
}
sampleOffset = ltime - ch->startSample;
count = end - ltime;
if( sampleOffset + count > sc->soundLength ) {
count = sc->soundLength - sampleOffset;
}
if( count > 0 ) {
if( sc->soundCompressionMethod == 1 ) {
S_PaintChannelFromADPCM( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
else if( sc->soundCompressionMethod == 2 ) {
S_PaintChannelFromWavelet( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
else if( sc->soundCompressionMethod == 3 ) {
S_PaintChannelFromMuLaw( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
else {
S_PaintChannelFrom16( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
}
}
// paint in the looped channels.
ch = loop_channels;
for( i = 0; i < numLoopChannels; i++, ch++ ) {
if( !ch->thesfx || ( !ch->leftvol && !ch->rightvol ) ) {
continue;
}
ltime = s_paintedtime;
sc = ch->thesfx;
if( sc->soundData == NULL || sc->soundLength == 0 ) {
continue;
}
// we might have to make two passes if it
// is a looping sound effect and the end of
// the sample is hit
do {
sampleOffset = ( ltime % sc->soundLength );
count = end - ltime;
if( sampleOffset + count > sc->soundLength ) {
count = sc->soundLength - sampleOffset;
}
if( count > 0 ) {
if( sc->soundCompressionMethod == 1 ) {
S_PaintChannelFromADPCM( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
else if( sc->soundCompressionMethod == 2 ) {
S_PaintChannelFromWavelet( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
else if( sc->soundCompressionMethod == 3 ) {
S_PaintChannelFromMuLaw( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
else {
S_PaintChannelFrom16( ch, sc, count, sampleOffset, ltime - s_paintedtime );
}
ltime += count;
}
} while( ltime < end );
}
// transfer out according to DMA format
S_TransferPaintBuffer( end );
s_paintedtime = end;
}
}