Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/tools/sdk-tools/adpcm/vadpcm_enc.c
7861 views
1
#include <string.h>
2
#include <assert.h>
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <getopt.h>
6
#include "vadpcm.h"
7
8
static char usage[] = "[-t -l min_loop_length] -c codebook aifcfile compressedfile";
9
10
int main(int argc, char **argv)
11
{
12
s32 c;
13
char *progname = argv[0];
14
s16 nloops = 0;
15
s16 numMarkers;
16
s16 *inBuffer;
17
s16 ts;
18
s32 minLoopLength = 800;
19
s32 ***coefTable = NULL;
20
s32 *state;
21
s32 order;
22
s32 npredictors;
23
s32 done = 0;
24
s32 truncate = 0;
25
s32 num;
26
s32 tableSize;
27
s32 nsam;
28
s32 left;
29
u32 newEnd;
30
s32 nRepeats;
31
s32 i;
32
s32 j;
33
s32 k;
34
s32 nFrames;
35
s32 offset;
36
s32 cChunkPos;
37
s32 currentPos;
38
s32 soundPointer = 0;
39
s32 startPointer = 0;
40
s32 startSoundPointer = 0;
41
s32 cType;
42
s32 nBytes = 0;
43
u32 loopEnd;
44
char *compName = "VADPCM ~4-1";
45
char *appCodeName = "VADPCMCODES";
46
char *appLoopName = "VADPCMLOOPS";
47
u8 strnLen;
48
Chunk AppChunk;
49
Chunk FormChunk;
50
ChunkHeader CSndChunk;
51
ChunkHeader Header;
52
CommonChunk CommChunk;
53
SoundDataChunk SndDChunk;
54
InstrumentChunk InstChunk;
55
Loop *loops = NULL;
56
ALADPCMloop *aloops;
57
Marker *markers;
58
CodeChunk cChunk;
59
char filename[1024];
60
FILE *fhandle;
61
FILE *ifile;
62
FILE *ofile;
63
64
if (argc < 2)
65
{
66
fprintf(stderr, "%s %s\n", progname, usage);
67
exit(1);
68
}
69
70
while ((c = getopt(argc, argv, "tc:l:")) != -1)
71
{
72
switch (c)
73
{
74
case 'c':
75
if (sscanf(optarg, "%s", filename) == 1)
76
{
77
if ((fhandle = fopen(filename, "r")) == NULL)
78
{
79
fprintf(stderr, "Codebook file %s could not be opened\n", filename);
80
exit(1);
81
}
82
if (readcodebook(fhandle, &coefTable, &order, &npredictors) != 0)
83
{
84
fprintf(stderr, "Error reading codebook\n");
85
exit(1);
86
}
87
}
88
break;
89
90
case 't':
91
truncate = 1;
92
break;
93
94
case 'l':
95
sscanf(optarg, "%d", &minLoopLength);
96
break;
97
98
default:
99
break;
100
}
101
}
102
103
if (coefTable == 0)
104
{
105
fprintf(stderr, "You should specify a coefficient codebook with the [-c] option\n");
106
exit(1);
107
}
108
109
argv += optind - 1;
110
if ((ifile = fopen(argv[1], MODE_READ)) == NULL)
111
{
112
fprintf(stderr, "%s: input file [%s] could not be opened.\n", progname, argv[1]);
113
exit(1);
114
}
115
if ((ofile = fopen(argv[2], MODE_WRITE)) == NULL)
116
{
117
fprintf(stderr, "%s: output file [%s] could not be opened.\n", progname, argv[2]);
118
exit(1);
119
}
120
121
state = malloc(16 * sizeof(s32));
122
for (i = 0; i < 16; i++)
123
{
124
state[i] = 0;
125
}
126
127
#ifndef __sgi
128
// If there is no instrument chunk, make sure to output zeroes instead of
129
// garbage. (This matches how the IRIX -g-compiled version behaves.)
130
memset(&InstChunk, 0, sizeof(InstChunk));
131
#endif
132
133
inBuffer = malloc(16 * sizeof(s16));
134
135
fread(&FormChunk, sizeof(Chunk), 1, ifile);
136
BSWAP32(FormChunk.ckID)
137
BSWAP32(FormChunk.ckSize)
138
BSWAP32(FormChunk.formType)
139
140
// @bug This doesn't check for FORM for AIFF files, probably due to mistaken operator precedence.
141
if (!((FormChunk.ckID == 0x464f524d && // FORM
142
FormChunk.formType == 0x41494643) || // AIFC
143
FormChunk.formType == 0x41494646)) // AIFF
144
{
145
fprintf(stderr, "%s: [%s] is not an AIFF-C File\n", progname, argv[1]);
146
exit(1);
147
}
148
149
while (!done)
150
{
151
num = fread(&Header, 8, 1, ifile);
152
if (num <= 0)
153
{
154
done = 1;
155
break;
156
}
157
BSWAP32(Header.ckID)
158
BSWAP32(Header.ckSize)
159
160
Header.ckSize++, Header.ckSize &= ~1;
161
switch (Header.ckID)
162
{
163
case 0x434f4d4d: // COMM
164
offset = ftell(ifile);
165
num = fread(&CommChunk, sizeof(CommonChunk), 1, ifile);
166
if (num <= 0)
167
{
168
fprintf(stderr, "%s: error parsing file [%s]\n", progname, argv[1]);
169
done = 1;
170
}
171
BSWAP16(CommChunk.numChannels)
172
BSWAP16(CommChunk.numFramesH)
173
BSWAP16(CommChunk.numFramesL)
174
BSWAP16(CommChunk.sampleSize)
175
if (FormChunk.formType != 0x41494646) // AIFF
176
{
177
BSWAP16(CommChunk.compressionTypeH)
178
BSWAP16(CommChunk.compressionTypeL)
179
cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL;
180
if (cType != 0x4e4f4e45) // NONE
181
{
182
fprintf(stderr, "%s: file [%s] contains compressed data.\n", progname, argv[1]);
183
exit(1);
184
}
185
}
186
if (CommChunk.numChannels != 1)
187
{
188
fprintf(stderr, "%s: file [%s] contains %ld channels, only 1 channel supported.\n", progname, argv[1], (long) CommChunk.numChannels);
189
exit(1);
190
}
191
if (CommChunk.sampleSize != 16)
192
{
193
fprintf(stderr, "%s: file [%s] contains %ld bit samples, only 16 bit samples supported.\n", progname, argv[1], (long) CommChunk.sampleSize);
194
exit(1);
195
}
196
fseek(ifile, offset + Header.ckSize, SEEK_SET);
197
break;
198
199
case 0x53534e44: // SSND
200
offset = ftell(ifile);
201
fread(&SndDChunk, sizeof(SoundDataChunk), 1, ifile);
202
BSWAP32(SndDChunk.offset)
203
BSWAP32(SndDChunk.blockSize)
204
// The assert error messages specify line numbers 219/220. Match
205
// that using a #line directive.
206
#ifdef __sgi
207
# line 218
208
#endif
209
assert(SndDChunk.offset == 0);
210
assert(SndDChunk.blockSize == 0);
211
soundPointer = ftell(ifile);
212
fseek(ifile, offset + Header.ckSize, SEEK_SET);
213
break;
214
215
case 0x4d41524b: // MARK
216
offset = ftell(ifile);
217
fread(&numMarkers, sizeof(s16), 1, ifile);
218
BSWAP16(numMarkers)
219
markers = malloc(numMarkers * sizeof(Marker));
220
for (i = 0; i < numMarkers; i++)
221
{
222
fread(&markers[i], sizeof(Marker), 1, ifile);
223
BSWAP16(markers[i].MarkerID)
224
BSWAP16(markers[i].positionH)
225
BSWAP16(markers[i].positionL)
226
fread(&strnLen, 1, 1, ifile);
227
if ((strnLen & 1) != 0)
228
{
229
fseek(ifile, strnLen, SEEK_CUR);
230
}
231
else
232
{
233
fseek(ifile, strnLen + 1, SEEK_CUR);
234
}
235
}
236
fseek(ifile, offset + Header.ckSize, SEEK_SET);
237
break;
238
239
case 0x494e5354: // INST
240
offset = ftell(ifile);
241
fread(&InstChunk, sizeof(InstrumentChunk), 1, ifile);
242
BSWAP16(InstChunk.sustainLoop.playMode)
243
BSWAP16(InstChunk.sustainLoop.beginLoop)
244
BSWAP16(InstChunk.sustainLoop.endLoop)
245
BSWAP16(InstChunk.releaseLoop.playMode)
246
BSWAP16(InstChunk.releaseLoop.beginLoop)
247
BSWAP16(InstChunk.releaseLoop.endLoop)
248
aloops = malloc(2 * sizeof(ALADPCMloop));
249
loops = malloc(2 * sizeof(Loop));
250
if (InstChunk.sustainLoop.playMode == 1)
251
{
252
loops[nloops].beginLoop = InstChunk.sustainLoop.beginLoop;
253
loops[nloops].endLoop = InstChunk.sustainLoop.endLoop;
254
nloops++;
255
}
256
if (InstChunk.releaseLoop.playMode == 1)
257
{
258
loops[nloops].beginLoop = InstChunk.releaseLoop.beginLoop;
259
loops[nloops].endLoop = InstChunk.releaseLoop.endLoop;
260
nloops++;
261
}
262
fseek(ifile, offset + Header.ckSize, SEEK_SET);
263
break;
264
265
default:
266
fseek(ifile, Header.ckSize, SEEK_CUR);
267
break;
268
}
269
}
270
271
FormChunk.formType = 0x41494643; // AIFC
272
BSWAP32(FormChunk.ckID)
273
BSWAP32(FormChunk.ckSize)
274
BSWAP32(FormChunk.formType)
275
fwrite(&FormChunk, sizeof(Chunk), 1, ofile);
276
277
Header.ckID = 0x434f4d4d; // COMM
278
Header.ckSize = sizeof(CommonChunk) + 1 + 11;
279
BSWAP32(Header.ckID)
280
BSWAP32(Header.ckSize)
281
fwrite(&Header, sizeof(ChunkHeader), 1, ofile);
282
CommChunk.compressionTypeH = 0x5641; // VA
283
CommChunk.compressionTypeL = 0x5043; // PC
284
cChunkPos = ftell(ofile);
285
// CommChunk written later
286
fwrite(&CommChunk, sizeof(CommonChunk), 1, ofile);
287
strnLen = sizeof("VADPCM ~4-1") - 1;
288
fwrite(&strnLen, 1, 1, ofile);
289
fwrite(compName, strnLen, 1, ofile);
290
291
Header.ckID = 0x494e5354; // INST
292
Header.ckSize = sizeof(InstrumentChunk);
293
BSWAP32(Header.ckID)
294
BSWAP32(Header.ckSize)
295
fwrite(&Header, sizeof(ChunkHeader), 1, ofile);
296
BSWAP16(InstChunk.sustainLoop.playMode)
297
BSWAP16(InstChunk.sustainLoop.beginLoop)
298
BSWAP16(InstChunk.sustainLoop.endLoop)
299
BSWAP16(InstChunk.releaseLoop.playMode)
300
BSWAP16(InstChunk.releaseLoop.beginLoop)
301
BSWAP16(InstChunk.releaseLoop.endLoop)
302
fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile);
303
304
tableSize = order * 2 * npredictors * 8;
305
strnLen = sizeof("VADPCMCODES") - 1;
306
AppChunk.ckID = 0x4150504c; // APPL
307
AppChunk.ckSize = 4 + tableSize + 1 + strnLen + sizeof(CodeChunk);
308
AppChunk.formType = 0x73746f63; // stoc
309
BSWAP32(AppChunk.ckID)
310
BSWAP32(AppChunk.ckSize)
311
BSWAP32(AppChunk.formType)
312
fwrite(&AppChunk, sizeof(Chunk), 1, ofile);
313
cChunk.version = 1;
314
cChunk.order = order;
315
cChunk.nEntries = npredictors;
316
BSWAP16(cChunk.version)
317
BSWAP16(cChunk.order)
318
BSWAP16(cChunk.nEntries)
319
fwrite(&strnLen, 1, 1, ofile);
320
fwrite(appCodeName, strnLen, 1, ofile);
321
fwrite(&cChunk, sizeof(CodeChunk), 1, ofile);
322
323
for (i = 0; i < npredictors; i++)
324
{
325
for (j = 0; j < order; j++)
326
{
327
for (k = 0; k < 8; k++)
328
{
329
ts = coefTable[i][k][j];
330
BSWAP16(ts)
331
fwrite(&ts, sizeof(s16), 1, ofile);
332
}
333
}
334
}
335
336
currentPos = 0;
337
if (soundPointer > 0)
338
{
339
fseek(ifile, soundPointer, SEEK_SET);
340
}
341
else
342
{
343
fprintf(stderr, "%s: Error in sound chunk", progname);
344
exit(1);
345
}
346
347
soundPointer = ftell(ofile);
348
// CSndChunk written later
349
fwrite(&CSndChunk, sizeof(ChunkHeader), 1, ofile);
350
BSWAP32(SndDChunk.offset)
351
BSWAP32(SndDChunk.blockSize)
352
fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile);
353
startSoundPointer = ftell(ifile);
354
for (i = 0; i < nloops; i++)
355
{
356
if (lookupMarker(&aloops[i].start, loops[i].beginLoop, markers, numMarkers) != 0)
357
{
358
fprintf(stderr, "%s: Start loop marker not found\n", progname);
359
}
360
else if (lookupMarker(&aloops[i].end, loops[i].endLoop, markers, numMarkers) != 0)
361
{
362
fprintf(stderr, "%s: End loop marker not found\n", progname);
363
}
364
else
365
{
366
startPointer = startSoundPointer + aloops[i].start * 2;
367
nRepeats = 0;
368
newEnd = aloops[i].end;
369
while (newEnd - aloops[i].start < minLoopLength)
370
{
371
nRepeats++;
372
newEnd += aloops[i].end - aloops[i].start;
373
}
374
375
while (currentPos <= aloops[i].start)
376
{
377
if (fread(inBuffer, sizeof(s16), 16, ifile) == 16)
378
{
379
BSWAP16_MANY(inBuffer, 16)
380
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
381
currentPos += 16;
382
nBytes += 9;
383
}
384
else
385
{
386
fprintf(stderr, "%s: Not enough samples in file [%s]\n", progname, argv[1]);
387
exit(1);
388
}
389
}
390
391
for (j = 0; j < 16; j++)
392
{
393
if (state[j] >= 0x8000)
394
{
395
state[j] = 0x7fff;
396
}
397
if (state[j] < -0x7fff)
398
{
399
state[j] = -0x7fff;
400
}
401
aloops[i].state[j] = state[j];
402
}
403
404
aloops[i].count = -1;
405
while (nRepeats > 0)
406
{
407
for (; currentPos + 16 < aloops[i].end; currentPos += 16)
408
{
409
if (fread(inBuffer, sizeof(s16), 16, ifile) == 16)
410
{
411
BSWAP16_MANY(inBuffer, 16)
412
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
413
nBytes += 9;
414
}
415
}
416
left = aloops[i].end - currentPos;
417
fread(inBuffer, sizeof(s16), left, ifile);
418
BSWAP16_MANY(inBuffer, left)
419
fseek(ifile, startPointer, SEEK_SET);
420
fread(inBuffer + left, sizeof(s16), 16 - left, ifile);
421
BSWAP16_MANY(inBuffer + left, 16 - left)
422
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
423
nBytes += 9;
424
currentPos = aloops[i].start - left + 16;
425
nRepeats--;
426
}
427
aloops[i].end = newEnd;
428
}
429
}
430
431
nFrames = (CommChunk.numFramesH << 16) + CommChunk.numFramesL;
432
if ((nloops > 0U) & truncate)
433
{
434
lookupMarker(&loopEnd, loops[nloops - 1].endLoop, markers, numMarkers);
435
nFrames = (loopEnd + 16 < nFrames ? loopEnd + 16 : nFrames);
436
}
437
438
while (currentPos < nFrames)
439
{
440
if (nFrames - currentPos < 16)
441
{
442
nsam = nFrames - currentPos;
443
}
444
else
445
{
446
nsam = 16;
447
}
448
449
if (fread(inBuffer, 2, nsam, ifile) == nsam)
450
{
451
BSWAP16_MANY(inBuffer, nsam)
452
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, nsam);
453
currentPos += nsam;
454
nBytes += 9;
455
}
456
else
457
{
458
fprintf(stderr, "Missed a frame!\n");
459
break;
460
}
461
}
462
463
if (nBytes % 2)
464
{
465
nBytes++;
466
ts = 0;
467
fwrite(&ts, 1, 1, ofile);
468
}
469
470
if (nloops > 0)
471
{
472
strnLen = sizeof("VADPCMLOOPS") - 1;
473
AppChunk.ckID = 0x4150504c; // APPL
474
AppChunk.ckSize = nloops * sizeof(ALADPCMloop) + strnLen + 4 + 1 + 2 + 2;
475
AppChunk.formType = 0x73746f63; // stoc
476
BSWAP32(AppChunk.ckID)
477
BSWAP32(AppChunk.ckSize)
478
BSWAP32(AppChunk.formType)
479
fwrite(&AppChunk, sizeof(Chunk), 1, ofile);
480
fwrite(&strnLen, 1, 1, ofile);
481
fwrite(appLoopName, strnLen, 1, ofile);
482
ts = 1;
483
BSWAP16(ts)
484
fwrite(&ts, sizeof(s16), 1, ofile);
485
BSWAP16(nloops)
486
fwrite(&nloops, sizeof(s16), 1, ofile);
487
BSWAP16(nloops)
488
for (i = 0; i < nloops; i++)
489
{
490
BSWAP32(aloops[i].start)
491
BSWAP32(aloops[i].end)
492
BSWAP32(aloops[i].count)
493
BSWAP16_MANY(aloops[i].state, 16)
494
fwrite(&aloops[i], sizeof(ALADPCMloop), 1, ofile);
495
}
496
}
497
498
fseek(ofile, soundPointer, SEEK_SET);
499
CSndChunk.ckID = 0x53534e44; // SSND
500
CSndChunk.ckSize = nBytes + 8;
501
BSWAP32(CSndChunk.ckID)
502
BSWAP32(CSndChunk.ckSize)
503
fwrite(&CSndChunk, sizeof(ChunkHeader), 1, ofile);
504
fseek(ofile, cChunkPos, SEEK_SET);
505
nFrames = nBytes * 16 / 9;
506
CommChunk.numFramesH = nFrames >> 16;
507
CommChunk.numFramesL = nFrames & 0xffff;
508
BSWAP16(CommChunk.numChannels)
509
BSWAP16(CommChunk.numFramesH)
510
BSWAP16(CommChunk.numFramesL)
511
BSWAP16(CommChunk.sampleSize)
512
BSWAP16(CommChunk.compressionTypeH)
513
BSWAP16(CommChunk.compressionTypeL)
514
fwrite(&CommChunk, sizeof(CommonChunk), 1, ofile);
515
fclose(ifile);
516
fclose(ofile);
517
return 0;
518
}
519
520