summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKiritoDv <kiritodev01@gmail.com>2023-09-03 23:29:11 -0600
committerKiritoDv <kiritodev01@gmail.com>2023-09-03 23:29:11 -0600
commit50b6c6f65bc0da4a2c07e24c75dcd19baba60083 (patch)
tree19fd3432904b3ad5cc4ad5ad7274f8a2434f2778 /src
parent063224c69d564d6e10dd4febf25caa9f59fccfda (diff)
Added full SM64 -> AIFF Decoding
Diffstat (limited to 'src')
-rw-r--r--src/audio/AIFCDecode.cpp656
-rw-r--r--src/audio/AIFCDecode.h6
-rw-r--r--src/audio/AIFCWriter.cpp48
-rw-r--r--src/audio/AIFCWriter.h36
-rw-r--r--src/audio/AudioManager.cpp4
-rw-r--r--src/audio/AudioManager.h13
-rw-r--r--src/factories/AudioFactory.cpp9
7 files changed, 683 insertions, 89 deletions
diff --git a/src/audio/AIFCDecode.cpp b/src/audio/AIFCDecode.cpp
new file mode 100644
index 0000000..21eec08
--- /dev/null
+++ b/src/audio/AIFCDecode.cpp
@@ -0,0 +1,656 @@
+/**
+ * Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way
+ * that roundtrips with vadpcm_enc.
+ */
+#include "AIFCDecode.h"
+#include <cassert>
+#include <cstring>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdarg>
+#include "binarytools/BinaryWriter.h"
+#include "BinaryReader.h"
+
+typedef signed char s8;
+typedef short s16;
+typedef int s32;
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef float f32;
+
+#undef BSWAP16
+#undef BSWAP32
+#undef BSWAP64
+
+#define bswap16(x) __builtin_bswap16(x)
+#define bswap32(x) __builtin_bswap32(x)
+#define BSWAP16(x) x = bswap16(x)
+#define BSWAP32(x) x = bswap32(x)
+
+#define BSWAP16_MANY(x, n) for (s32 _i = 0; _i < n; _i++) BSWAP16((x)[_i])
+
+#ifndef WIN32
+#define NORETURN __attribute__((noreturn))
+#define UNUSED __attribute__((unused))
+#else
+#define NORETURN
+#define UNUSED
+#endif
+
+typedef struct {
+ u32 ckID;
+ u32 ckSize;
+} ChunkHeader;
+
+typedef struct {
+ u32 ckID;
+ u32 ckSize;
+ u32 formType;
+} Chunk;
+
+typedef struct {
+ s16 numChannels;
+ u16 numFramesH;
+ u16 numFramesL;
+ s16 sampleSize;
+ s16 sampleRate[5]; // 80-bit float
+ u16 compressionTypeH;
+ u16 compressionTypeL;
+} CommonChunk;
+
+typedef struct {
+ s16 MarkerID;
+ u16 positionH;
+ u16 positionL;
+} Marker;
+
+typedef struct {
+ s16 playMode;
+ s16 beginLoop;
+ s16 endLoop;
+} Loop;
+
+typedef struct {
+ s8 baseNote;
+ s8 detune;
+ s8 lowNote;
+ s8 highNote;
+ s8 lowVelocity;
+ s8 highVelocity;
+ s16 gain;
+ Loop sustainLoop;
+ Loop releaseLoop;
+} InstrumentChunk;
+
+typedef struct {
+ s32 offset;
+ s32 blockSize;
+} SoundDataChunk;
+
+typedef struct {
+ s16 version;
+ s16 order;
+ s16 nEntries;
+} CodeChunk;
+
+typedef struct
+{
+ u32 start;
+ u32 end;
+ u32 count;
+ s16 state[16];
+} ALADPCMloop;
+
+#define checked_fread(a, b, c, d) d.Read((char*) a, b * c)
+
+NORETURN
+void fail_parse(const char *fmt, ...)
+{
+ char *formatted = nullptr;
+ va_list ap;
+ va_start(ap, fmt);
+ int size = vsnprintf(nullptr, 0, fmt, ap);
+ va_end(ap);
+ if (size >= 0) {
+ size++;
+ formatted = static_cast<char *>(malloc(size));
+ if (formatted != nullptr) {
+ va_start(ap, fmt);
+ size = vsnprintf(formatted, size, fmt, ap);
+ va_end(ap);
+ if (size < 0) {
+ free(formatted);
+ formatted = nullptr;
+ }
+ }
+ }
+
+ if (formatted != nullptr) {
+ throw std::runtime_error(formatted);
+ free(formatted);
+ }
+ throw std::runtime_error("Error parsing file");
+}
+
+s32 myrand()
+{
+ static u64 state = 1619236481962341ULL;
+ state *= 3123692312231ULL;
+ state++;
+ return state >> 33;
+}
+
+s16 qsample(s32 x, s32 scale)
+{
+ // Compute x / 2^scale rounded to the nearest integer, breaking ties towards zero.
+ if (scale == 0) return x;
+ return (x + (1 << (scale - 1)) - (x > 0)) >> scale;
+}
+
+s16 clamp_to_s16(s32 x)
+{
+ if (x < -0x8000) return -0x8000;
+ if (x > 0x7fff) return 0x7fff;
+ return (s16) x;
+}
+
+s32 toi4(s32 x)
+{
+ if (x >= 8) return x - 16;
+ return x;
+}
+
+s32 readaifccodebook(LUS::BinaryReader& fhandle, s32 ****table, s16 *order, s16 *npredictors)
+{
+ checked_fread(order, sizeof(s16), 1, fhandle);
+ BSWAP16(*order);
+ checked_fread(npredictors, sizeof(s16), 1, fhandle);
+ BSWAP16(*npredictors);
+ *table = static_cast<s32 ***>(malloc(*npredictors * sizeof(s32 **)));
+ for (s32 i = 0; i < *npredictors; i++) {
+ (*table)[i] = static_cast<s32 **>(malloc(8 * sizeof(s32 *)));
+ for (s32 j = 0; j < 8; j++) {
+ (*table)[i][j] = static_cast<s32 *>(malloc((*order + 8) * sizeof(s32)));
+ }
+ }
+
+ for (s32 i = 0; i < *npredictors; i++) {
+ s32 **table_entry = (*table)[i];
+ for (s32 j = 0; j < *order; j++) {
+ for (s32 k = 0; k < 8; k++) {
+ s16 ts;
+ checked_fread(&ts, sizeof(s16), 1, fhandle);
+ BSWAP16(ts);
+ table_entry[k][j] = ts;
+ }
+ }
+
+ for (s32 k = 1; k < 8; k++) {
+ table_entry[k][*order] = table_entry[k - 1][*order - 1];
+ }
+
+ table_entry[0][*order] = 1 << 11;
+
+ for (s32 k = 1; k < 8; k++) {
+ s32 j = 0;
+ for (; j < k; j++) {
+ table_entry[j][k + *order] = 0;
+ }
+
+ for (; j < 8; j++) {
+ table_entry[j][k + *order] = table_entry[j - k][*order];
+ }
+ }
+ }
+ return 0;
+}
+
+ALADPCMloop *readlooppoints(LUS::BinaryReader& reader, s16 *nloops) {
+ BSWAP16(*nloops);
+ checked_fread(nloops, sizeof(s16), 1, reader);
+ auto *al = static_cast<ALADPCMloop *>(malloc(*nloops * sizeof(ALADPCMloop)));
+ for (s32 i = 0; i < *nloops; i++) {
+ checked_fread(&al[i], sizeof(ALADPCMloop), 1, reader);
+ BSWAP32(al[i].start);
+ BSWAP32(al[i].end);
+ BSWAP32(al[i].count);
+ BSWAP16_MANY(al[i].state, 16);
+ }
+ return al;
+}
+
+s32 inner_product(s32 length, s32 *v1, s32 *v2)
+{
+ s32 out = 0;
+ for (s32 i = 0; i < length; i++) {
+ out += v1[i] * v2[i];
+ }
+
+ // Compute "out / 2^11", rounded down.
+ s32 dout = out / (1 << 11);
+ s32 fiout = dout * (1 << 11);
+ return dout - (out - fiout < 0);
+}
+
+void my_decodeframe(u8 *frame, s32 *state, s32 order, s32 ***coefTable)
+{
+ s32 ix[16];
+
+ u8 header = frame[0];
+ s32 scale = 1 << (header >> 4);
+ s32 optimalp = header & 0xf;
+
+ for (s32 i = 0; i < 16; i += 2) {
+ u8 c = frame[1 + i/2];
+ ix[i] = c >> 4;
+ ix[i + 1] = c & 0xf;
+ }
+
+ for (s32 i = 0; i < 16; i++) {
+ if (ix[i] >= 8) ix[i] -= 16;
+ ix[i] *= scale;
+ }
+
+ for (s32 j = 0; j < 2; j++) {
+ s32 in_vec[16];
+ if (j == 0) {
+ for (s32 i = 0; i < order; i++) {
+ in_vec[i] = state[16 - order + i];
+ }
+ } else {
+ for (s32 i = 0; i < order; i++) {
+ in_vec[i] = state[8 - order + i];
+ }
+ }
+
+ for (s32 i = 0; i < 8; i++) {
+ s32 ind = j * 8 + i;
+ in_vec[order + i] = ix[ind];
+ state[ind] = inner_product(order + i, coefTable[optimalp][i], in_vec) + ix[ind];
+ }
+ }
+}
+
+void my_encodeframe(u8 *out, s16 *inBuffer, s32 *state, s32 ***coefTable, s32 order, s32 npredictors)
+{
+ s16 ix[16];
+ s32 prediction[16];
+ s32 inVector[16];
+ s32 saveState[16];
+ s32 optimalp = 0;
+ s32 scale;
+ s32 ie[16];
+ s32 e[16];
+ f32 min = 1e30;
+
+ for (s32 k = 0; k < npredictors; k++) {
+ for (s32 j = 0; j < 2; j++) {
+ for (s32 i = 0; i < order; i++) {
+ inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]);
+ }
+
+ for (s32 i = 0; i < 8; i++) {
+ prediction[j * 8 + i] = inner_product(order + i, coefTable[k][i], inVector);
+ e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i];
+ }
+ }
+
+ f32 se = 0.0f;
+ for (s32 j = 0; j < 16; j++) {
+ se += (f32) e[j] * (f32) e[j];
+ }
+
+ if (se < min) {
+ min = se;
+ optimalp = k;
+ }
+ }
+
+ for (s32 j = 0; j < 2; j++) {
+ for (s32 i = 0; i < order; i++) {
+ inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]);
+ }
+
+ for (s32 i = 0; i < 8; i++) {
+ prediction[j * 8 + i] = inner_product(order + i, coefTable[optimalp][i], inVector);
+ e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i];
+ }
+ }
+
+ for (s32 i = 0; i < 16; i++) {
+ ie[i] = clamp_to_s16(e[i]);
+ }
+
+ s32 max = 0;
+ for (s32 i = 0; i < 16; i++) {
+ if (abs(ie[i]) > abs(max)) {
+ max = ie[i];
+ }
+ }
+
+ for (scale = 0; scale <= 12; scale++) {
+ if (max <= 7 && max >= -8) break;
+ max /= 2;
+ }
+
+ for (s32 i = 0; i < 16; i++) {
+ saveState[i] = state[i];
+ }
+
+ for (s32 nIter = 0, again = 1; nIter < 2 && again; nIter++) {
+ again = 0;
+ if (nIter == 1) scale++;
+ if (scale > 12) {
+ scale = 12;
+ }
+
+ for (s32 j = 0; j < 2; j++) {
+ s32 base = j * 8;
+ for (s32 i = 0; i < order; i++) {
+ inVector[i] = (j == 0 ?
+ saveState[16 - order + i] : state[8 - order + i]);
+ }
+
+ for (s32 i = 0; i < 8; i++) {
+ prediction[base + i] = inner_product(order + i, coefTable[optimalp][i], inVector);
+ s32 se = inBuffer[base + i] - prediction[base + i];
+ ix[base + i] = qsample(se, scale);
+ s32 cV = clamp_to_s16(ix[base + i]) - ix[base + i];
+ if (cV > 1 || cV < -1) again = 1;
+ ix[base + i] += cV;
+ inVector[i + order] = ix[base + i] * (1 << scale);
+ state[base + i] = prediction[base + i] + inVector[i + order];
+ }
+ }
+ }
+
+ u8 header = (scale << 4) | (optimalp & 0xf);
+ out[0] = header;
+ for (s32 i = 0; i < 16; i += 2) {
+ u8 c = ((ix[i] & 0xf) << 4) | (ix[i + 1] & 0xf);
+ out[1 + i/2] = c;
+ }
+}
+
+void permute(s16 *out, s32 *in, s32 scale)
+{
+ for (s32 i = 0; i < 16; i++) {
+ out[i] = clamp_to_s16(in[i] - scale / 2 + myrand() % (scale + 1));
+ }
+}
+
+void write_header(LUS::BinaryWriter& writer, const char *id, s32 size) {
+ writer.Write((char*) id, (size_t) 4);
+ BSWAP32(size);
+ writer.Write(size);
+}
+
+void write_aiff(std::vector<char> data, LUS::BinaryWriter& writer) {
+ s16 order = -1;
+ s16 nloops = 0;
+ ALADPCMloop *aloops = nullptr;
+ s16 npredictors = -1;
+ s32 ***coefTable = nullptr;
+ s32 state[16];
+ s32 soundPointer = -1;
+ s32 currPos = 0;
+ s32 nSamples = 0;
+ Chunk FormChunk;
+ ChunkHeader Header;
+ CommonChunk CommChunk;
+ InstrumentChunk InstChunk;
+ SoundDataChunk SndDChunk;
+
+ memset(&InstChunk, 0, sizeof(InstChunk));
+ LUS::BinaryReader reader((char*)data.data(), data.size());
+
+ checked_fread(&FormChunk, sizeof(FormChunk), 1, reader);
+ BSWAP32(FormChunk.ckID);
+ BSWAP32(FormChunk.formType);
+ if ((FormChunk.ckID != 0x464f524d) || (FormChunk.formType != 0x41494643)) { // FORM, AIFC
+ fail_parse("not an AIFF-C file");
+ }
+
+ for (;;) {
+ if (reader.GetBaseAddress() >= reader.GetLength()) {
+ break;
+ }
+ reader.Read((char*) &Header, sizeof(Header));
+ u32 ts;
+ BSWAP32(Header.ckID);
+ BSWAP32(Header.ckSize);
+
+ Header.ckSize++;
+ Header.ckSize &= ~1;
+ s32 offset = reader.GetBaseAddress();
+
+ if(Header.ckID == 0x434f4d4d) { // COMM
+ checked_fread(&CommChunk, sizeof(CommChunk), 1, reader);
+ BSWAP16(CommChunk.numChannels);
+ BSWAP16(CommChunk.numFramesH);
+ BSWAP16(CommChunk.numFramesL);
+ BSWAP16(CommChunk.sampleSize);
+ BSWAP16(CommChunk.compressionTypeH);
+ BSWAP16(CommChunk.compressionTypeL);
+ s32 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL;
+ if (cType != 0x56415043) { // VAPC
+ fail_parse("file is of the wrong compression type");
+ }
+ if (CommChunk.numChannels != 1) {
+ fail_parse("file contains %d channels, only 1 channel supported", CommChunk.numChannels);
+ }
+ if (CommChunk.sampleSize != 16) {
+ fail_parse("file contains %d bit samples, only 16 bit samples supported", CommChunk.sampleSize);
+ }
+
+ nSamples = (CommChunk.numFramesH << 16) + CommChunk.numFramesL;
+
+ // Allow broken input lengths
+ if (nSamples % 16) {
+ nSamples--;
+ }
+
+ if (nSamples % 16 != 0) {
+ fail_parse("number of chunks must be a multiple of 16, found %d", nSamples);
+ }
+ }
+
+ if(Header.ckID == 0x53534e44) { //SSND
+ checked_fread(&SndDChunk, sizeof(SndDChunk), 1, reader);
+ BSWAP32(SndDChunk.offset);
+ BSWAP32(SndDChunk.blockSize);
+ assert(SndDChunk.offset == 0);
+ assert(SndDChunk.blockSize == 0);
+ soundPointer = reader.GetBaseAddress();
+ }
+
+ if(Header.ckID == 0x4150504c){ // APPL
+ checked_fread(&ts, sizeof(u32), 1, reader);
+ BSWAP32(ts);
+ if (ts == 0x73746f63) { // stoc
+ u8 len;
+ checked_fread(&len, 1, 1, reader);
+ if (len == 11) {
+ char ChunkName[12];
+ s16 version;
+ checked_fread(ChunkName, 11, 1, reader);
+ ChunkName[11] = '\0';
+ if (strcmp("VADPCMCODES", ChunkName) == 0) {
+ checked_fread(&version, sizeof(s16), 1, reader);
+ BSWAP16(version);
+ if (version != 1) {
+ fail_parse("Unknown codebook chunk version");
+ }
+ readaifccodebook(reader, &coefTable, &order, &npredictors);
+ }
+ else if (strcmp("VADPCMLOOPS", ChunkName) == 0) {
+ checked_fread(&version, sizeof(s16), 1, reader);
+ BSWAP16(version);
+ if (version != 1) {
+ fail_parse("Unknown loop chunk version");
+ }
+ aloops = readlooppoints(reader, &nloops);
+ BSWAP16(nloops);
+ if (nloops != 1) {
+ fail_parse("Only a single loop supported");
+ }
+ }
+ }
+ }
+ }
+
+ reader.Seek(offset + Header.ckSize, LUS::SeekOffsetType::Start);
+ }
+
+ if (coefTable == nullptr) {
+ fail_parse("Codebook missing from bitstream");
+ }
+
+ for (s32 i = 0; i < order; i++) {
+ state[15 - i] = 0;
+ }
+
+ u32 outputBytes = nSamples * sizeof(s16);
+
+ reader.Seek(soundPointer, LUS::SeekOffsetType::Start);
+
+ while (currPos < nSamples) {
+ u8 input[9];
+ u8 encoded[9];
+ s32 lastState[16];
+ s32 decoded[16];
+ s16 guess[16];
+ s16 origGuess[16];
+
+ memcpy(lastState, state, sizeof(lastState));
+ checked_fread(input, 9, 1, reader);
+
+ // Decode for real
+ my_decodeframe(input, state, order, coefTable);
+ memcpy(decoded, state, sizeof(lastState));
+
+ // Create a guess from that, by clamping to 16 bits
+ for (s32 i = 0; i < 16; i++) {
+ origGuess[i] = clamp_to_s16(state[i]);
+ }
+
+ // Encode the guess
+ memcpy(state, lastState, sizeof(lastState));
+ memcpy(guess, origGuess, sizeof(guess));
+ my_encodeframe(encoded, guess, state, coefTable, order, npredictors);
+
+ // If it doesn't match, randomly round numbers until it does.
+ if (memcmp(input, encoded, 9) != 0) {
+ s32 scale = 1 << (input[0] >> 4);
+ do {
+ permute(guess, decoded, scale);
+ memcpy(state, lastState, sizeof(lastState));
+ my_encodeframe(encoded, guess, state, coefTable, order, npredictors);
+ } while (memcmp(input, encoded, 9) != 0);
+
+ // Bring the matching closer to the original decode (not strictly
+ // necessary, but it will move us closer to the target on average).
+ for (s32 failures = 0; failures < 50; failures++) {
+ s32 ind = myrand() % 16;
+ s32 old = guess[ind];
+ if (old == origGuess[ind]) continue;
+ guess[ind] = origGuess[ind];
+ if (myrand() % 2) guess[ind] += (old - origGuess[ind]) / 2;
+ memcpy(state, lastState, sizeof(lastState));
+ my_encodeframe(encoded, guess, state, coefTable, order, npredictors);
+ if (memcmp(input, encoded, 9) == 0) {
+ failures = -1;
+ }
+ else {
+ guess[ind] = old;
+ }
+ }
+ }
+
+ memcpy(state, decoded, sizeof(lastState));
+ BSWAP16_MANY(guess, 16);
+ writer.Seek(currPos * 2, LUS::SeekOffsetType::Start);
+ writer.Write((char*) guess, sizeof(guess));
+ currPos += 16;
+ }
+
+ // Write an incomplete file header. We'll fill in the size later.
+ writer.Seek(0, LUS::SeekOffsetType::Start);
+ writer.Write((char*) "FORM\0\0\0\0AIFF", 12);
+
+ // Subtract 4 from the COMM size to skip the compression field.
+ write_header(writer, "COMM", sizeof(CommonChunk) - 4);
+ CommChunk.numFramesH = nSamples >> 16;
+ CommChunk.numFramesL = nSamples & 0xffff;
+ BSWAP16(CommChunk.numChannels);
+ BSWAP16(CommChunk.numFramesH);
+ BSWAP16(CommChunk.numFramesL);
+ BSWAP16(CommChunk.sampleSize);
+ writer.Write((char*) &CommChunk, sizeof(CommonChunk) - 4);
+
+ if (nloops > 0) {
+ s32 startPos = aloops[0].start, endPos = aloops[0].end;
+ const char *markerNames[2] = {"start", "end"};
+ Marker markers[2] = {
+ {1, static_cast<u16>((u16) startPos >> 16), static_cast<u16>((u16) startPos & 0xffff)},
+ {2, static_cast<u16>(endPos >> 16), static_cast<u16>(endPos & 0xffff)}
+ };
+ write_header(writer, "MARK", 2 + 2 * sizeof(Marker) + 1 + 5 + 1 + 3);
+ s16 numMarkers = bswap16(2);
+ writer.Write(numMarkers);
+ for (s32 i = 0; i < 2; i++) {
+ u8 len = (u8) strlen(markerNames[i]);
+ BSWAP16(markers[i].MarkerID);
+ BSWAP16(markers[i].positionH);
+ BSWAP16(markers[i].positionL);
+ writer.Write(markers[i].MarkerID);
+ writer.Write(markers[i].positionH);
+ writer.Write(markers[i].positionL);
+ writer.Write(len);
+ writer.Write((char*) markerNames[i], len);
+ }
+
+ write_header(writer, "INST", sizeof(InstrumentChunk));
+ InstChunk.sustainLoop.playMode = bswap16(1);
+ InstChunk.sustainLoop.beginLoop = bswap16(1);
+ InstChunk.sustainLoop.endLoop = bswap16(2);
+ InstChunk.releaseLoop.playMode = 0;
+ InstChunk.releaseLoop.beginLoop = 0;
+ InstChunk.releaseLoop.endLoop = 0;
+ writer.Write((char*) &InstChunk, sizeof(InstrumentChunk));
+ }
+
+ // Save the coefficient table for use when encoding. Ideally this wouldn't
+ // be needed and "tabledesign -s 1" would generate the right table, but in
+ // practice it's difficult to adjust samples to make that happen.
+ write_header(writer, "APPL", 4 + 12 + sizeof(CodeChunk) + npredictors * order * 8 * 2);
+ writer.Write("stoc", false);
+ CodeChunk cChunk;
+ cChunk.version = bswap16(1);
+ cChunk.order = bswap16(order);
+ cChunk.nEntries = bswap16(npredictors);
+ writer.Write("\x0bVADPCMCODES", false);
+ writer.Write((char*) &cChunk, sizeof(CodeChunk));
+ for (s32 i = 0; i < npredictors; i++) {
+ for (s32 j = 0; j < order; j++) {
+ for (s32 k = 0; k < 8; k++) {
+ s16 ts = bswap16(coefTable[i][k][j]);
+ writer.Write(ts);
+ }
+ }
+ }
+
+ write_header(writer, "SSND", outputBytes + 8);
+ SndDChunk.offset = 0;
+ SndDChunk.blockSize = 0;
+ writer.Write((char*) &SndDChunk, sizeof(SoundDataChunk));
+
+ // Fix the size in the header
+ s32 fileSize = bswap32(writer.GetLength() - 8);
+ writer.Seek(4, LUS::SeekOffsetType::Start);
+ writer.Write(fileSize);
+
+ reader.Close();
+}
diff --git a/src/audio/AIFCDecode.h b/src/audio/AIFCDecode.h
new file mode 100644
index 0000000..e141a80
--- /dev/null
+++ b/src/audio/AIFCDecode.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <vector>
+#include "BinaryWriter.h"
+
+void write_aiff(std::vector<char> data, LUS::BinaryWriter& writer); \ No newline at end of file
diff --git a/src/audio/AIFCWriter.cpp b/src/audio/AIFCWriter.cpp
deleted file mode 100644
index 1df5813..0000000
--- a/src/audio/AIFCWriter.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "AIFCWriter.h"
-
-#include "hj/pyutils.h"
-
-#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al)
-
-std::vector<char> AIFCWriter::pack(const std::string& str) {
- std::vector<char> result;
- result.insert(result.end(), str.begin(), str.end());
- return result;
-}
-
-std::vector<char> AIFCWriter::pstring(const std::vector<char>& data) {
- std::vector<char> result;
- result.insert(result.end(), data.begin(), data.end());
- if (data.size() % 2 == 0) {
- result.push_back('\0');
- }
- return result;
-}
-
-void AIFCWriter::add_section(AIFC::MagicValues magic, const std::vector<char>& data) {
- sections.emplace_back(magic, data);
- total_size += ALIGN(data.size(), 2) + 8;
-}
-
-void AIFCWriter::add_custom_section(const std::string& section, const std::vector<char>& data) {
- this->add_section(AIFC::MagicValues::AAPL, pack("stoc") + pstring(pack(section)) + data);
-}
-
-void AIFCWriter::finish() {
- // total_size isn't used and is regularly wrong.
- // In particular, vadpcm_enc preserves the size of the input file...
- total_size += 4;
-
- out.Write(BSWAP32(AIFC::MagicValues::FORM));
- out.Write((uint32_t) BSWAP32(total_size));
- out.Write(BSWAP32(AIFC::MagicValues::AIFC));
-
- for (const auto& section : sections) {
- out.Write((uint32_t) BSWAP32(section.first));
- out.Write((uint32_t) BSWAP32(section.second.size()));
- out.Write((char*) section.second.data(), section.second.size());
- if(section.second.size() % 2) {
- out.Write('\0');
- }
- }
-}
diff --git a/src/audio/AIFCWriter.h b/src/audio/AIFCWriter.h
deleted file mode 100644
index 9ea7843..0000000
--- a/src/audio/AIFCWriter.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <iostream>
-#include <vector>
-#include <string>
-#include <cstdint>
-#include "binarytools/BinaryWriter.h"
-
-namespace AIFC {
- enum MagicValues {
- FORM = 0x464f524d,
- AIFC = 0x41494643,
- COMM = 0x434f4d4d,
- INST = 0x494e5354,
- VAPC = 0x56415043,
- SSND = 0x53534e44,
- AAPL = 0x4150504c,
- stoc = 0x73746f63,
- };
-}
-
-class AIFCWriter {
-public:
- AIFCWriter(LUS::BinaryWriter& out) : out(out), total_size(0) {}
-
- static std::vector<char> pack(const std::string& str);
- static std::vector<char> pstring(const std::vector<char>& data);
- void add_section(AIFC::MagicValues, const std::vector<char>& data);
- void add_custom_section(const std::string& section, const std::vector<char>& data);
- void finish();
-
-private:
- LUS::BinaryWriter& out;
- std::vector<std::pair<AIFC::MagicValues, std::vector<char>>> sections;
- std::size_t total_size;
-}; \ No newline at end of file
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index f241a9b..4549658 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -8,7 +8,6 @@
#include "hj/pyutils.h"
#include "binarytools/BinaryReader.h"
#include "hj/zip.h"
-#include "AIFCWriter.h"
#include <nlohmann/json.hpp>
std::unordered_map<std::string, uint32_t> name_table;
@@ -435,7 +434,6 @@ void AudioManager::initialize(std::vector<uint8_t>& buffer) {
auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, this->loaded_tbl.tbls);
-// std::cout << "================================" << std::endl;
for (const auto& item : zipped) {
auto [index, ctrl, sample_bank_name] = item;
auto sample_bank = this->loaded_tbl.map[sample_bank_name];
@@ -444,8 +442,6 @@ void AudioManager::initialize(std::vector<uint8_t>& buffer) {
auto header = parse_ctl_header(headerRaw);
auto bank = parse_ctl(header, PyUtils::slice(entry, 16), sample_bank, index);
banks.push_back(bank);
-// bank.print();
-// std::cout << "================================" << std::endl;
}
diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h
index 6a1c559..4c3265c 100644
--- a/src/audio/AudioManager.h
+++ b/src/audio/AudioManager.h
@@ -9,6 +9,19 @@
#define NONE 0xFFFF
#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al)
+namespace AIFC {
+ enum MagicValues {
+ FORM = 0x464f524d,
+ AIFC = 0x41494643,
+ COMM = 0x434f4d4d,
+ INST = 0x494e5354,
+ VAPC = 0x56415043,
+ SSND = 0x53534e44,
+ AAPL = 0x4150504c,
+ stoc = 0x73746f63,
+ };
+}
+
struct Entry {
uint32_t offset;
uint32_t length;
diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp
index 64c940e..d1d2111 100644
--- a/src/factories/AudioFactory.cpp
+++ b/src/factories/AudioFactory.cpp
@@ -2,6 +2,7 @@
#include <vector>
#include "audio/AudioManager.h"
+#include "audio/AIFCDecode.h"
#include "binarytools/BinaryReader.h"
bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) {
@@ -25,6 +26,12 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std:
return false;
}
- AudioManager::Instance->create_aifc(metadata[1]["us"][1], *writer);
+ LUS::BinaryWriter aifcWriter;
+ AudioManager::Instance->create_aifc(metadata[1]["us"][1], aifcWriter);
+ std::vector<char> aifcData = aifcWriter.ToVector();
+ aifcWriter.Close();
+
+ write_aiff(aifcData, *writer);
+
return true;
}