Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/tools/aifc_decode.c
7854 views
1
/**
2
* Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way
3
* that roundtrips with vadpcm_enc.
4
*/
5
#include <unistd.h>
6
#include <assert.h>
7
#include <math.h>
8
#include <string.h>
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <stdarg.h>
12
13
typedef signed char s8;
14
typedef short s16;
15
typedef int s32;
16
typedef unsigned char u8;
17
typedef unsigned short u16;
18
typedef unsigned int u32;
19
typedef unsigned long long u64;
20
typedef float f32;
21
22
#define bswap16(x) __builtin_bswap16(x)
23
#define bswap32(x) __builtin_bswap32(x)
24
#define BSWAP16(x) x = __builtin_bswap16(x)
25
#define BSWAP32(x) x = __builtin_bswap32(x)
26
#define BSWAP16_MANY(x, n) for (s32 _i = 0; _i < n; _i++) BSWAP16((x)[_i])
27
28
#define NORETURN __attribute__((noreturn))
29
#define UNUSED __attribute__((unused))
30
31
typedef struct {
32
u32 ckID;
33
u32 ckSize;
34
} ChunkHeader;
35
36
typedef struct {
37
u32 ckID;
38
u32 ckSize;
39
u32 formType;
40
} Chunk;
41
42
typedef struct {
43
s16 numChannels;
44
u16 numFramesH;
45
u16 numFramesL;
46
s16 sampleSize;
47
s16 sampleRate[5]; // 80-bit float
48
u16 compressionTypeH;
49
u16 compressionTypeL;
50
} CommonChunk;
51
52
typedef struct {
53
s16 MarkerID;
54
u16 positionH;
55
u16 positionL;
56
} Marker;
57
58
typedef struct {
59
s16 playMode;
60
s16 beginLoop;
61
s16 endLoop;
62
} Loop;
63
64
typedef struct {
65
s8 baseNote;
66
s8 detune;
67
s8 lowNote;
68
s8 highNote;
69
s8 lowVelocity;
70
s8 highVelocity;
71
s16 gain;
72
Loop sustainLoop;
73
Loop releaseLoop;
74
} InstrumentChunk;
75
76
typedef struct {
77
s32 offset;
78
s32 blockSize;
79
} SoundDataChunk;
80
81
typedef struct {
82
s16 version;
83
s16 order;
84
s16 nEntries;
85
} CodeChunk;
86
87
typedef struct
88
{
89
u32 start;
90
u32 end;
91
u32 count;
92
s16 state[16];
93
} ALADPCMloop;
94
95
96
static char usage[] = "input.aifc output.aiff";
97
static const char *progname, *infilename;
98
99
#define checked_fread(a, b, c, d) if (fread(a, b, c, d) != c) fail_parse("error parsing file")
100
101
NORETURN
102
void fail_parse(const char *fmt, ...)
103
{
104
char *formatted = NULL;
105
va_list ap;
106
va_start(ap, fmt);
107
int size = vsnprintf(NULL, 0, fmt, ap);
108
va_end(ap);
109
if (size >= 0) {
110
size++;
111
formatted = malloc(size);
112
if (formatted != NULL) {
113
va_start(ap, fmt);
114
size = vsnprintf(formatted, size, fmt, ap);
115
va_end(ap);
116
if (size < 0) {
117
free(formatted);
118
formatted = NULL;
119
}
120
}
121
}
122
123
if (formatted != NULL) {
124
fprintf(stderr, "%s: %s [%s]\n", progname, formatted, infilename);
125
free(formatted);
126
}
127
exit(1);
128
}
129
130
s32 myrand()
131
{
132
static u64 state = 1619236481962341ULL;
133
state *= 3123692312231ULL;
134
state++;
135
return state >> 33;
136
}
137
138
s16 qsample(s32 x, s32 scale)
139
{
140
// Compute x / 2^scale rounded to the nearest integer, breaking ties towards zero.
141
if (scale == 0) return x;
142
return (x + (1 << (scale - 1)) - (x > 0)) >> scale;
143
}
144
145
s16 clamp_to_s16(s32 x)
146
{
147
if (x < -0x8000) return -0x8000;
148
if (x > 0x7fff) return 0x7fff;
149
return (s16) x;
150
}
151
152
s32 toi4(s32 x)
153
{
154
if (x >= 8) return x - 16;
155
return x;
156
}
157
158
s32 readaifccodebook(FILE *fhandle, s32 ****table, s16 *order, s16 *npredictors)
159
{
160
checked_fread(order, sizeof(s16), 1, fhandle);
161
BSWAP16(*order);
162
checked_fread(npredictors, sizeof(s16), 1, fhandle);
163
BSWAP16(*npredictors);
164
*table = malloc(*npredictors * sizeof(s32 **));
165
for (s32 i = 0; i < *npredictors; i++) {
166
(*table)[i] = malloc(8 * sizeof(s32 *));
167
for (s32 j = 0; j < 8; j++) {
168
(*table)[i][j] = malloc((*order + 8) * sizeof(s32));
169
}
170
}
171
172
for (s32 i = 0; i < *npredictors; i++) {
173
s32 **table_entry = (*table)[i];
174
for (s32 j = 0; j < *order; j++) {
175
for (s32 k = 0; k < 8; k++) {
176
s16 ts;
177
checked_fread(&ts, sizeof(s16), 1, fhandle);
178
BSWAP16(ts);
179
table_entry[k][j] = ts;
180
}
181
}
182
183
for (s32 k = 1; k < 8; k++) {
184
table_entry[k][*order] = table_entry[k - 1][*order - 1];
185
}
186
187
table_entry[0][*order] = 1 << 11;
188
189
for (s32 k = 1; k < 8; k++) {
190
s32 j = 0;
191
for (; j < k; j++) {
192
table_entry[j][k + *order] = 0;
193
}
194
195
for (; j < 8; j++) {
196
table_entry[j][k + *order] = table_entry[j - k][*order];
197
}
198
}
199
}
200
return 0;
201
}
202
203
ALADPCMloop *readlooppoints(FILE *ifile, s16 *nloops)
204
{
205
checked_fread(nloops, sizeof(s16), 1, ifile);
206
BSWAP16(*nloops);
207
ALADPCMloop *al = malloc(*nloops * sizeof(ALADPCMloop));
208
for (s32 i = 0; i < *nloops; i++) {
209
checked_fread(&al[i], sizeof(ALADPCMloop), 1, ifile);
210
BSWAP32(al[i].start);
211
BSWAP32(al[i].end);
212
BSWAP32(al[i].count);
213
BSWAP16_MANY(al[i].state, 16);
214
}
215
return al;
216
}
217
218
s32 inner_product(s32 length, s32 *v1, s32 *v2)
219
{
220
s32 out = 0;
221
for (s32 i = 0; i < length; i++) {
222
out += v1[i] * v2[i];
223
}
224
225
// Compute "out / 2^11", rounded down.
226
s32 dout = out / (1 << 11);
227
s32 fiout = dout * (1 << 11);
228
return dout - (out - fiout < 0);
229
}
230
231
void my_decodeframe(u8 *frame, s32 *state, s32 order, s32 ***coefTable)
232
{
233
s32 ix[16];
234
235
u8 header = frame[0];
236
s32 scale = 1 << (header >> 4);
237
s32 optimalp = header & 0xf;
238
239
for (s32 i = 0; i < 16; i += 2) {
240
u8 c = frame[1 + i/2];
241
ix[i] = c >> 4;
242
ix[i + 1] = c & 0xf;
243
}
244
245
for (s32 i = 0; i < 16; i++) {
246
if (ix[i] >= 8) ix[i] -= 16;
247
ix[i] *= scale;
248
}
249
250
for (s32 j = 0; j < 2; j++) {
251
s32 in_vec[16];
252
if (j == 0) {
253
for (s32 i = 0; i < order; i++) {
254
in_vec[i] = state[16 - order + i];
255
}
256
} else {
257
for (s32 i = 0; i < order; i++) {
258
in_vec[i] = state[8 - order + i];
259
}
260
}
261
262
for (s32 i = 0; i < 8; i++) {
263
s32 ind = j * 8 + i;
264
in_vec[order + i] = ix[ind];
265
state[ind] = inner_product(order + i, coefTable[optimalp][i], in_vec) + ix[ind];
266
}
267
}
268
}
269
270
void my_encodeframe(u8 *out, s16 *inBuffer, s32 *state, s32 ***coefTable, s32 order, s32 npredictors)
271
{
272
s16 ix[16];
273
s32 prediction[16];
274
s32 inVector[16];
275
s32 saveState[16];
276
s32 optimalp = 0;
277
s32 scale;
278
s32 ie[16];
279
s32 e[16];
280
f32 min = 1e30;
281
282
for (s32 k = 0; k < npredictors; k++) {
283
for (s32 j = 0; j < 2; j++) {
284
for (s32 i = 0; i < order; i++) {
285
inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]);
286
}
287
288
for (s32 i = 0; i < 8; i++) {
289
prediction[j * 8 + i] = inner_product(order + i, coefTable[k][i], inVector);
290
e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i];
291
}
292
}
293
294
f32 se = 0.0f;
295
for (s32 j = 0; j < 16; j++) {
296
se += (f32) e[j] * (f32) e[j];
297
}
298
299
if (se < min) {
300
min = se;
301
optimalp = k;
302
}
303
}
304
305
for (s32 j = 0; j < 2; j++) {
306
for (s32 i = 0; i < order; i++) {
307
inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]);
308
}
309
310
for (s32 i = 0; i < 8; i++) {
311
prediction[j * 8 + i] = inner_product(order + i, coefTable[optimalp][i], inVector);
312
e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i];
313
}
314
}
315
316
for (s32 i = 0; i < 16; i++) {
317
ie[i] = clamp_to_s16(e[i]);
318
}
319
320
s32 max = 0;
321
for (s32 i = 0; i < 16; i++) {
322
if (abs(ie[i]) > abs(max)) {
323
max = ie[i];
324
}
325
}
326
327
for (scale = 0; scale <= 12; scale++) {
328
if (max <= 7 && max >= -8) break;
329
max /= 2;
330
}
331
332
for (s32 i = 0; i < 16; i++) {
333
saveState[i] = state[i];
334
}
335
336
for (s32 nIter = 0, again = 1; nIter < 2 && again; nIter++) {
337
again = 0;
338
if (nIter == 1) scale++;
339
if (scale > 12) {
340
scale = 12;
341
}
342
343
for (s32 j = 0; j < 2; j++) {
344
s32 base = j * 8;
345
for (s32 i = 0; i < order; i++) {
346
inVector[i] = (j == 0 ?
347
saveState[16 - order + i] : state[8 - order + i]);
348
}
349
350
for (s32 i = 0; i < 8; i++) {
351
prediction[base + i] = inner_product(order + i, coefTable[optimalp][i], inVector);
352
s32 se = inBuffer[base + i] - prediction[base + i];
353
ix[base + i] = qsample(se, scale);
354
s32 cV = clamp_to_s16(ix[base + i]) - ix[base + i];
355
if (cV > 1 || cV < -1) again = 1;
356
ix[base + i] += cV;
357
inVector[i + order] = ix[base + i] * (1 << scale);
358
state[base + i] = prediction[base + i] + inVector[i + order];
359
}
360
}
361
}
362
363
u8 header = (scale << 4) | (optimalp & 0xf);
364
out[0] = header;
365
for (s32 i = 0; i < 16; i += 2) {
366
u8 c = ((ix[i] & 0xf) << 4) | (ix[i + 1] & 0xf);
367
out[1 + i/2] = c;
368
}
369
}
370
371
void permute(s16 *out, s32 *in, s32 scale)
372
{
373
for (s32 i = 0; i < 16; i++) {
374
out[i] = clamp_to_s16(in[i] - scale / 2 + myrand() % (scale + 1));
375
}
376
}
377
378
void write_header(FILE *ofile, const char *id, s32 size)
379
{
380
fwrite(id, 4, 1, ofile);
381
BSWAP32(size);
382
fwrite(&size, sizeof(s32), 1, ofile);
383
}
384
385
int main(int argc, char **argv)
386
{
387
s16 order = -1;
388
s16 nloops = 0;
389
ALADPCMloop *aloops = NULL;
390
s16 npredictors = -1;
391
s32 ***coefTable = NULL;
392
s32 state[16];
393
s32 soundPointer = -1;
394
s32 currPos = 0;
395
s32 nSamples = 0;
396
Chunk FormChunk;
397
ChunkHeader Header;
398
CommonChunk CommChunk;
399
InstrumentChunk InstChunk;
400
SoundDataChunk SndDChunk;
401
FILE *ifile;
402
FILE *ofile;
403
progname = argv[0];
404
405
if (argc < 3) {
406
fprintf(stderr, "%s %s\n", progname, usage);
407
exit(1);
408
}
409
410
infilename = argv[1];
411
412
if ((ifile = fopen(infilename, "rb")) == NULL) {
413
fail_parse("AIFF-C file could not be opened");
414
exit(1);
415
}
416
417
if ((ofile = fopen(argv[2], "wb")) == NULL) {
418
fprintf(stderr, "%s: output file could not be opened [%s]\n", progname, argv[2]);
419
exit(1);
420
}
421
422
memset(&InstChunk, 0, sizeof(InstChunk));
423
424
checked_fread(&FormChunk, sizeof(FormChunk), 1, ifile);
425
BSWAP32(FormChunk.ckID);
426
BSWAP32(FormChunk.formType);
427
if ((FormChunk.ckID != 0x464f524d) || (FormChunk.formType != 0x41494643)) { // FORM, AIFC
428
fail_parse("not an AIFF-C file");
429
}
430
431
for (;;) {
432
s32 num = fread(&Header, sizeof(Header), 1, ifile);
433
u32 ts;
434
if (num <= 0) break;
435
BSWAP32(Header.ckID);
436
BSWAP32(Header.ckSize);
437
438
Header.ckSize++;
439
Header.ckSize &= ~1;
440
s32 offset = ftell(ifile);
441
442
switch (Header.ckID) {
443
case 0x434f4d4d: // COMM
444
checked_fread(&CommChunk, sizeof(CommChunk), 1, ifile);
445
BSWAP16(CommChunk.numChannels);
446
BSWAP16(CommChunk.numFramesH);
447
BSWAP16(CommChunk.numFramesL);
448
BSWAP16(CommChunk.sampleSize);
449
BSWAP16(CommChunk.compressionTypeH);
450
BSWAP16(CommChunk.compressionTypeL);
451
s32 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL;
452
if (cType != 0x56415043) { // VAPC
453
fail_parse("file is of the wrong compression type");
454
}
455
if (CommChunk.numChannels != 1) {
456
fail_parse("file contains %d channels, only 1 channel supported", CommChunk.numChannels);
457
}
458
if (CommChunk.sampleSize != 16) {
459
fail_parse("file contains %d bit samples, only 16 bit samples supported", CommChunk.sampleSize);
460
}
461
462
nSamples = (CommChunk.numFramesH << 16) + CommChunk.numFramesL;
463
464
// Allow broken input lengths
465
if (nSamples % 16) {
466
nSamples--;
467
}
468
469
if (nSamples % 16 != 0) {
470
fail_parse("number of chunks must be a multiple of 16, found %d", nSamples);
471
}
472
break;
473
474
case 0x53534e44: // SSND
475
checked_fread(&SndDChunk, sizeof(SndDChunk), 1, ifile);
476
BSWAP32(SndDChunk.offset);
477
BSWAP32(SndDChunk.blockSize);
478
assert(SndDChunk.offset == 0);
479
assert(SndDChunk.blockSize == 0);
480
soundPointer = ftell(ifile);
481
break;
482
483
case 0x4150504c: // APPL
484
checked_fread(&ts, sizeof(u32), 1, ifile);
485
BSWAP32(ts);
486
if (ts == 0x73746f63) { // stoc
487
u8 len;
488
checked_fread(&len, 1, 1, ifile);
489
if (len == 11) {
490
char ChunkName[12];
491
s16 version;
492
checked_fread(ChunkName, 11, 1, ifile);
493
ChunkName[11] = '\0';
494
if (strcmp("VADPCMCODES", ChunkName) == 0) {
495
checked_fread(&version, sizeof(s16), 1, ifile);
496
BSWAP16(version);
497
if (version != 1) {
498
fail_parse("Unknown codebook chunk version");
499
}
500
readaifccodebook(ifile, &coefTable, &order, &npredictors);
501
}
502
else if (strcmp("VADPCMLOOPS", ChunkName) == 0) {
503
checked_fread(&version, sizeof(s16), 1, ifile);
504
BSWAP16(version);
505
if (version != 1) {
506
fail_parse("Unknown loop chunk version");
507
}
508
aloops = readlooppoints(ifile, &nloops);
509
if (nloops != 1) {
510
fail_parse("Only a single loop supported");
511
}
512
}
513
}
514
}
515
break;
516
}
517
518
fseek(ifile, offset + Header.ckSize, SEEK_SET);
519
}
520
521
if (coefTable == NULL) {
522
fail_parse("Codebook missing from bitstream");
523
}
524
525
for (s32 i = 0; i < order; i++) {
526
state[15 - i] = 0;
527
}
528
529
u32 outputBytes = nSamples * sizeof(s16);
530
u8 *outputBuf = malloc(outputBytes);
531
532
fseek(ifile, soundPointer, SEEK_SET);
533
while (currPos < nSamples) {
534
u8 input[9];
535
u8 encoded[9];
536
s32 lastState[16];
537
s32 decoded[16];
538
s16 guess[16];
539
s16 origGuess[16];
540
541
memcpy(lastState, state, sizeof(lastState));
542
checked_fread(input, 9, 1, ifile);
543
544
// Decode for real
545
my_decodeframe(input, state, order, coefTable);
546
memcpy(decoded, state, sizeof(lastState));
547
548
// Create a guess from that, by clamping to 16 bits
549
for (s32 i = 0; i < 16; i++) {
550
origGuess[i] = clamp_to_s16(state[i]);
551
}
552
553
// Encode the guess
554
memcpy(state, lastState, sizeof(lastState));
555
memcpy(guess, origGuess, sizeof(guess));
556
my_encodeframe(encoded, guess, state, coefTable, order, npredictors);
557
558
// If it doesn't match, randomly round numbers until it does.
559
if (memcmp(input, encoded, 9) != 0) {
560
s32 scale = 1 << (input[0] >> 4);
561
do {
562
permute(guess, decoded, scale);
563
memcpy(state, lastState, sizeof(lastState));
564
my_encodeframe(encoded, guess, state, coefTable, order, npredictors);
565
} while (memcmp(input, encoded, 9) != 0);
566
567
// Bring the matching closer to the original decode (not strictly
568
// necessary, but it will move us closer to the target on average).
569
for (s32 failures = 0; failures < 50; failures++) {
570
s32 ind = myrand() % 16;
571
s32 old = guess[ind];
572
if (old == origGuess[ind]) continue;
573
guess[ind] = origGuess[ind];
574
if (myrand() % 2) guess[ind] += (old - origGuess[ind]) / 2;
575
memcpy(state, lastState, sizeof(lastState));
576
my_encodeframe(encoded, guess, state, coefTable, order, npredictors);
577
if (memcmp(input, encoded, 9) == 0) {
578
failures = -1;
579
}
580
else {
581
guess[ind] = old;
582
}
583
}
584
}
585
586
memcpy(state, decoded, sizeof(lastState));
587
BSWAP16_MANY(guess, 16);
588
memcpy(outputBuf + currPos * 2, guess, sizeof(guess));
589
currPos += 16;
590
}
591
592
// Write an incomplete file header. We'll fill in the size later.
593
fwrite("FORM\0\0\0\0AIFF", 12, 1, ofile);
594
595
// Subtract 4 from the COMM size to skip the compression field.
596
write_header(ofile, "COMM", sizeof(CommonChunk) - 4);
597
CommChunk.numFramesH = nSamples >> 16;
598
CommChunk.numFramesL = nSamples & 0xffff;
599
BSWAP16(CommChunk.numChannels);
600
BSWAP16(CommChunk.numFramesH);
601
BSWAP16(CommChunk.numFramesL);
602
BSWAP16(CommChunk.sampleSize);
603
fwrite(&CommChunk, sizeof(CommonChunk) - 4, 1, ofile);
604
605
if (nloops > 0) {
606
s32 startPos = aloops[0].start, endPos = aloops[0].end;
607
const char *markerNames[2] = {"start", "end"};
608
Marker markers[2] = {
609
{1, startPos >> 16, startPos & 0xffff},
610
{2, endPos >> 16, endPos & 0xffff}
611
};
612
write_header(ofile, "MARK", 2 + 2 * sizeof(Marker) + 1 + 5 + 1 + 3);
613
s16 numMarkers = bswap16(2);
614
fwrite(&numMarkers, sizeof(s16), 1, ofile);
615
for (s32 i = 0; i < 2; i++) {
616
u8 len = (u8) strlen(markerNames[i]);
617
BSWAP16(markers[i].MarkerID);
618
BSWAP16(markers[i].positionH);
619
BSWAP16(markers[i].positionL);
620
fwrite(&markers[i], sizeof(Marker), 1, ofile);
621
fwrite(&len, 1, 1, ofile);
622
fwrite(markerNames[i], len, 1, ofile);
623
}
624
625
write_header(ofile, "INST", sizeof(InstrumentChunk));
626
InstChunk.sustainLoop.playMode = bswap16(1);
627
InstChunk.sustainLoop.beginLoop = bswap16(1);
628
InstChunk.sustainLoop.endLoop = bswap16(2);
629
InstChunk.releaseLoop.playMode = 0;
630
InstChunk.releaseLoop.beginLoop = 0;
631
InstChunk.releaseLoop.endLoop = 0;
632
fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile);
633
}
634
635
// Save the coefficient table for use when encoding. Ideally this wouldn't
636
// be needed and "tabledesign -s 1" would generate the right table, but in
637
// practice it's difficult to adjust samples to make that happen.
638
write_header(ofile, "APPL", 4 + 12 + sizeof(CodeChunk) + npredictors * order * 8 * 2);
639
fwrite("stoc", 4, 1, ofile);
640
CodeChunk cChunk;
641
cChunk.version = bswap16(1);
642
cChunk.order = bswap16(order);
643
cChunk.nEntries = bswap16(npredictors);
644
fwrite("\x0bVADPCMCODES", 12, 1, ofile);
645
fwrite(&cChunk, sizeof(CodeChunk), 1, ofile);
646
for (s32 i = 0; i < npredictors; i++) {
647
for (s32 j = 0; j < order; j++) {
648
for (s32 k = 0; k < 8; k++) {
649
s16 ts = bswap16(coefTable[i][k][j]);
650
fwrite(&ts, sizeof(s16), 1, ofile);
651
}
652
}
653
}
654
655
write_header(ofile, "SSND", outputBytes + 8);
656
SndDChunk.offset = 0;
657
SndDChunk.blockSize = 0;
658
fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile);
659
fwrite(outputBuf, outputBytes, 1, ofile);
660
661
// Fix the size in the header
662
s32 fileSize = bswap32(ftell(ofile) - 8);
663
fseek(ofile, 4, SEEK_SET);
664
fwrite(&fileSize, 4, 1, ofile);
665
666
fclose(ifile);
667
fclose(ofile);
668
return 0;
669
}
670
671