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