1 /* kexis.c
2 * Kexis is a lossless wav (PCM) sound compressor.
3 * Copyright (C) 2000 Wayde Milas (wmilas@rarcoa.com)
4 *
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/resource.h>
24 #include <unistd.h>
25 #include "types.h"
26 #include "kexis.h"
27 #include "encode.h"
28 #include "decode.h"
29 #include "rice.h"
30
useage(void)31 void useage(void)
32 {
33 printf("Useage:\n");
34 printf("kexis [MODE] [OPTION]... FILE\n\n");
35 printf("Mode:\n");
36 printf(" -c Compress a WAV file. This is the default if no mode is given.\n");
37 printf(" A WAV file is the input and a KEX file is the output.\n");
38 printf(" -x Extract a KEX file to a WAV.\n");
39 printf(" -s Extract a KEX file to stdout.\n\n");
40 printf("Options:\n");
41 printf(" -p Display a progress bar and ending statistics during\n");
42 printf(" compression/extraction.\n");
43 printf(" Default is to display NO data whatsoever.\n");
44 printf(" -h Display full WAV header if in Compress Mode.\n");
45 printf(" Display full KEXIS header if in Uncompress (Extract) Mode.\n");
46 printf(" -v Display Verbose performance/debugging data during\n");
47 printf(" procesing. Enabling this also assumes and enables -p.\n");
48 printf(" -d When encoding, delete the WAV file after the encode is\n");
49 printf(" finished.\n");
50 printf(" -ns Do NOT use similarities between stereo channels when\n");
51 printf(" encoding. Note: This almost always results in worse\n");
52 printf(" compression, so only use it if you know you need it.\n");
53 printf("\n");
54 printf("Predictors when encoding:\n");
55 printf(" -pr # Where # is:\n");
56 printf(" 0 Use Zero order predictors.\n");
57 printf(" 1 Use First order predictors.\n");
58 printf(" 2 Use Second order predictors.\n");
59 printf(" 3 Use Third order predictors.\n");
60 printf(" 4 Use predictive Predictors. The predictor uses the best\n");
61 printf(" order predictors based on the LAST values.\n");
62 printf(" 5 Break the data stream down into frames, preprocess, and\n");
63 printf(" use the best order predictors for that frame. Default.\n");
64 printf(" -fr # Where # is the frame size you want to use. Default 1024.\n");
65 printf(" There is no \"right\" number. Optimum values differ per\n");
66 printf(" individual file.\n");
67 printf("\n");
68 printf("Encoders used to compress:\n");
69 printf(" -en # Where # is:\n");
70 printf(" 1 Single K Pseudo Rice encoding.\n");
71 printf(" 2 Dual K Pseudo Rice encoding.\n");
72 printf(" 3 Dual K Pseudo Rice encoding with Variable History.\n");
73 printf(" Defaiult. The Variable history size is setable via -kh\n");
74 printf(" -kh # Where # is the K history size you want to use. Default 32.\n");
75 printf(" There is no \"right\" number. Optimum values differ per\n");
76 printf(" individual file.\n");
77 printf("\n");
78 return;
79 }
80
display_copyright(void)81 void display_copyright(void)
82 {
83 printf("Kexis version %d.%d.%d, Copyright (C) 2000 Wayde Milas ",
84 VERSION_MAJOR, VERSION_MINOR, VERSION_SUB_MINOR);
85 printf("(wmilas@rarcoa.com)\n");
86 printf("Kexis comes with ABSOLUTELY NO WARRANTY; for details see included ");
87 printf("GPL license.\n");
88 printf("This is free software, and you are welcome to redistribute it ");
89 printf("under certain\nconditions; for details see included GPL license.");
90 printf("\n");
91 }
92
parse_arg(OPTIONSTRUCT * options,int argc,char * argv[])93 int parse_arg(OPTIONSTRUCT *options, int argc, char *argv[])
94 {
95 int loop =1;
96
97 if (argc == 1) {
98 display_copyright();
99 useage();
100 return 1;
101 }
102
103 // The default Mode
104 options->inFileStream=NULL;
105 options->outFileStream=NULL;
106 options->inFileName=NULL;
107 options->outFileName=NULL;
108 options->mode = COMPRESS;
109 options->progress = 0;
110 options->displayHeader = 0;
111 options->dec_stdout = 0;
112 options->verbose = 0;
113 options->del_file = 0;
114 options->argPosition = 1;
115 options->predictorVersion = STANDARD_PREDICTOR;
116 options->encoderVersion = STANDARD_ENCODER;
117 options->kHistorySize = DEFAULT_KHISTORY;
118 options->frameSize = FRAME_UNITS;
119 options->joint = 1;
120
121 for(loop=1;loop < argc;loop++) {
122
123 if(strcmp("-c", argv[loop]) == 0)
124 options->mode = COMPRESS;
125 else if(strcmp("-d", argv[loop]) == 0)
126 options->del_file = 1;
127 else if(strcmp("-x", argv[loop]) == 0)
128 options->mode = DECOMPRESS;
129 else if(strcmp("-s", argv[loop]) == 0) {
130 options->mode = DECOMPRESS;
131 options->dec_stdout = 1;
132 }
133 else if(strcmp("-pr", argv[loop]) == 0) {
134 loop++;
135 sscanf(argv[loop], "%hd", &options->predictorVersion);
136 options->predictorVersion++;
137 if(options->predictorVersion < MIN_STANDARD_PREDICTOR ||
138 options->predictorVersion > MAX_STANDARD_PREDICTOR) {
139 printf("Predictors must be between %d and %d!\n",
140 MIN_STANDARD_PREDICTOR-1, MAX_STANDARD_PREDICTOR-1);
141 display_copyright();
142 useage();
143 return -1;
144 }
145 }
146 else if(strcmp("-fr", argv[loop]) == 0) {
147 loop++;
148 sscanf(argv[loop], "%ld", &options->frameSize);
149 if(options->frameSize < 64){
150 printf("Frames must be at least 64 long!\n");
151 display_copyright();
152 useage();
153 return -1;
154 }
155 }
156 else if(strcmp("-en", argv[loop]) == 0) {
157 loop++;
158 sscanf(argv[loop], "%hd", &options->encoderVersion);
159 if(options->encoderVersion < MIN_STANDARD_ENCODER ||
160 options->encoderVersion > MAX_STANDARD_ENCODER) {
161 printf("Encoders must be between %d and %d!\n",
162 MIN_STANDARD_ENCODER, MAX_STANDARD_ENCODER);
163 display_copyright();
164 useage();
165 return -1;
166 }
167 }
168 else if(strcmp("-kh", argv[loop]) == 0) {
169 loop++;
170 sscanf(argv[loop], "%d", &options->kHistorySize);
171 if(options->kHistorySize < 1 || options->kHistorySize > MAX_KHISTORY){
172 printf("K history Size must be between 1 and %d\n",MAX_KHISTORY);
173 display_copyright();
174 useage();
175 return -1;
176 }
177 }
178 else if(strcmp("-ns", argv[loop]) == 0)
179 options->joint = 0;
180 else if(strcmp("-p", argv[loop]) == 0)
181 options->progress = 1;
182 else if(strcmp("-h", argv[loop]) == 0)
183 options->displayHeader = 1;
184 else if(strcmp("-v", argv[loop]) == 0) {
185 options->progress = 1;
186 options->verbose = 1;
187 }
188 else
189 break;
190 }
191
192 if(loop == argc) {
193 display_copyright();
194 useage();
195 return -1;
196 }
197
198 options->argPosition = loop;
199 return 0;
200 }
201
parse_files(OPTIONSTRUCT * options,int argc,char * argv[])202 int parse_files(OPTIONSTRUCT *options, int argc, char *argv[])
203 {
204 char *ext=NULL;
205
206 // Check and see if we are in compress mode that the file ends in .wav
207 // and .kex is decompress mode
208 ext = strrchr(argv[options->argPosition], '.');
209 if(ext != NULL) {
210 if(options->mode == COMPRESS) {
211 if(strcmp(ext, ".wav") != 0) {
212 printf("Kexis can only compress files that end in a .wav!\n");
213 return -1;
214 }
215 }
216 else {
217 if(strcmp(ext, ".kxs") != 0) {
218 printf("Kexis can only uncompress files that end in a .kxs!\n");
219 return -1;
220 }
221 }
222 }
223 else {
224 if(options->mode == COMPRESS)
225 printf("Kexis can only compress files that end in a .wav!\n");
226 else
227 printf("Kexis can only uncompress files that end in a .kxs!\n");
228 return -1;
229 }
230
231 options->inFileName = calloc(1, strlen(argv[options->argPosition])+2);
232 strcpy(options->inFileName, argv[options->argPosition]);
233
234 return 0;
235 }
236
print_progress(KEXISBLOCKSTRUCT * kexisBlock,unsigned long pcmLength,OPTIONSTRUCT * options,unsigned long totalLength,int mode)237 void print_progress(KEXISBLOCKSTRUCT *kexisBlock, unsigned long pcmLength,
238 OPTIONSTRUCT *options, unsigned long totalLength, int mode)
239 {
240 struct timeval currentTime;
241 PREDICTORTABLE *pred;
242
243 gettimeofday(¤tTime,0);
244 if((currentTime.tv_sec - options->progTime.tv_sec > 0) ||
245 (currentTime.tv_usec - options->progTime.tv_usec > 250000)) {
246
247 options->progTime.tv_sec = currentTime.tv_sec;
248 options->progTime.tv_usec = currentTime.tv_usec;
249
250 pred = &kexisBlock->predictor;
251 if(mode == COMPRESS)
252 printf("Progress:%6.2f%% ",
253 100.0-(((float)pcmLength*2.0)/(float)totalLength)*100);
254 else
255 printf("Progress:%6.2f%% ",
256 (((float)pcmLength*2.0)/(float)totalLength)*100);
257
258 printf("Time: %ld:%.2ld ", (currentTime.tv_sec - options->time.tv_sec)/60,
259 (currentTime.tv_sec - options->time.tv_sec)%60);
260 if(options->encoderVersion == RICE_SINGLE_K)
261 if(mode == COMPRESS)
262 printf("K:%3d Pd(mid,ave):%6d,%6d\r", pred->mid_K[0],
263 pred->diffMid, pred->diffAve);
264 else
265 printf("K:%3d mid,ave:%6d,%6d\r", pred->mid_K[0],
266 pred->diffMid, pred->diffAve);
267 else if(options->encoderVersion == RICE_DUAL_K) {
268 printf("K(m,a):%3d,%3d ", pred->mid_K[0], pred->ave_K[0]);
269 if(mode == COMPRESS)
270 printf("Pd(m,a):%6d,%6d\r", pred->diffMid, pred->diffAve);
271 else
272 printf("mid,ave:%6d,%6d\r", pred->diffMid, pred->diffAve);
273 }
274 else if(options->encoderVersion == RICE_DUAL_K_BLOCK) {
275 printf("K(m,a):%3d,%3d ",
276 block_ave_K(pred->mid_K, options->kHistorySize),
277 block_ave_K(pred->ave_K, options->kHistorySize));
278 if(mode == COMPRESS)
279 printf("Pd(m,a):%6d,%6d\r", pred->diffMid, pred->diffAve);
280 else
281 printf("mid,ave:%6d,%6d\r", pred->diffMid, pred->diffAve);
282 }
283 fflush(stdout);
284 }
285 }
286
main(int argc,char * argv[])287 int main(int argc, char *argv[])
288 {
289 WAVHEADER wavHead;
290 KEXISHEADER kexisHead;
291 OPTIONSTRUCT options;
292 struct timeval currentTime;
293
294 gettimeofday(&options.time,0);
295 gettimeofday(&options.progTime,0);
296 if(parse_arg(&options, argc, argv))
297 exit(0);
298
299 if((options.progress || options.displayHeader) && !options.dec_stdout)
300 display_copyright();
301
302 while(options.argPosition < argc) {
303 if(parse_files(&options, argc, argv))
304 exit(0);
305
306 if(options.mode == COMPRESS)
307 compress(&wavHead, &options);
308
309 if(options.mode == DECOMPRESS)
310 decompress(&kexisHead, &options);
311
312 if(options.progress && !options.dec_stdout) {
313 gettimeofday(¤tTime,0);
314 printf("\nTotal elapsed time: %ld:%.2ld\n",
315 (currentTime.tv_sec - options.time.tv_sec)/60,
316 (currentTime.tv_sec - options.time.tv_sec)%60);
317 }
318
319 options.argPosition++;
320 }
321
322 exit_nicely(&options);
323 exit(0);
324 }
325
handle_verbose(OPTIONSTRUCT * options,KEXISBLOCKSTRUCT * kexisBlock,PCMBLOCKSTRUCT * pcmBlock)326 void handle_verbose(OPTIONSTRUCT *options, KEXISBLOCKSTRUCT *kexisBlock,
327 PCMBLOCKSTRUCT *pcmBlock)
328 {
329 struct rusage usage;
330
331 if(options->progress && options->mode==COMPRESS && !options->dec_stdout)
332 printf("\nCompression:%6.2f%% (of original file size)",
333 (float) 100.0 *
334 ((float)(kexisBlock->sumTotalSize*2)/
335 ((float)pcmBlock->pcmStreamLength/2)));
336
337 if(options->verbose && !options->dec_stdout) {
338 getrusage(RUSAGE_SELF,&usage);
339 printf("\nTotal User Time : %ld seconds, %ld ms.\n",
340 usage.ru_utime.tv_sec, usage.ru_utime.tv_usec/1000);
341 printf("Total System Time: %ld seconds, %ld ms.\n",
342 usage.ru_stime.tv_sec, usage.ru_stime.tv_usec/1000);
343 printf("Low Pd mid: %d\tHigh Pd mid: %d\tAverage Pd mid: %9.3f\n",
344 kexisBlock->predictor.lowDiffMid,
345 kexisBlock->predictor.highDiffMid,
346 kexisBlock->predictor.sumDiffMid/kexisBlock->predictor.countDiffMid);
347 printf("Low Pd ave: %d\tHigh Pd ave: %d\tAverage Pd ave: %9.3f\n",
348 kexisBlock->predictor.lowDiffAve,
349 kexisBlock->predictor.highDiffAve,
350 kexisBlock->predictor.sumDiffAve/
351 kexisBlock->predictor.countDiffAve);
352 if(options->encoderVersion == RICE_SINGLE_K)
353 printf("Low K : %d\t\tHigh K : %d\t\tAverage K : %9.3f",
354 kexisBlock->predictor.midLowK, kexisBlock->predictor.midHighK,
355 kexisBlock->predictor.midSumK/kexisBlock->predictor.midCountK);
356 else if(options->encoderVersion == RICE_DUAL_K ||
357 options->encoderVersion == RICE_DUAL_K_BLOCK) {
358 printf("Low K mid : %d\t\tHigh K mid : %d\t\tAverage K mid : %9.3f\n",
359 kexisBlock->predictor.midLowK, kexisBlock->predictor.midHighK,
360 kexisBlock->predictor.midSumK/kexisBlock->predictor.midCountK);
361 printf("Low K ave : %d\t\tHigh K ave : %d\t\tAverage K ave : %9.3f",
362 kexisBlock->predictor.aveLowK, kexisBlock->predictor.aveHighK,
363 kexisBlock->predictor.aveSumK/kexisBlock->predictor.aveCountK);
364 }
365 }
366 }
367
handle_error(OPTIONSTRUCT * options)368 void handle_error(OPTIONSTRUCT *options)
369 {
370 printf("\n%s\n", options->errorString);
371 printf("Was not able to finish. Exiting...\n");
372 exit_nicely(options);
373 }
374
exit_nicely(OPTIONSTRUCT * options)375 void exit_nicely(OPTIONSTRUCT *options)
376 {
377 exit(0);
378 }
379
free_allocated(PCMBLOCKSTRUCT * pcmBlock,KEXISBLOCKSTRUCT * kexisBlock,OPTIONSTRUCT * options)380 void free_allocated(PCMBLOCKSTRUCT *pcmBlock, KEXISBLOCKSTRUCT *kexisBlock,
381 OPTIONSTRUCT *options)
382 {
383 int hold;
384
385 // check and see if we need to delete an encoded file
386 if(options->mode == COMPRESS && options->del_file &&
387 options->inFileName != NULL) {
388 hold = unlink(options->inFileName);
389 if(hold == -1)
390 printf("Was not able to delete file %s.\n", options->inFileName);
391 }
392
393 if(options->inFileStream != NULL)
394 fclose(options->inFileStream);
395 if(options->outFileStream != NULL && !options->dec_stdout)
396 fclose(options->outFileStream);
397
398 if(options->inFileName != NULL)
399 free(options->inFileName);
400 if(options->outFileName != NULL && !options->dec_stdout)
401 free(options->outFileName);
402
403 if(kexisBlock->predictor.mid_K != NULL)
404 free(kexisBlock->predictor.mid_K);
405 if(kexisBlock->predictor.ave_K != NULL)
406 free(kexisBlock->predictor.ave_K);
407 if(kexisBlock->predictor.newKLookUP != NULL)
408 free(kexisBlock->predictor.newKLookUP);
409
410 if(kexisBlock->data != NULL)
411 free(kexisBlock->data);
412 if(pcmBlock->data != NULL)
413 free(pcmBlock->data);
414
415 options->inFileStream = NULL;
416 if(options->dec_stdout == 0)
417 options->outFileStream = NULL;
418 options->inFileName = NULL;
419 if(options->dec_stdout == 0)
420 options->outFileName = NULL;
421 kexisBlock->predictor.mid_K = NULL;
422 kexisBlock->predictor.ave_K = NULL;
423 kexisBlock->predictor.newKLookUP = NULL;
424 kexisBlock->data = NULL;
425 pcmBlock->data = NULL;
426 }
427