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(&currentTime,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(&currentTime,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