This is shorten.c in view mode; [Download] [Up]
/****************************************************************************** * * * Copyright (C) 1992,1993,1994 Tony Robinson * * * * See the file LICENSE for conditions on distribution and usage * * * ******************************************************************************/ /* SAL --- Inserted (unsigned long) before first argument in all calls to uvar_put and ulong_put. */ # include <math.h> # include <stdio.h> # include <setjmp.h> # include <varargs.h> # include "shorten.h" #ifdef unix char *readmode = "r"; char *writemode = "w"; #else char *readmode = "rb"; char *writemode = "wb"; #endif char *argv0 = "shorten"; int getc_exit_val; # define ALPHA0 (3.0 / 2.0) # define ALPHA1 M_LN2 /* SAL --- put (unsigned long) into macro */ # define UINT_PUT(val, nbit, file) \ if(version == 0) uvar_put((unsigned long) val, nbit, file); \ else ulong_put((unsigned long) val, file) # define UINT_GET(nbit, file) \ ((version == 0) ? uvar_get(nbit, file) : ulong_get(file)) /* SAL --- put (unsigned long) into macro */ # define VAR_PUT(val, nbit, file) \ if(version == 0) var_put((unsigned long) val, nbit - 1, file); \ else var_put((unsigned long) val, nbit, file) int init_offset(offset, nchan, nblock, ftype) long **offset; int nchan, nblock, ftype; { long mean = 0; int chan, i; /* initialise offset */ switch(ftype) { case TYPE_AU: case TYPE_S8: case TYPE_S16HL: case TYPE_S16LH: case TYPE_ULAW: mean = 0; break; case TYPE_U8: mean = 0x80; break; case TYPE_U16HL: case TYPE_U16LH: mean = 0x8000; break; default: update_exit(1, "unknown file type: %d\n", ftype); } for(chan = 0; chan < nchan; chan++) for(i = 0; i < nblock; i++) offset[chan][i] = mean; return(mean); } int shorten(stdi, stdo, argc, argv) FILE *stdi, *stdo; int argc; char **argv; { long **buffer, *buffer1, **offset; long default_offset; int version = FORMAT_VERSION, extract = 0, bitshift = 0; int hiloint = 1, hilo = !(*((char*) &hiloint)); int ftype = hilo ? TYPE_S16HL : TYPE_S16LH; char *magic = MAGIC, *filenamei = NULL, *filenameo = NULL; char *tmpfilename = NULL, *minusstr = "-", *filesuffix = ".shn"; FILE *filei, *fileo; int blocksize = DEFAULT_BLOCK_SIZE, nchan = DEFAULT_NCHAN; int i, chan, nwrap, nskip = DEFAULT_NSKIP, ndiscard = DEFAULT_NDISCARD; int *qlpc = NULL, maxnlpc = DEFAULT_MAXNLPC, nmean = DEFAULT_NMEAN; int maxresn = DEFAULT_MAXBITRATE, quanterror = DEFAULT_QUANTERROR; int nfilename; extern char *hs_optarg; extern int hs_optind; /* this block just processes the command line arguments */ { int c; hs_resetopt(); while((c = hs_getopt(argc, argv, "a:b:c:d:hm:p:q:r:t:xv:")) != -1) switch(c) { case 'a': if((nskip = atoi(hs_optarg)) < 0) usage_exit(1, "number of bytes to copy must be positive\n"); break; case 'b': if((blocksize = atoi(hs_optarg)) <= 0) usage_exit(1, "block size must be greater than zero\n"); break; case 'c': if((nchan = atoi(hs_optarg)) <= 0) usage_exit(1, "number of channels must be greater than zero\n"); break; case 'd': if((ndiscard = atoi(hs_optarg)) < 0) usage_exit(1, "number of bytes to discard must be positive\n"); break; case 'h': printf("%s: version %d.%s\n",argv0,FORMAT_VERSION,BUGFIX_RELEASE); printf("usage: %s [-hx] [-a #byte] [-b #sample] [-c #channel] [-d #discard]\n\t[-m #block] [-p #delay] [-q #bit] [-r #bit] [-t filetype]\n\t[input file] [output file]\n", argv0); printf("\t-a %d\tbytes to copy verbatim to align file\n", DEFAULT_NSKIP); printf("\t-b %d\tblock size\n", DEFAULT_BLOCK_SIZE); printf("\t-c %d\tnumber of channels\n", DEFAULT_NCHAN); printf("\t-d %d\tbytes to discard before compression or decompression\n", DEFAULT_NDISCARD); printf("\t-h\thelp (this message)\n"); printf("\t-m %d\tnumber of past block for mean estimation\n", DEFAULT_NMEAN); printf("\t-p %d\tmaximum LPC predictor order (0 == fast polynomial predictor)\n", DEFAULT_MAXNLPC); printf("\t-q %d\tacceptable quantisation error in bits\n", DEFAULT_QUANTERROR); printf("\t-r %d\tmaximum number of coded bits per sample\n", DEFAULT_MAXBITRATE); printf("\t-t s16\tfiletype {au,ulaw,{s,u}{8,16,16x,16hl,16lh}}\n"); printf("\t-v %d\tformat version number\n", FORMAT_VERSION); printf("\t-x\textract (all other options except -d are ignored)\n"); basic_exit(0); break; case 'm': if((nmean = atoi(hs_optarg)) < 0) usage_exit(1, "number of blocks for mean estimation must be positive\n"); break; case 'p': maxnlpc = atoi(hs_optarg); if(maxnlpc < 0 || maxnlpc > MAX_LPC_ORDER) usage_exit(1, "linear prediction order must be in the range 0 ... %d\n", MAX_LPC_ORDER); break; case 'q': if((quanterror = atoi(hs_optarg)) < 0) usage_exit(1, "quantisation level must be positive\n"); break; case 'r': if((maxresn = atoi(hs_optarg) - 3) < 0) usage_exit(1, "expected bit rate must be >= 3\n"); break; case 't': if (!strcmp(hs_optarg, "au")) ftype = TYPE_AU; else if(!strcmp(hs_optarg, "s8")) ftype = TYPE_S8; else if(!strcmp(hs_optarg, "u8")) ftype = TYPE_U8; else if(!strcmp(hs_optarg, "s16")) ftype = hilo?TYPE_S16HL:TYPE_S16LH; else if(!strcmp(hs_optarg, "u16")) ftype = hilo?TYPE_U16HL:TYPE_U16LH; else if(!strcmp(hs_optarg, "s16x")) ftype = hilo?TYPE_S16LH:TYPE_S16HL; else if(!strcmp(hs_optarg, "u16x")) ftype = hilo?TYPE_U16LH:TYPE_U16HL; else if(!strcmp(hs_optarg, "s16hl"))ftype = TYPE_S16HL; else if(!strcmp(hs_optarg, "u16hl"))ftype = TYPE_U16HL; else if(!strcmp(hs_optarg, "s16lh"))ftype = TYPE_S16LH; else if(!strcmp(hs_optarg, "u16lh"))ftype = TYPE_U16LH; else if(!strcmp(hs_optarg, "ulaw")) ftype = TYPE_ULAW; else usage_exit(1, "unknown file type: %s\n", hs_optarg); break; case 'v': version = atoi(hs_optarg); if(version < 0 || version > FORMAT_VERSION) usage_exit(1, "currently supported versions are in the range 0 ... %d\n", FORMAT_VERSION); break; case 'x': extract = 1; break; default: usage_exit(1, "unknown option: -%c\n", c); break; } } if(maxnlpc >= blocksize) usage_exit(1, "the predictor order must be less than the block size\n"); if(ftype == TYPE_AU && (maxresn != DEFAULT_MAXBITRATE || quanterror != DEFAULT_QUANTERROR)) usage_exit(1, "lossy compression currently not supported for file type au\n"); if(nmean != 0 && (maxresn != DEFAULT_MAXBITRATE || quanterror != DEFAULT_QUANTERROR)) usage_exit(1, "mean estimation currently not supported for lossy compression\n"); /* this chunk just sets up the input and output files */ nfilename = argc - hs_optind; switch(nfilename) { case 0: filenamei = minusstr; filenameo = minusstr; break; case 1: { int oldfilelen, suffixlen, maxlen; filenamei = argv[argc - 1]; oldfilelen = strlen(filenamei); suffixlen = strlen(filesuffix); maxlen = oldfilelen + suffixlen; tmpfilename = pmalloc((ulong) (maxlen + 1)); strcpy(tmpfilename, filenamei); if(extract) { int newfilelen = oldfilelen - suffixlen; if(strcmp(filenamei + newfilelen, filesuffix)) usage_exit(1,"file name does not end in %s: %s\n", filesuffix, filenamei); tmpfilename[newfilelen] = '\0'; } else strcat(tmpfilename, filesuffix); filenameo = tmpfilename; break; } case 2: filenamei = argv[argc - 2]; filenameo = argv[argc - 1]; break; default: usage_exit(1, NULL); } if(strcmp(filenamei, minusstr)) { if((filei = fopen(filenamei, readmode)) == NULL) perror_exit("fopen(\"%s\", \"%s\")", filenamei, readmode); } else filei = stdi; if(strcmp(filenameo, minusstr)) { if((fileo = fopen(filenameo, writemode)) == NULL) perror_exit("fopen(\"%s\", \"%s\")", filenameo, writemode); } else fileo = stdo; /* discard header on input file - can't rely on fseek() here */ if(ndiscard != 0) { char discardbuf[BUFSIZ]; for(i = 0; i < ndiscard / BUFSIZ; i++) if(fread(discardbuf, BUFSIZ, 1, filei) != 1) usage_exit(1, "EOF on input when discarding header\n"); if(ndiscard % BUFSIZ != 0) if(fread(discardbuf, ndiscard % BUFSIZ, 1, filei) != 1) usage_exit(1, "EOF on input when discarding header\n"); } if(!extract) { float alpha; int nread, nscan = 0; nwrap = MAX(NWRAP, maxnlpc); /* grab some space for the input buffers */ buffer = long2d((ulong) nchan, (ulong) (blocksize + nwrap)); buffer1 = (long*) pmalloc((ulong) (blocksize * sizeof(*buffer1))); offset = long2d((ulong) nchan, (ulong) nmean); for(chan = 0; chan < nchan; chan++) { for(i = 0; i < nwrap; i++) buffer[chan][i] = 0; buffer[chan] += nwrap; } if(maxnlpc > 0) qlpc = (int*) pmalloc((ulong) (maxnlpc * sizeof(*qlpc))); default_offset = init_offset(offset, nchan, nmean, ftype); /* verbatim copy of skip bytes from input to output checking for the existence of magic number in header, and defaulting to internal storage if that happens */ if(version >= 2) { while(nskip - nscan > 0 && magic[nscan] != '\0') { int byte = getc_exit(filei); if(byte == magic[nscan]) nscan++; else { for(i = 0; i < nscan; i++) putc_exit(magic[i], fileo); if(byte == magic[0]) { nskip -= nscan; nscan = 1; } else { putc_exit(byte, fileo); nskip -= nscan + 1; nscan = 0; } } } if(magic[nscan] != '\0') { for(i = 0; i < nscan; i++) putc_exit(magic[i], fileo); nskip -= nscan; nscan = 0; } } /* write magic number */ if(fwrite(magic, strlen(magic), 1, fileo) != 1) usage_exit(1, "could not write the magic number\n"); /* write version number */ putc_exit(version, fileo); /* initialise the fixed length file read for the uncompressed stream */ fread_type_init(); /* initialise the variable length file write for the compressed stream */ var_put_init(fileo); /* put file type and number of channels */ UINT_PUT(ftype, TYPESIZE, fileo); UINT_PUT(nchan, CHANSIZE, fileo); /* put blocksize if version > 0 */ if(version == 0) { alpha = ALPHA0; if(blocksize != DEFAULT_BLOCK_SIZE) { uvar_put((ulong) FN_BLOCKSIZE, FNSIZE, fileo); UINT_PUT(blocksize, (int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2), fileo); } } else { alpha = ALPHA1; UINT_PUT(blocksize, (int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2), fileo); UINT_PUT(maxnlpc, LPCQSIZE, fileo); UINT_PUT(nmean, 0, fileo); UINT_PUT(nskip, NSKIPSIZE, fileo); for(i = 0; i < nscan; i++) uvar_put((ulong) magic[i], XBYTESIZE, fileo); for(i = 0; i < nskip - nscan; i++) { int byte = getc_exit(filei); uvar_put((ulong) byte, XBYTESIZE, fileo); } } while((nread = fread_type(buffer, ftype, nchan, blocksize, filei)) != 0) { /* put blocksize if changed */ if(nread != blocksize) { uvar_put((ulong) FN_BLOCKSIZE, FNSIZE, fileo); UINT_PUT(nread, (int) (log((double) blocksize) / M_LN2), fileo); blocksize = nread; } for(chan = 0; chan < nchan; chan++) { long coffset, *cbuffer = buffer[chan]; long sum, fnd; int resn, nlpc, newbitshift; /* force the lower quanterror bits to be zero */ if(quanterror != 0) { int offset = (1 << (quanterror - 1)); for(i = 0; i < blocksize; i++) cbuffer[i] = (cbuffer[i] + offset) >> quanterror; } /* test for excessive and exploitable quantisation, and exploit!! */ newbitshift = find_bitshift(cbuffer, blocksize, ftype) + quanterror; if(newbitshift > NBITPERLONG) newbitshift = NBITPERLONG; /* deduct mean if appropriate */ if(nmean == 0) coffset = default_offset; else { sum = 0; for(i = 0; i < nmean; i++) sum += offset[chan][i]; coffset = sum / nmean; sum = 0; for(i = 0; i < blocksize; i++) sum += cbuffer[i]; for(i = 1; i < nmean; i++) offset[chan][i - 1] = offset[chan][i]; offset[chan][nmean - 1] = sum / blocksize; } if(coffset != 0) for(i = -nwrap; i < blocksize; i++) cbuffer[i] -= coffset; /* find the best model */ if(newbitshift == NBITPERLONG && version >= 2) { fnd = FN_ZERO; } else if(maxnlpc == 0) { long sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0, last0, last1, last2; last2 = (last1 = (last0 = cbuffer[-1]) - cbuffer[-2]) - (cbuffer[-2] -cbuffer[-3]); for(i = 0; i < blocksize; i++) { long diff0, diff1, diff2, diff3; sum0 += abs(diff0 = cbuffer[i]); sum1 += abs(diff1 = diff0 - last0); sum2 += abs(diff2 = diff1 - last1); sum3 += abs(diff3 = diff2 - last2); last0 = diff0; last1 = diff1; last2 = diff2; } if(sum0 < MIN(MIN(sum1, sum2), sum3)) { sum = sum0; fnd = FN_DIFF0; } else if(sum1 < MIN(sum2, sum3)) { sum = sum1; fnd = FN_DIFF1; } else if(sum2 < sum3) { sum = sum2; fnd = FN_DIFF2; } else { sum = sum3; fnd = FN_DIFF3; } if(alpha * sum < blocksize) resn = 0; else resn = floor(log(alpha * sum / (double) blocksize)/M_LN2 + 0.5); } else nlpc = wav2lpc(cbuffer, blocksize, qlpc, maxnlpc, &resn); if(resn > maxresn) { int extrabitshift = resn - maxresn; int offset = (1 << (extrabitshift - 1)); for(i = 0; i < blocksize; i++) cbuffer[i] = (cbuffer[i] + offset) >> extrabitshift; resn = maxresn; newbitshift += extrabitshift; } if(newbitshift != bitshift) { uvar_put((ulong) FN_BITSHIFT, FNSIZE, fileo); uvar_put((ulong) newbitshift, BITSHIFTSIZE, fileo); bitshift = newbitshift; } if(newbitshift == NBITPERLONG && version >= 2) { uvar_put(fnd, FNSIZE, fileo); } else if(maxnlpc == 0) { uvar_put(fnd, FNSIZE, fileo); uvar_put((ulong) resn, ENERGYSIZE, fileo); switch(fnd) { case FN_DIFF0: for(i = 0; i < blocksize; i++) VAR_PUT(cbuffer[i], resn, fileo); break; case FN_DIFF1: for(i = 0; i < blocksize; i++) VAR_PUT(cbuffer[i] - cbuffer[i - 1], resn, fileo); break; case FN_DIFF2: for(i = 0; i < blocksize; i++) VAR_PUT(cbuffer[i] - 2 * cbuffer[i - 1] + cbuffer[i - 2], resn, fileo); break; case FN_DIFF3: for(i = 0; i < blocksize; i++) VAR_PUT(cbuffer[i] - 3 * (cbuffer[i - 1] - cbuffer[i - 2]) - cbuffer[i - 3], resn, fileo); break; } #ifdef DEBUG_STATS_FOR_ICASSP_PAPER /* print stats */ if(resn == 2) switch(fnd) { case FN_DIFF0: for(i = 0; i < blocksize; i++) printf("%d\n", cbuffer[i]); break; case FN_DIFF1: for(i = 0; i < blocksize; i++) printf("%d\n", (cbuffer[i] - cbuffer[i - 1])); break; case FN_DIFF2: for(i = 0; i < blocksize; i++) printf("%d\n", (cbuffer[i] - 2 * cbuffer[i - 1] + cbuffer[i - 2])); break; case FN_DIFF3: for(i = 0; i < blocksize; i++) printf("%d\n", (cbuffer[i] - 3 * cbuffer[i - 1] + 3 * cbuffer[i - 2] - cbuffer[i - 3])); break; } #endif /* DEBUG_STATS */ } else { uvar_put((ulong) FN_QLPC, FNSIZE, fileo); uvar_put((ulong) resn, ENERGYSIZE, fileo); uvar_put((ulong) nlpc, LPCQSIZE, fileo); for(i = 0; i < nlpc; i++) var_put((long) qlpc[i], LPCQUANT, fileo); /* use the quantised LPC coefficients to generate the residual */ for(i = 0; i < blocksize; i++) { int j; long sum = 0; for(j = 0; j < nlpc; j++) sum += qlpc[j] * cbuffer[i - j - 1]; var_put(cbuffer[i] - (sum >> LPCQUANT), resn, fileo); /*ICASSP printf("%f\n", (float) (cbuffer[i] - (sum >> LPCQUANT)) / ( 1 << resn)); */ } } /* do the wrap */ for(i = -nwrap; i < 0; i++) cbuffer[i] = cbuffer[i + blocksize] + coffset; } } /* wind up */ fread_type_quit(); uvar_put((ulong) FN_QUIT, FNSIZE, fileo); var_put_quit(fileo); /* and free the space used */ free((char*) buffer); free((char*) buffer1); free((char*) offset); if(maxnlpc > 0) free((char*) qlpc); } else { /***********************/ /* EXTRACT starts here */ /***********************/ int i, cmd; /* read magic number */ if(FORMAT_VERSION < 2) { for(i = 0; i < strlen(magic); i++) if(getc_exit(filei) != magic[i]) usage_exit(1, "Bad magic number\n"); } else { int nscan = 0; while(magic[nscan] != '\0') { int byte = getc(filei); if(byte == EOF) usage_exit(1, "No magic number\n"); if(byte == magic[nscan]) nscan++; else { for(i = 0; i < nscan; i++) putc_exit(magic[i], fileo); if(byte == magic[0]) nscan = 1; else { putc_exit(byte, fileo); nscan = 0; } } } } /* get and check version number */ version = getc_exit(filei); if(version > FORMAT_VERSION) update_exit(1, "can't decode version %d\n", version); /* initialise the variable length file read for the compressed stream */ var_get_init(filei); /* initialise the fixed length file write for the uncompressed stream */ fwrite_type_init(); /* get file type and set up appropriately, ignoring command line state */ ftype = UINT_GET(TYPESIZE, filei); if(ftype >= TYPE_EOF) update_exit(1, "can't decode file type %d\n", ftype); nchan = UINT_GET(CHANSIZE, filei); /* get blocksize if version > 0 */ if(version > 0) { blocksize = UINT_GET((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2), filei); maxnlpc = UINT_GET(LPCQSIZE, filei); nmean = UINT_GET(0, fileo); nskip = UINT_GET(NSKIPSIZE, filei); for(i = 0; i < nskip; i++) { int byte = uvar_get(XBYTESIZE, filei); putc_exit(byte, fileo); } } else blocksize = DEFAULT_BLOCK_SIZE; nwrap = MAX(NWRAP, maxnlpc); /* grab some space for the input buffer */ buffer = long2d((ulong) nchan, (ulong) (blocksize + nwrap)); offset = long2d((ulong) nchan, (ulong) nmean); for(chan = 0; chan < nchan; chan++) { for(i = 0; i < nwrap; i++) buffer[chan][i] = 0; buffer[chan] += nwrap; } if(maxnlpc > 0) qlpc = (int*) pmalloc((ulong) (maxnlpc * sizeof(*qlpc))); default_offset = init_offset(offset, nchan, nmean, ftype); /* get commands from file and execute them */ chan = 0; while((cmd = uvar_get(FNSIZE, filei)) != FN_QUIT) switch(cmd) { case FN_ZERO: case FN_DIFF0: case FN_DIFF1: case FN_DIFF2: case FN_DIFF3: case FN_QLPC: { long coffset, *cbuffer = buffer[chan]; int resn, nlpc, j; if(cmd != FN_ZERO) { resn = uvar_get(ENERGYSIZE, filei); /* this is a hack as version 0 differed in definition of var_get */ if(version == 0) resn--; } /* find offset */ if(nmean == 0) coffset = default_offset; else { long sum = 0; for(i = 0; i < nmean; i++) sum += offset[chan][i]; coffset = sum / nmean; } switch(cmd) { case FN_ZERO: for(i = 0; i < blocksize; i++) cbuffer[i] = 0; break; case FN_DIFF0: for(i = 0; i < blocksize; i++) cbuffer[i] = var_get(resn, filei) + coffset; break; case FN_DIFF1: for(i = 0; i < blocksize; i++) cbuffer[i] = var_get(resn, filei) + cbuffer[i - 1]; break; case FN_DIFF2: for(i = 0; i < blocksize; i++) cbuffer[i] = var_get(resn, filei) + (2 * cbuffer[i - 1] - cbuffer[i - 2]); break; case FN_DIFF3: for(i = 0; i < blocksize; i++) cbuffer[i] = var_get(resn, filei) + 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3]; break; case FN_QLPC: nlpc = uvar_get(LPCQSIZE, filei); for(i = 0; i < nlpc; i++) qlpc[i] = var_get(LPCQUANT, filei); for(i = 0; i < nlpc; i++) cbuffer[i - nlpc] -= coffset; for(i = 0; i < blocksize; i++) { long sum = 0; for(j = 0; j < nlpc; j++) sum += qlpc[j] * cbuffer[i - j - 1]; cbuffer[i] = var_get(resn, filei) + (sum >> LPCQUANT); } if(coffset != 0) for(i = 0; i < blocksize; i++) cbuffer[i] += coffset; break; } /* store mean value if appropriate */ if(nmean > 0) { long sum = 0; for(i = 1; i < nmean; i++) offset[chan][i - 1] = offset[chan][i]; for(i = 0; i < blocksize; i++) sum += cbuffer[i]; offset[chan][nmean - 1] = sum / blocksize; } /* do the wrap */ for(i = -nwrap; i < 0; i++) cbuffer[i] = cbuffer[i + blocksize]; fix_bitshift(cbuffer, blocksize, bitshift, ftype); if(chan == nchan - 1) fwrite_type(buffer, ftype, nchan, blocksize, fileo); chan = (chan + 1) % nchan; } break; case FN_BLOCKSIZE: blocksize = UINT_GET((int) (log((double) blocksize) / M_LN2), filei); break; case FN_BITSHIFT: bitshift = uvar_get(BITSHIFTSIZE, filei); break; default: update_exit(1, "sanity check fails trying to decode function: %d\n", cmd); } /* wind up */ var_get_quit(filei); fwrite_type_quit(); free((char*) buffer); free((char*) offset); if(maxnlpc > 0) free((char*) qlpc); } /* close the files */ if(filei != stdi) fclose(filei); if(fileo != stdo) fclose(fileo); /* make the compressed file look like the original if possible */ if((filei != stdi) && (fileo != stdo)) (void) dupfileinfo(filenamei, filenameo); if(nfilename == 1) if(unlink(filenamei)) perror_exit("unlink(\"%s\")", filenamei); if(tmpfilename != NULL) free(tmpfilename); /* quit happy */ return(0); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.