parent
90fc7eb88b
commit
260e7c82a9
1 changed files with 197 additions and 0 deletions
@ -0,0 +1,197 @@ |
||||
#include <cstdio> |
||||
#include <cstdlib> |
||||
#include <cstring> |
||||
#include <cstdint> |
||||
|
||||
const int STEPS[] = { |
||||
256, 272, 304, 336, 368, 400, 448, 496, 544, 592, 656, 720, |
||||
800, 880, 960, 1056, 1168, 1280, 1408, 1552, 1712, 1888, 2080, 2288, |
||||
2512, 2768, 3040, 3344, 3680, 4048, 4464, 4912, 5392, 5936, 6528, 7184, |
||||
7904, 8704, 9568, 10528, 11584, 12736, 14016, 15408, 16960, 18656, 20512, 22576, |
||||
24832 |
||||
}; |
||||
|
||||
const int CHANGES[] = { |
||||
-1, -1, -1, -1, 2, 4, 6, 8, |
||||
-1, -1, -1, -1, 2, 4, 6, 8 |
||||
}; |
||||
|
||||
class AdpcmWave { |
||||
public: |
||||
AdpcmWave() { |
||||
} |
||||
|
||||
unsigned short *process_sample_batch(unsigned char *samples, int samples_len, unsigned short *output, int output_offset, bool encode, int channel, int cidx_max) { |
||||
int step_index = 0; |
||||
int pcm_sample = 0; |
||||
int sample_encode = 0; |
||||
int mask = 0xfffffffe; |
||||
|
||||
for(int idx = 0; idx < samples_len; idx += cidx_max) { |
||||
for(int cidx = 0; cidx < cidx_max; cidx++) { |
||||
int sample = 0; |
||||
|
||||
if (encode) { |
||||
sample = *(short*)(samples + (idx + cidx) * (4 / cidx_max) + (channel * 2)); |
||||
} else { |
||||
sample = samples[idx / cidx_max]; |
||||
|
||||
if (channel + cidx == 0) { |
||||
sample = (sample >> 4) & 0x0f; |
||||
} else if (channel + cidx == 1) { |
||||
sample = sample & 0x0f; |
||||
} |
||||
} |
||||
|
||||
int step = STEPS[step_index]; |
||||
|
||||
if (encode) { |
||||
int delta = sample - pcm_sample; |
||||
|
||||
int sign = 0; |
||||
if (delta < 0) { |
||||
sign = 0x08; |
||||
delta = -delta; |
||||
} |
||||
|
||||
int v = (delta << 2); |
||||
v = v / step; |
||||
|
||||
|
||||
if (v > 7) { |
||||
v = 7; |
||||
} |
||||
|
||||
sample = sign | v; |
||||
sample_encode = sample; |
||||
} |
||||
|
||||
int new_sample = (step >> 3) |
||||
+ ((step >> 2) & -(sample & 1)) |
||||
+ ((step >> 1) & -((sample >> 1) & 1)) |
||||
+ (step & -((sample >> 2) & 1)); |
||||
|
||||
step_index += CHANGES[sample % 16]; |
||||
|
||||
if (step_index > 48) { |
||||
step_index = 48; |
||||
} else if (step_index < 0) { |
||||
step_index = 0; |
||||
} |
||||
|
||||
if ((sample & 0x08) != 0) { |
||||
new_sample = -new_sample; |
||||
} |
||||
|
||||
pcm_sample += new_sample; |
||||
|
||||
if (pcm_sample > 32767) { |
||||
pcm_sample = 32767; |
||||
} else if (pcm_sample < -32768) { |
||||
pcm_sample = -32768; |
||||
} |
||||
|
||||
if (encode) { |
||||
if (channel + cidx == 0) { |
||||
((unsigned char*)output)[idx / cidx_max] |= (unsigned char)(((sample_encode & 0xff) << 4) & 0xff); |
||||
} else if (channel + cidx == 1) { |
||||
((unsigned char*)output)[idx / cidx_max] |= (unsigned char)(sample_encode & 0xff); |
||||
} |
||||
} else { |
||||
output[((idx / cidx_max) * 2) + output_offset + cidx] = (unsigned short)(pcm_sample & 0xffff); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return output; |
||||
} |
||||
}; |
||||
|
||||
unsigned char *process_stereo(unsigned char *samples, int samples_len, int *output_len, bool encode) { |
||||
AdpcmWave decoder; |
||||
AdpcmWave decoder2; |
||||
|
||||
unsigned short *output = (unsigned short*)calloc(samples_len * 2, sizeof(unsigned short)); |
||||
|
||||
if (encode) { |
||||
decoder.process_sample_batch(samples, samples_len / 2 / 2, output, 0, encode, 0, 1); |
||||
decoder2.process_sample_batch(samples, samples_len / 2 / 2, output, 1, encode, 1, 1); |
||||
*output_len = samples_len / 4; |
||||
} else { |
||||
decoder.process_sample_batch(samples, samples_len, output, 0, encode, 0, 1); |
||||
decoder2.process_sample_batch(samples, samples_len, output, 1, encode, 1, 1); |
||||
*output_len = samples_len * 2 * sizeof(unsigned short); |
||||
} |
||||
|
||||
return (unsigned char*)output; |
||||
} |
||||
|
||||
unsigned char *process_mono(unsigned char *samples, int samples_len, int *output_len, bool encode) { |
||||
AdpcmWave decoder; |
||||
|
||||
unsigned short *output = (unsigned short*)calloc(samples_len * 2, sizeof(unsigned short)); |
||||
|
||||
if (encode) { |
||||
decoder.process_sample_batch(samples, samples_len / 2, output, 0, encode, 0, 2); |
||||
*output_len = samples_len / 4; |
||||
} else { |
||||
decoder.process_sample_batch(samples, samples_len * 2, output, 0, encode, 0, 2); |
||||
*output_len = samples_len * 2 * sizeof(unsigned short); |
||||
} |
||||
|
||||
return (unsigned char*)output; |
||||
} |
||||
|
||||
int parse_file(char *mode, char *filename, char *output_filename, int channels) { |
||||
FILE *file = fopen(filename, "rb"); |
||||
|
||||
if (!file) { |
||||
printf("Couldn't open %s\n", filename); |
||||
exit(1); |
||||
} |
||||
|
||||
int samples_len = 0; |
||||
fseek(file, 0, SEEK_END); |
||||
samples_len = ftell(file); |
||||
rewind(file); |
||||
|
||||
unsigned char *samples = (unsigned char*)calloc(samples_len, sizeof(unsigned char)); |
||||
fread(samples, 1, samples_len, file); |
||||
fclose(file); |
||||
|
||||
int output_len = 0; |
||||
unsigned char *decoded; |
||||
|
||||
uint32_t data_offset = 0; |
||||
if (memcmp(samples, "ADP\x02", 4) == 0) { |
||||
uint32_t filesize = (samples[4] << 24) | (samples[5] << 16) | (samples[6] << 8) | samples[7]; |
||||
|
||||
if (filesize == samples_len - 0x10) { |
||||
data_offset = 0x10; |
||||
printf("Found PPP 2nd ADP file, skipping header\n"); |
||||
} |
||||
} |
||||
|
||||
bool is_encode = mode[0] == 'e' || mode[0] == 'E'; |
||||
if (channels == 1) { |
||||
decoded = process_mono(samples + data_offset, samples_len - data_offset, &output_len, is_encode); |
||||
} else if (channels == 2) { |
||||
decoded = process_stereo(samples + data_offset, samples_len - data_offset, &output_len, is_encode); |
||||
} |
||||
|
||||
file = fopen(output_filename, "wb"); |
||||
fwrite(decoded, 1, output_len, file); |
||||
fclose(file); |
||||
|
||||
free(samples); |
||||
free(decoded); |
||||
} |
||||
|
||||
int main(int argc, char **argv) { |
||||
if (argc != 5) { |
||||
printf("usage: %s [d/e] input output channels\n", argv[0]); |
||||
exit(1); |
||||
} |
||||
|
||||
parse_file(argv[1], argv[2], argv[3], atoi(argv[4])); |
||||
} |
Loading…
Reference in new issue