252 lines
6.8 KiB
C
252 lines
6.8 KiB
C
/*
|
|
* PCM -> ADPCM routines
|
|
*
|
|
* based on work by Bitmaster and extended
|
|
*/
|
|
|
|
// The version here was converted by double to ints, and so
|
|
// it loses much precision... but if that wasn't done it would have
|
|
// a very bad performance on the PlayStation hardware
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <strings.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <psx.h>
|
|
#include "adpcm.h"
|
|
|
|
#define PCM_BUFFER_SIZE 128*28
|
|
|
|
short pcm_buffer[PCM_BUFFER_SIZE];
|
|
|
|
void SsAdpcm_find_predict( short *samples, int *d_samples, int *predict_nr, int *shift_factor);
|
|
void SsAdpcm_pack( int *d_samples, short *four_bit, int predict_nr, int shift_factor);
|
|
|
|
int SsAdpcmPack(void *pcm_data, void *adpcm_data, int sample_len,
|
|
int sample_fmt, int adpcm_len, int enable_looping)
|
|
{
|
|
short *ptr;
|
|
unsigned char *pcm_data_c = pcm_data;
|
|
short *pcm_data_s = pcm_data;
|
|
unsigned char *adpcm_data_c = adpcm_data;
|
|
int d_samples[28];
|
|
short four_bit[28];
|
|
int predict_nr;
|
|
int shift_factor;
|
|
int flags;
|
|
int size;
|
|
int i, j, k;
|
|
unsigned char d;
|
|
int ap = 0;
|
|
|
|
/*printf("pcm_data = %x, adpcm_data = %x, len = %x, fmt = %x, alen = %x,"
|
|
"loop = %x\n", pcm_data, adpcm_data, sample_len, sample_fmt,
|
|
adpcm_len, enable_looping);
|
|
for(i=0;i<8;i++)
|
|
printf("I[%d] = %x\n", i, pcm_data_c[i]);*/
|
|
|
|
if(enable_looping)
|
|
flags = 6;
|
|
else
|
|
flags = 0;
|
|
|
|
//sample_len -= sample_len % 28;
|
|
|
|
while( sample_len > 0 ) {
|
|
size = ( sample_len >= PCM_BUFFER_SIZE ) ? PCM_BUFFER_SIZE : sample_len;
|
|
|
|
if(sample_fmt == FMT_U8)
|
|
{
|
|
for(i = 0; i < size; i++)
|
|
{
|
|
//c = fgetc(fp);
|
|
pcm_buffer[i] = *(pcm_data_c++);
|
|
pcm_buffer[i] ^= 0x80;
|
|
pcm_buffer[i] <<= 8;
|
|
}
|
|
}
|
|
else if(sample_fmt == FMT_S16)
|
|
{
|
|
//fread( wave, sizeof( short ), size, fp );
|
|
memcpy(pcm_buffer, pcm_data_s, size * sizeof(short));
|
|
pcm_data_s += size;
|
|
}
|
|
else
|
|
{
|
|
printf("%s, line %d: Unknown source sample format!, id=%d\n",__FUNCTION__,__LINE__,sample_fmt);
|
|
return 0;
|
|
}
|
|
|
|
i = size / 28;
|
|
if ( size % 28 ) {
|
|
for ( j = size % 28; j < 28; j++ )
|
|
pcm_buffer[28*i+j] = 0;
|
|
i++;
|
|
}
|
|
|
|
for ( j = 0; j < i; j++ ) { // pack 28 samples
|
|
ptr = pcm_buffer + j * 28;
|
|
SsAdpcm_find_predict( ptr, d_samples, &predict_nr, &shift_factor );
|
|
SsAdpcm_pack( d_samples, four_bit, predict_nr, shift_factor );
|
|
d = ( predict_nr << 4 ) | shift_factor;
|
|
// fputc( d, vag );
|
|
adpcm_data_c[ap++] = d;
|
|
if(ap>=adpcm_len) goto adpcm_too_big;
|
|
// fputc( flags, vag );
|
|
adpcm_data_c[ap++] = flags;
|
|
if(ap>=adpcm_len) goto adpcm_too_big;
|
|
for ( k = 0; k < 28; k += 2 ) {
|
|
d = ( ( four_bit[k+1] >> 8 ) & 0xf0 ) | ( ( four_bit[k] >> 12 ) & 0xf );
|
|
// fputc( d, vag );
|
|
adpcm_data_c[ap++] = d;
|
|
if(ap>=adpcm_len) goto adpcm_too_big;
|
|
}
|
|
sample_len -= 28;
|
|
if ( sample_len < 28 && enable_looping == 0)
|
|
flags = 1;
|
|
|
|
if(enable_looping)
|
|
flags = 2;
|
|
}
|
|
}
|
|
|
|
// fputc( ( predict_nr << 4 ) | shift_factor, vag );
|
|
adpcm_data_c[ap++] = ( predict_nr << 4 ) | shift_factor;
|
|
if(ap>=adpcm_len) goto adpcm_too_big;
|
|
|
|
if(enable_looping == 1)
|
|
// fputc(3, vag);
|
|
adpcm_data_c[ap++] = 3;
|
|
else if(enable_looping == 2)
|
|
adpcm_data_c[ap++] = 0;
|
|
else if(enable_looping == 3)
|
|
adpcm_data_c[ap++] = 2;
|
|
else
|
|
// fputc( 7, vag ); // end flag
|
|
adpcm_data_c[ap++] = 7;
|
|
|
|
if(ap>=adpcm_len) goto adpcm_too_big;
|
|
|
|
if(enable_looping != 2)
|
|
{
|
|
for ( i = 0; i < 14; i++ )
|
|
// fputc( 0, vag );
|
|
adpcm_data_c[ap++] = 0;
|
|
}
|
|
|
|
if(ap>=adpcm_len) goto adpcm_too_big;
|
|
|
|
return ap;
|
|
|
|
adpcm_too_big:
|
|
printf("%s: Resulting ADPCM data would have been larger than the output array length! Exiting %s.\n", __FUNCTION__, __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const int f[5][2] = { { 0, 0 },
|
|
{ 60, 0 },
|
|
{ 115, -52},
|
|
{ 98, -55},
|
|
{ 122, -60} };
|
|
|
|
void SsAdpcm_find_predict( short *samples, int *d_samples, int *predict_nr, int *shift_factor )
|
|
{
|
|
int i, j;
|
|
int buffer[28][5];
|
|
int min = 0x7fffffff;
|
|
int max[5];
|
|
int ds;
|
|
int min2;
|
|
int shift_mask;
|
|
static int _s_1 = 0; // s[t-1]
|
|
static int _s_2 = 0; // s[t-2]
|
|
int s_0, s_1, s_2;
|
|
|
|
|
|
for ( i = 0; i < 5; i++ ) {
|
|
max[i] = 0.0;
|
|
s_1 = _s_1;
|
|
s_2 = _s_2;
|
|
for ( j = 0; j < 28; j ++ ) {
|
|
s_0 = (int) samples[j]; // s[t-0]
|
|
if ( s_0 > 32767 )
|
|
s_0 = 32767;
|
|
if ( s_0 < - 32768 )
|
|
s_0 = -32768;
|
|
ds = s_0 + s_1 * f[i][0] + s_2 * f[i][1];
|
|
buffer[j][i] = ds;
|
|
if ( ds > max[i] )
|
|
max[i] = ds;
|
|
// printf( "%+5.2f\n", s2 );
|
|
s_2 = s_1; // new s[t-2]
|
|
s_1 = s_0; // new s[t-1]
|
|
}
|
|
|
|
if ( max[i] <= min ) {
|
|
// printf("min = %d, max[i] = %d", min, max[i]);
|
|
min = max[i];
|
|
*predict_nr = i;
|
|
}
|
|
if ( min <= 7 ) {
|
|
*predict_nr = 0;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// store s[t-2] and s[t-1] in a static variable
|
|
// these than used in the next function call
|
|
|
|
_s_1 = s_1;
|
|
_s_2 = s_2;
|
|
|
|
for ( i = 0; i < 28; i++ )
|
|
d_samples[i] = buffer[i][*predict_nr];
|
|
|
|
// if ( min > 32767.0 )
|
|
// min = 32767.0;
|
|
|
|
min2 = ( int ) min;
|
|
shift_mask = 0x4000;
|
|
*shift_factor = 0;
|
|
|
|
while( *shift_factor < 12 ) {
|
|
if ( shift_mask & ( min2 + ( shift_mask >> 3 ) ) )
|
|
break;
|
|
(*shift_factor)++;
|
|
shift_mask = shift_mask >> 1;
|
|
}
|
|
|
|
}
|
|
|
|
void SsAdpcm_pack( int *d_samples, short *four_bit, int predict_nr, int shift_factor )
|
|
{
|
|
int ds;
|
|
int di;
|
|
int s_0;
|
|
static int s_1 = 0;
|
|
static int s_2 = 0;
|
|
int i;
|
|
|
|
for ( i = 0; i < 28; i++ ) {
|
|
s_0 = d_samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
|
|
ds = s_0 * (int) ( 1 << shift_factor );
|
|
|
|
di = ( (int) ds + 0x800 ) & 0xfffff000;
|
|
|
|
if ( di > 32767 )
|
|
di = 32767;
|
|
if ( di < -32768 )
|
|
di = -32768;
|
|
|
|
four_bit[i] = (short) di;
|
|
|
|
di = di >> shift_factor;
|
|
s_2 = s_1;
|
|
s_1 = (int) di - s_0;
|
|
|
|
}
|
|
}
|