sm64coopdx/tools/sdk-tools/adpcm/vadpcm_enc.c

520 lines
16 KiB
C

#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "vadpcm.h"
static char usage[] = "[-t -l min_loop_length] -c codebook aifcfile compressedfile";
int main(int argc, char **argv)
{
s32 c;
char *progname = argv[0];
u16 nloops = 0;
s16 numMarkers;
s16 *inBuffer;
s16 ts;
u32 minLoopLength = 800;
s32 ***coefTable = NULL;
s32 *state; // has to be signed
s32 order;
s32 npredictors;
s32 done = 0;
s32 truncate = 0;
s32 num;
s32 tableSize;
u32 nsam;
s32 left;
u32 newEnd;
s32 nRepeats;
s32 i;
s32 j;
s32 k;
u32 nFrames;
s32 offset;
s32 cChunkPos;
u32 currentPos = 0;
s32 soundPointer = 0;
s32 startPointer = 0;
s32 startSoundPointer = 0;
s32 cType;
s32 nBytes = 0;
u32 loopEnd;
char *compName = "VADPCM ~4-1";
char *appCodeName = "VADPCMCODES";
char *appLoopName = "VADPCMLOOPS";
u8 strnLen;
Chunk AppChunk;
Chunk FormChunk;
ChunkHeader CSndChunk;
ChunkHeader Header;
CommonChunk CommChunk;
SoundDataChunk SndDChunk;
InstrumentChunk InstChunk;
Loop *loops = NULL;
ALADPCMloop *aloops = NULL;
Marker *markers = NULL;
CodeChunk cChunk;
char filename[1024];
FILE *fhandle;
FILE *ifile;
FILE *ofile;
if (argc < 2)
{
fprintf(stderr, "%s %s\n", progname, usage);
exit(1);
}
while ((c = getopt(argc, argv, "tc:l:")) != -1)
{
switch (c)
{
case 'c':
if (sscanf(optarg, "%s", filename) == 1)
{
if ((fhandle = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "Codebook file %s could not be opened\n", filename);
exit(1);
}
if (readcodebook(fhandle, &coefTable, &order, &npredictors) != 0)
{
fprintf(stderr, "Error reading codebook\n");
exit(1);
}
}
break;
case 't':
truncate = 1;
break;
case 'l':
sscanf(optarg, "%u", &minLoopLength);
break;
default:
break;
}
}
if (coefTable == 0)
{
fprintf(stderr, "You should specify a coefficient codebook with the [-c] option\n");
exit(1);
}
argv += optind - 1;
if ((ifile = fopen(argv[1], MODE_READ)) == NULL)
{
fprintf(stderr, "%s: input file [%s] could not be opened.\n", progname, argv[1]);
exit(1);
}
if ((ofile = fopen(argv[2], MODE_WRITE)) == NULL)
{
fprintf(stderr, "%s: output file [%s] could not be opened.\n", progname, argv[2]);
exit(1);
}
state = malloc(16 * sizeof(s32));
for (i = 0; i < 16; i++)
{
state[i] = 0;
}
#ifndef __sgi
// If there is no instrument chunk, make sure to output zeroes instead of
// garbage. (This matches how the IRIX -g-compiled version behaves.)
memset(&InstChunk, 0, sizeof(InstChunk));
#endif
inBuffer = malloc(16 * sizeof(s16));
fread(&FormChunk, sizeof(Chunk), 1, ifile);
BSWAP32(FormChunk.ckID)
BSWAP32(FormChunk.ckSize)
BSWAP32(FormChunk.formType)
// @bug This doesn't check for FORM for AIFF files, probably due to mistaken operator precedence.
if (!((FormChunk.ckID == 0x464f524d && // FORM
FormChunk.formType == 0x41494643) || // AIFC
FormChunk.formType == 0x41494646)) // AIFF
{
fprintf(stderr, "%s: [%s] is not an AIFF-C File\n", progname, argv[1]);
exit(1);
}
while (!done)
{
num = fread(&Header, 8, 1, ifile);
if (num <= 0)
{
done = 1;
break;
}
BSWAP32(Header.ckID)
BSWAP32(Header.ckSize)
Header.ckSize++, Header.ckSize &= ~1;
switch (Header.ckID)
{
case 0x434f4d4d: // COMM
offset = ftell(ifile);
num = fread(&CommChunk, sizeof(CommonChunk), 1, ifile);
if (num <= 0)
{
fprintf(stderr, "%s: error parsing file [%s]\n", progname, argv[1]);
done = 1;
}
BSWAP16(CommChunk.numChannels)
BSWAP16(CommChunk.numFramesH)
BSWAP16(CommChunk.numFramesL)
BSWAP16(CommChunk.sampleSize)
if (FormChunk.formType != 0x41494646) // AIFF
{
BSWAP16(CommChunk.compressionTypeH)
BSWAP16(CommChunk.compressionTypeL)
cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL;
if (cType != 0x4e4f4e45) // NONE
{
fprintf(stderr, "%s: file [%s] contains compressed data.\n", progname, argv[1]);
exit(1);
}
}
if (CommChunk.numChannels != 1)
{
fprintf(stderr, "%s: file [%s] contains %ld channels, only 1 channel supported.\n", progname, argv[1], (long) CommChunk.numChannels);
exit(1);
}
if (CommChunk.sampleSize != 16)
{
fprintf(stderr, "%s: file [%s] contains %ld bit samples, only 16 bit samples supported.\n", progname, argv[1], (long) CommChunk.sampleSize);
exit(1);
}
fseek(ifile, offset + Header.ckSize, SEEK_SET);
break;
case 0x53534e44: // SSND
offset = ftell(ifile);
fread(&SndDChunk, sizeof(SoundDataChunk), 1, ifile);
BSWAP32(SndDChunk.offset)
BSWAP32(SndDChunk.blockSize)
// The assert error messages specify line numbers 219/220. Match
// that using a #line directive.
#ifdef __sgi
# line 218
#endif
assert(SndDChunk.offset == 0);
assert(SndDChunk.blockSize == 0);
soundPointer = ftell(ifile);
fseek(ifile, offset + Header.ckSize, SEEK_SET);
break;
case 0x4d41524b: // MARK
offset = ftell(ifile);
fread(&numMarkers, sizeof(s16), 1, ifile);
BSWAP16(numMarkers)
markers = malloc(numMarkers * sizeof(Marker));
for (i = 0; i < numMarkers; i++)
{
fread(&markers[i], sizeof(Marker), 1, ifile);
BSWAP16(markers[i].MarkerID)
BSWAP16(markers[i].positionH)
BSWAP16(markers[i].positionL)
fread(&strnLen, 1, 1, ifile);
if ((strnLen & 1) != 0)
{
fseek(ifile, strnLen, SEEK_CUR);
}
else
{
fseek(ifile, strnLen + 1, SEEK_CUR);
}
}
fseek(ifile, offset + Header.ckSize, SEEK_SET);
break;
case 0x494e5354: // INST
offset = ftell(ifile);
fread(&InstChunk, sizeof(InstrumentChunk), 1, ifile);
BSWAP16(InstChunk.sustainLoop.playMode)
BSWAP16(InstChunk.sustainLoop.beginLoop)
BSWAP16(InstChunk.sustainLoop.endLoop)
BSWAP16(InstChunk.releaseLoop.playMode)
BSWAP16(InstChunk.releaseLoop.beginLoop)
BSWAP16(InstChunk.releaseLoop.endLoop)
aloops = malloc(2 * sizeof(ALADPCMloop));
loops = malloc(2 * sizeof(Loop));
if (InstChunk.sustainLoop.playMode == 1)
{
loops[nloops].beginLoop = InstChunk.sustainLoop.beginLoop;
loops[nloops].endLoop = InstChunk.sustainLoop.endLoop;
nloops++;
}
if (InstChunk.releaseLoop.playMode == 1)
{
loops[nloops].beginLoop = InstChunk.releaseLoop.beginLoop;
loops[nloops].endLoop = InstChunk.releaseLoop.endLoop;
nloops++;
}
fseek(ifile, offset + Header.ckSize, SEEK_SET);
break;
default:
fseek(ifile, Header.ckSize, SEEK_CUR);
break;
}
}
FormChunk.formType = 0x41494643; // AIFC
BSWAP32(FormChunk.ckID)
BSWAP32(FormChunk.ckSize)
BSWAP32(FormChunk.formType)
fwrite(&FormChunk, sizeof(Chunk), 1, ofile);
Header.ckID = 0x434f4d4d; // COMM
Header.ckSize = sizeof(CommonChunk) + 1 + 11;
BSWAP32(Header.ckID)
BSWAP32(Header.ckSize)
fwrite(&Header, sizeof(ChunkHeader), 1, ofile);
CommChunk.compressionTypeH = 0x5641; // VA
CommChunk.compressionTypeL = 0x5043; // PC
cChunkPos = ftell(ofile);
// CommChunk written later
fwrite(&CommChunk, sizeof(CommonChunk), 1, ofile);
strnLen = sizeof("VADPCM ~4-1") - 1;
fwrite(&strnLen, 1, 1, ofile);
fwrite(compName, strnLen, 1, ofile);
Header.ckID = 0x494e5354; // INST
Header.ckSize = sizeof(InstrumentChunk);
BSWAP32(Header.ckID)
BSWAP32(Header.ckSize)
fwrite(&Header, sizeof(ChunkHeader), 1, ofile);
BSWAP16(InstChunk.sustainLoop.playMode)
BSWAP16(InstChunk.sustainLoop.beginLoop)
BSWAP16(InstChunk.sustainLoop.endLoop)
BSWAP16(InstChunk.releaseLoop.playMode)
BSWAP16(InstChunk.releaseLoop.beginLoop)
BSWAP16(InstChunk.releaseLoop.endLoop)
fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile);
tableSize = order * 2 * npredictors * 8;
strnLen = sizeof("VADPCMCODES") - 1;
AppChunk.ckID = 0x4150504c; // APPL
AppChunk.ckSize = 4 + tableSize + 1 + strnLen + sizeof(CodeChunk);
AppChunk.formType = 0x73746f63; // stoc
BSWAP32(AppChunk.ckID)
BSWAP32(AppChunk.ckSize)
BSWAP32(AppChunk.formType)
fwrite(&AppChunk, sizeof(Chunk), 1, ofile);
cChunk.version = 1;
cChunk.order = order;
cChunk.nEntries = npredictors;
BSWAP16(cChunk.version)
BSWAP16(cChunk.order)
BSWAP16(cChunk.nEntries)
fwrite(&strnLen, 1, 1, ofile);
fwrite(appCodeName, strnLen, 1, ofile);
fwrite(&cChunk, sizeof(CodeChunk), 1, ofile);
for (i = 0; i < npredictors; i++)
{
for (j = 0; j < order; j++)
{
for (k = 0; k < 8; k++)
{
ts = coefTable[i][k][j];
BSWAP16(ts)
fwrite(&ts, sizeof(s16), 1, ofile);
}
}
}
currentPos = 0;
if (soundPointer > 0)
{
fseek(ifile, soundPointer, SEEK_SET);
}
else
{
fprintf(stderr, "%s: Error in sound chunk", progname);
exit(1);
}
soundPointer = ftell(ofile);
// CSndChunk written later
fwrite(&CSndChunk, sizeof(ChunkHeader), 1, ofile);
BSWAP32(SndDChunk.offset)
BSWAP32(SndDChunk.blockSize)
fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile);
startSoundPointer = ftell(ifile);
for (i = 0; i < nloops; i++)
{
if (aloops == NULL || markers == NULL || lookupMarker(&aloops[i].start, loops[i].beginLoop, markers, numMarkers) != 0)
{
fprintf(stderr, "%s: Start loop marker not found\n", progname);
}
else if (aloops == NULL || markers == NULL || lookupMarker(&aloops[i].end, loops[i].endLoop, markers, numMarkers) != 0)
{
fprintf(stderr, "%s: End loop marker not found\n", progname);
}
else
{
startPointer = startSoundPointer + aloops[i].start * 2;
nRepeats = 0;
newEnd = aloops[i].end;
while (newEnd - aloops[i].start < minLoopLength)
{
nRepeats++;
newEnd += aloops[i].end - aloops[i].start;
}
while (currentPos <= aloops[i].start)
{
if (fread(inBuffer, sizeof(s16), 16, ifile) == 16)
{
BSWAP16_MANY(inBuffer, 16)
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
currentPos += 16;
nBytes += 9;
}
else
{
fprintf(stderr, "%s: Not enough samples in file [%s]\n", progname, argv[1]);
exit(1);
}
}
for (j = 0; j < 16; j++)
{
if (state[j] >= 0x8000)
{
state[j] = 0x7fff;
}
if (state[j] < -0x7fff)
{
state[j] = -0x7fff;
}
aloops[i].state[j] = state[j];
}
aloops[i].count = -1;
while (nRepeats > 0)
{
for (; currentPos + 16 < aloops[i].end; currentPos += 16)
{
if (fread(inBuffer, sizeof(s16), 16, ifile) == 16)
{
BSWAP16_MANY(inBuffer, 16)
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
nBytes += 9;
}
}
left = aloops[i].end - currentPos;
fread(inBuffer, sizeof(s16), left, ifile);
BSWAP16_MANY(inBuffer, left)
fseek(ifile, startPointer, SEEK_SET);
fread(inBuffer + left, sizeof(s16), 16 - left, ifile);
BSWAP16_MANY(inBuffer + left, 16 - left)
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, 16);
nBytes += 9;
currentPos = aloops[i].start - left + 16;
nRepeats--;
}
aloops[i].end = newEnd;
}
}
nFrames = (CommChunk.numFramesH << 16) + CommChunk.numFramesL;
if ((nloops > 0U) & truncate)
{
lookupMarker(&loopEnd, loops[nloops - 1].endLoop, markers, numMarkers);
nFrames = (loopEnd + 16 < nFrames ? loopEnd + 16 : nFrames);
}
while (currentPos < nFrames)
{
if (nFrames - currentPos < 16)
{
nsam = nFrames - currentPos;
}
else
{
nsam = 16;
}
if (fread(inBuffer, 2, nsam, ifile) == nsam)
{
BSWAP16_MANY(inBuffer, nsam)
vencodeframe(ofile, inBuffer, state, coefTable, order, npredictors, nsam);
currentPos += nsam;
nBytes += 9;
}
else
{
fprintf(stderr, "Missed a frame!\n");
break;
}
}
if (nBytes % 2)
{
nBytes++;
ts = 0;
fwrite(&ts, 1, 1, ofile);
}
if (nloops > 0)
{
strnLen = sizeof("VADPCMLOOPS") - 1;
AppChunk.ckID = 0x4150504c; // APPL
AppChunk.ckSize = nloops * sizeof(ALADPCMloop) + strnLen + 4 + 1 + 2 + 2;
AppChunk.formType = 0x73746f63; // stoc
BSWAP32(AppChunk.ckID)
BSWAP32(AppChunk.ckSize)
BSWAP32(AppChunk.formType)
fwrite(&AppChunk, sizeof(Chunk), 1, ofile);
fwrite(&strnLen, 1, 1, ofile);
fwrite(appLoopName, strnLen, 1, ofile);
ts = 1;
BSWAP16(ts)
fwrite(&ts, sizeof(s16), 1, ofile);
BSWAP16(nloops)
fwrite(&nloops, sizeof(s16), 1, ofile);
BSWAP16(nloops)
for (i = 0; i < nloops; i++)
{
if (aloops == NULL) { continue; }
BSWAP32(aloops[i].start)
BSWAP32(aloops[i].end)
BSWAP32(aloops[i].count)
BSWAP16_MANY(aloops[i].state, 16)
fwrite(&aloops[i], sizeof(ALADPCMloop), 1, ofile);
}
}
fseek(ofile, soundPointer, SEEK_SET);
CSndChunk.ckID = 0x53534e44; // SSND
CSndChunk.ckSize = nBytes + 8;
BSWAP32(CSndChunk.ckID)
BSWAP32(CSndChunk.ckSize)
fwrite(&CSndChunk, sizeof(ChunkHeader), 1, ofile);
fseek(ofile, cChunkPos, SEEK_SET);
nFrames = nBytes * 16 / 9;
CommChunk.numFramesH = nFrames >> 16;
CommChunk.numFramesL = nFrames & 0xffff;
BSWAP16(CommChunk.numChannels)
BSWAP16(CommChunk.numFramesH)
BSWAP16(CommChunk.numFramesL)
BSWAP16(CommChunk.sampleSize)
BSWAP16(CommChunk.compressionTypeH)
BSWAP16(CommChunk.compressionTypeL)
fwrite(&CommChunk, sizeof(CommonChunk), 1, ofile);
fclose(ifile);
fclose(ofile);
return 0;
}