Xash3D FWGS engine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

312 lines
7.9 KiB

/*
synth.c - compact version of famous library mpg123
Copyright (C) 2017 Uncle Mike
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 3 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.
*/
#include "mpg123.h"
#include "sample.h"
#define BACKPEDAL 0x10 // we use autoincrement and thus need this re-adjustment for window/b0.
#define BLOCK 0x40 // one decoding block is 64 samples.
#define WRITE_SHORT_SAMPLE( samples, sum, clip ) \
if(( sum ) > 32767.0f ) { *(samples) = 0x7fff; (clip)++; } \
else if(( sum ) < -32768.0f ) { *(samples) = -0x8000; (clip)++; } \
else { *(samples) = REAL_TO_SHORT( sum ); }
// main synth function, uses the plain dct64
static int synth_1to1( float *bandPtr, int channel, mpg123_handle_t *fr, int final )
{
static const int step = 2;
short *samples = (short *) (fr->buffer.data + fr->buffer.fill);
float *b0, **buf; // (*buf)[0x110];
int clip = 0;
int bo1;
if( !channel )
{
fr->bo--;
fr->bo &= 0xf;
buf = fr->float_buffs[0];
}
else
{
samples++;
buf = fr->float_buffs[1];
}
if( fr->bo & 0x1 )
{
b0 = buf[0];
bo1 = fr->bo;
dct64( buf[1] + ((fr->bo + 1) & 0xf ), buf[0] + fr->bo, bandPtr );
}
else
{
b0 = buf[1];
bo1 = fr->bo+1;
dct64( buf[0] + fr->bo, buf[1] + fr->bo + 1, bandPtr );
}
{
float *window = fr->decwin + 16 - bo1;
register int j;
for( j = (BLOCK / 4); j; j--, b0 += 0x400 / BLOCK - BACKPEDAL, window += 0x800 / BLOCK - BACKPEDAL, samples += step )
{
float sum;
sum = REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
sum += REAL_MUL_SYNTH( *window++, *b0++ );
sum -= REAL_MUL_SYNTH( *window++, *b0++ );
WRITE_SHORT_SAMPLE( samples, sum, clip );
}
{
float sum;
sum = REAL_MUL_SYNTH( window[0x0], b0[0x0] );
sum += REAL_MUL_SYNTH( window[0x2], b0[0x2] );
sum += REAL_MUL_SYNTH( window[0x4], b0[0x4] );
sum += REAL_MUL_SYNTH( window[0x6], b0[0x6] );
sum += REAL_MUL_SYNTH( window[0x8], b0[0x8] );
sum += REAL_MUL_SYNTH( window[0xA], b0[0xA] );
sum += REAL_MUL_SYNTH( window[0xC], b0[0xC] );
sum += REAL_MUL_SYNTH( window[0xE], b0[0xE] );
WRITE_SHORT_SAMPLE( samples, sum, clip );
samples += step;
b0 -= 0x400 / BLOCK;
window -= 0x800 / BLOCK;
}
window += bo1<<1;
for( j= (BLOCK / 4) - 1; j; j--, b0 -= 0x400 / BLOCK + BACKPEDAL, window -= 0x800 / BLOCK - BACKPEDAL, samples += step )
{
float sum;
sum = -REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
sum -= REAL_MUL_SYNTH( *(--window), *b0++ );
WRITE_SHORT_SAMPLE( samples, sum, clip );
}
}
if( final ) fr->buffer.fill += BLOCK * sizeof( short );
return clip;
}
// the call of left and right plain synth, wrapped.
// this may be replaced by a direct stereo optimized synth.
static int synth_stereo( float *bandPtr_l, float *bandPtr_r, mpg123_handle_t *fr )
{
int clip;
clip = (fr->synth)( bandPtr_l, 0, fr, 0 );
clip += (fr->synth)( bandPtr_r, 1, fr, 1 );
return clip;
}
// mono to stereo synth, wrapping over synth_1to1
static int synth_1to1_m2s(float *bandPtr, mpg123_handle_t *fr )
{
byte *samples = fr->buffer.data;
int i, ret;
ret = synth_1to1( bandPtr, 0, fr, 1 );
samples += fr->buffer.fill - BLOCK * sizeof( short );
for( i = 0; i < (BLOCK / 2); i++ )
{
((short *)samples)[1] = ((short *)samples)[0];
samples += 2 * sizeof( short );
}
return ret;
}
// mono synth, wrapping over synth_1to1
static int synth_1to1_mono( float *bandPtr, mpg123_handle_t *fr )
{
short samples_tmp[BLOCK];
short *tmp1 = samples_tmp;
byte *samples = fr->buffer.data;
int pnt = fr->buffer.fill;
int i, ret;
// save buffer stuff, trick samples_tmp into there, decode, restore
fr->buffer.data = (byte *)samples_tmp;
fr->buffer.fill = 0;
ret = synth_1to1( bandPtr, 0, fr, 0 ); // decode into samples_tmp
fr->buffer.data = samples; // restore original value
// now append samples from samples_tmp
samples += pnt; // just the next mem in frame buffer
for( i = 0; i < (BLOCK / 2); i++ )
{
*((short *)samples) = *tmp1;
samples += sizeof( short );
tmp1 += 2;
}
fr->buffer.fill = pnt + (BLOCK / 2) * sizeof( short );
return ret;
}
static const struct synth_s synth_base =
{
{
{ synth_1to1 } // plain
},
{
{ synth_stereo } // stereo, by default only wrappers over plain synth
},
{
{ synth_1to1_m2s } // mono2stereo
},
{
{ synth_1to1_mono } // mono
}
};
void init_synth( mpg123_handle_t *fr )
{
fr->synths = synth_base;
}
static int find_synth(func_synth synth, const func_synth synths[r_limit][f_limit])
{
enum synth_resample ri;
enum synth_format fi;
for( ri = 0; ri < r_limit; ++ri )
{
for( fi = 0; fi < f_limit; ++fi )
{
if( synth == synths[ri][fi] )
return TRUE;
}
}
return FALSE;
}
enum optdec
{
autodec = 0,
generic,
nodec
};
// determine what kind of decoder is actually active
// this depends on runtime choices which may cause fallback to i386 or generic code.
static int find_dectype( mpg123_handle_t *fr )
{
enum optdec type = nodec;
func_synth basic_synth = fr->synth;
if( find_synth( basic_synth, synth_base.plain ))
type = generic;
if( type != nodec )
{
return MPG123_OK;
}
else
{
fr->err = MPG123_BAD_DECODER_SETUP;
return MPG123_ERR;
}
}
// set synth functions for current frame
int set_synth_functions( mpg123_handle_t *fr )
{
enum synth_resample resample = r_none;
enum synth_format basic_format = f_none; // default is always 16bit, or whatever.
if( fr->af.dec_enc & MPG123_ENC_16 )
basic_format = f_16;
// make sure the chosen format is compiled into this lib.
if( basic_format == f_none )
return -1;
// be explicit about downsampling variant.
switch( fr->down_sample )
{
case 0: resample = r_1to1; break;
}
if( resample == r_none )
return -1;
// finally selecting the synth functions for stereo / mono.
fr->synth = fr->synths.plain[resample][basic_format];
fr->synth_stereo = fr->synths.stereo[resample][basic_format];
fr->synth_mono = fr->af.channels == 2 ? fr->synths.mono2stereo[resample][basic_format] : fr->synths.mono[resample][basic_format];
if( find_dectype( fr ) != MPG123_OK )
{
fr->err = MPG123_BAD_DECODER_SETUP;
return MPG123_ERR;
}
if( frame_buffers( fr ) != 0 )
{
fr->err = MPG123_NO_BUFFERS;
return MPG123_ERR;
}
init_layer3_stuff( fr );
fr->make_decode_tables = make_decode_tables;
// we allocated the table buffers just now, so (re)create the tables.
fr->make_decode_tables( fr );
return 0;
}