/* * 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 #include #include #include #include #include #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; } }