2018-07-09 18:05:31 +00:00
|
|
|
#include "amuse/N64MusyXCodec.hpp"
|
2019-08-25 10:28:43 +00:00
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
#include <cstring>
|
2016-05-28 02:28:59 +00:00
|
|
|
|
|
|
|
/* Acknowledgements:
|
|
|
|
* SubDrag for N64 Sound Tool (http://www.goldeneyevault.com/viewfile.php?id=212)
|
|
|
|
* Bobby Smiles for MusyX codec research
|
|
|
|
*/
|
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
static int rdot(unsigned n, const int16_t* x, const int16_t* y) {
|
|
|
|
int accu = 0;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
y += n;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
while (n != 0) {
|
|
|
|
accu += ((int)*(x++) * (int)*(--y));
|
|
|
|
--n;
|
|
|
|
}
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
return accu;
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
static int16_t adpcm_get_predicted_sample(unsigned char byte, unsigned char mask, unsigned lshift, unsigned rshift) {
|
|
|
|
int16_t sample = ((int16_t)byte & (int16_t)mask) << lshift;
|
|
|
|
sample >>= rshift; /* signed */
|
|
|
|
return sample;
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
static void adpcm_get_predicted_frame(int16_t* dst, const unsigned char* src, const unsigned char* nibbles,
|
|
|
|
unsigned rshift) {
|
|
|
|
*(dst++) = (src[0] << 8) | src[1];
|
|
|
|
*(dst++) = (src[2] << 8) | src[3];
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
for (unsigned i = 1; i < 16; ++i) {
|
|
|
|
unsigned char byteData = nibbles[i];
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
*(dst++) = adpcm_get_predicted_sample(byteData, 0xf0, 8, rshift);
|
|
|
|
*(dst++) = adpcm_get_predicted_sample(byteData, 0x0f, 12, rshift);
|
|
|
|
}
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
static unsigned adpcm_decode_upto_8_samples(int16_t* dst, const int16_t* src, const int16_t* cb_entry,
|
|
|
|
const int16_t* last_samples, unsigned size) {
|
|
|
|
if (size == 0)
|
|
|
|
return 0;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
const int16_t* book1 = cb_entry;
|
|
|
|
const int16_t* book2 = cb_entry + 8;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
int16_t l1 = last_samples[0];
|
|
|
|
int16_t l2 = last_samples[1];
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
int accu;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
for (unsigned i = 0; i < size; ++i) {
|
|
|
|
accu = (int)src[i] << 11;
|
|
|
|
accu += book1[i] * l1 + book2[i] * l2 + rdot(i, book2, src);
|
|
|
|
dst[i] = N64MusyXSampClamp(accu >> 11);
|
|
|
|
}
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
return size;
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
unsigned N64MusyXDecompressFrame(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8], unsigned lastSample) {
|
|
|
|
int16_t frame[32];
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
unsigned remSamples = lastSample;
|
|
|
|
unsigned samples = 0;
|
|
|
|
unsigned procSamples, procRes;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
unsigned char c2 = in[0x8];
|
|
|
|
c2 = c2 % 0x80;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
const int16_t* bookPredictors = coefs[(c2 & 0xf0) >> 4][0];
|
|
|
|
unsigned int rshift = (c2 & 0x0f);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
adpcm_get_predicted_frame(frame, &in[0x0], &in[0x8], rshift);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
procSamples = (remSamples < 2) ? remSamples : 2;
|
|
|
|
memmove(out, frame, 2 * procSamples);
|
|
|
|
samples += procSamples;
|
|
|
|
remSamples -= procSamples;
|
|
|
|
if (samples == lastSample)
|
|
|
|
return samples;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
#define adpcm_decode_upto_end_samples(inOffset, outOffset, size) \
|
|
|
|
procSamples = (remSamples < size) ? remSamples : size; \
|
|
|
|
procRes = \
|
|
|
|
adpcm_decode_upto_8_samples(out + inOffset, frame + inOffset, bookPredictors, out + outOffset, procSamples); \
|
|
|
|
samples += procRes; \
|
|
|
|
remSamples -= procRes; \
|
|
|
|
if (samples == lastSample) \
|
|
|
|
return samples;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
adpcm_decode_upto_end_samples(2, 0, 6);
|
|
|
|
adpcm_decode_upto_end_samples(8, 6, 8);
|
|
|
|
adpcm_decode_upto_end_samples(16, 14, 8);
|
|
|
|
adpcm_decode_upto_end_samples(24, 22, 8);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
out += 32;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
c2 = in[0x18];
|
|
|
|
c2 = c2 % 0x80;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
bookPredictors = coefs[(c2 & 0xf0) >> 4][0];
|
|
|
|
rshift = (c2 & 0x0f);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
adpcm_get_predicted_frame(frame, &in[0x4], &in[0x18], rshift);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
procSamples = (remSamples < 2) ? remSamples : 2;
|
|
|
|
memmove(out, frame, 2 * procSamples);
|
|
|
|
samples += procSamples;
|
|
|
|
remSamples -= procSamples;
|
|
|
|
if (samples == lastSample)
|
|
|
|
return samples;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
adpcm_decode_upto_end_samples(2, 0, 6);
|
|
|
|
adpcm_decode_upto_end_samples(8, 6, 8);
|
|
|
|
adpcm_decode_upto_end_samples(16, 14, 8);
|
|
|
|
adpcm_decode_upto_end_samples(24, 22, 8);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
return samples;
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:20:09 +00:00
|
|
|
unsigned N64MusyXDecompressFrameRanged(int16_t* out, const uint8_t* in, const int16_t coefs[8][2][8],
|
|
|
|
unsigned firstSample, unsigned lastSample) {
|
|
|
|
int16_t final[64];
|
|
|
|
unsigned procSamples = N64MusyXDecompressFrame(final, in, coefs, firstSample + lastSample);
|
|
|
|
unsigned samples = procSamples - firstSample;
|
|
|
|
memmove(out, final + firstSample, samples * 2);
|
|
|
|
return samples;
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|