1 /* encode.c
2  * Handlers for encoding a wav stream into a kexis stream.
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 <math.h>
23 #include <string.h>
24 #include "types.h"
25 #include "encode.h"
26 #include "header.h"
27 #include "kexis.h"
28 #include "predictor.h"
29 #include "rice.h"
30 #include "bits.h"
31 
compress(WAVHEADER * wavHead,OPTIONSTRUCT * options)32 void compress(WAVHEADER *wavHead, OPTIONSTRUCT *options)
33 {
34   int length;
35 	unsigned long pcmLength=0;
36   KEXISBLOCKSTRUCT kexisBlock;
37 	PCMBLOCKSTRUCT pcmBlock;
38 
39   options->inFileStream = fopen(options->inFileName, "rb");
40   if(options->inFileStream == NULL) {
41     sprintf(options->errorString, "Can't open file: %s!",
42 			options->inFileName);
43     handle_error(options);
44   }
45 
46 	if(options->progress)
47 		printf("\nEncoding file: %s\n", options->inFileName);
48 
49   // Suck in the WaveHeader
50   parse_wave_header(wavHead, options);
51   pcmLength = wavHead->data_ckSize;
52 
53 	//Save the original size right now as a quick hack. Remove this later.
54 	pcmBlock.pcmStreamLength = wavHead->data_ckSize;
55 
56   // Since the Wav Header data length is in bytes, we need to convert it to
57   // "units". Each "unit" is 2 bytes, or a short int. so we devide by 2.
58   pcmLength = pcmLength / 2;
59 
60 	// If the frame size is LARGER than the pcmLength, make it the pcmLength
61 	if(options->frameSize > wavHead->data_ckSize)
62 		options->frameSize = wavHead->data_ckSize;
63 
64   // Here we malloc 200,000 units, or 400,000 bytes.
65   pcmBlock.data = malloc(sizeof(short)*options->frameSize);
66 	kexisBlock.data = malloc(sizeof(long)*(options->frameSize/2));
67 
68 	// Clear the Predictor Table
69 	zero_predictor_table(&kexisBlock.predictor, options);
70 	kexisBlock.bitCounter=0;
71 	kexisBlock.bitBucket=0;
72 	kexisBlock.dataPosition=0;
73 	kexisBlock.sumTotalSize=0;
74 	kexisBlock.debugBlockCount=0;
75 
76   while(pcmLength > 0) {
77 
78     if(options->progress)
79 			print_progress(&kexisBlock, pcmLength, options, wavHead->data_ckSize,
80 				COMPRESS);
81 
82     if(pcmLength < options->frameSize)
83       pcmBlock.size = pcmLength;
84     else
85       pcmBlock.size = options->frameSize;
86 
87     length = fread(pcmBlock.data, sizeof(short), pcmBlock.size,
88 			options->inFileStream);
89     if(length < pcmBlock.size) {
90       sprintf(options->errorString, "Error: read %d units out of %ld units.\n Error Reading PCM stream. Short Block count. Your WAV file is\n corrupt and is not as long as it should be.", length, pcmBlock.size);
91       handle_error(options);
92     }
93 
94     encode_pcmblock(&pcmBlock, &kexisBlock, options);
95     pcmLength = pcmLength - pcmBlock.size;
96   }
97 
98 	cleanup_bits(&kexisBlock, options, &pcmBlock);
99 	handle_verbose(options, &kexisBlock, &pcmBlock);
100 	free_allocated(&pcmBlock, &kexisBlock, options);
101 }
102 
encode_pcmblock(PCMBLOCKSTRUCT * pcmBlock,KEXISBLOCKSTRUCT * kexisBlock,OPTIONSTRUCT * options)103 void encode_pcmblock(PCMBLOCKSTRUCT *pcmBlock, KEXISBLOCKSTRUCT *kexisBlock,
104 	OPTIONSTRUCT *options)
105 {
106   long   left, right, mid, ave;
107   int     loop=0, holdM, holdA;
108 
109 	if(options->predictorVersion == FRAME_PREDICTOR) {
110 		precompute_predictor(kexisBlock, pcmBlock, &holdA, &holdM);
111 		push_bits(kexisBlock, holdA, 3, options, pcmBlock);
112 		push_bits(kexisBlock, holdM, 3, options, pcmBlock);
113 	}
114 
115   while(loop < pcmBlock->size) {
116     left = pcmBlock->data[loop];
117     right = pcmBlock->data[loop+1];
118 
119     // X = average of the 2 channels
120     // Y = distance from mid to right channel.
121     // Thats all we need to extract L and R later, plus hopefully
122     // Y is now a smaller number.
123 		if(options->joint == 1) {
124     	mid = left - right;
125     	ave = right + (mid/2);
126 		}
127 		else {
128 			mid = left;
129 			ave = right;
130 		}
131 
132 		// Now use our predictor to tranform X and Y into differences.
133 		if(options->predictorVersion == FRAME_PREDICTOR)
134 			frame_difference_calc(&kexisBlock->predictor, holdA, holdM,
135 				mid, ave, COMPRESS);
136 		else
137 			difference_calc(&kexisBlock->predictor, options->predictorVersion,
138 				mid, ave, COMPRESS);
139 
140 		// If we are gathering statistics, gather them :P
141 		if(options->verbose)
142 			collect_prediction_stats(kexisBlock, kexisBlock->predictor.diffMid,
143 				kexisBlock->predictor.diffAve);
144 
145 		// now we need to do our bit encoding
146 		rice_encode(kexisBlock, kexisBlock->predictor.diffMid, options,
147 			pcmBlock, MID);
148 		//kexisBlock->debugBlockCount++;
149 		rice_encode(kexisBlock, kexisBlock->predictor.diffAve, options,
150 		  pcmBlock, AVE);
151 		//kexisBlock->debugBlockCount++;
152 
153     loop = loop +2;
154   }
155 }
156 
write_kexisblock(OPTIONSTRUCT * options,KEXISBLOCKSTRUCT * kexisBlock,PCMBLOCKSTRUCT * pcmBlock)157 void write_kexisblock(OPTIONSTRUCT *options, KEXISBLOCKSTRUCT *kexisBlock,
158 	PCMBLOCKSTRUCT *pcmBlock)
159 {
160   int length;
161 	KEXISHEADER kexisHeader;
162 
163 	// Check and see if its the beginning of the stream.
164   if(options->outFileStream == NULL) {
165 		// Make the filename
166     options->outFileName = calloc(1, strlen(options->inFileName)+1);
167     strncpy(options->outFileName, options->inFileName,
168       strlen(options->inFileName)-3);
169     strcat(options->outFileName, "kxs\0");
170 
171 		// Open the file
172     options->outFileStream = fopen(options->outFileName, "wb");
173     if(options->outFileStream == NULL) {
174       sprintf(options->errorString, "Can't open file: %s!",
175 				options->outFileName);
176 			handle_error(options);
177 		}
178 
179 		// Create the header
180 		create_kexis_header(&kexisHeader, options, pcmBlock->pcmStreamLength);
181 		length = fwrite(&kexisHeader, sizeof(kexisHeader), 1,
182 			options->outFileStream);
183 		if(length != 1) {
184 			sprintf(options->errorString, "Error writing Header to %s!",
185 				options->outFileName);
186 			handle_error(options);
187     }
188   }
189 
190   length = fwrite(kexisBlock->data, sizeof(long), kexisBlock->dataPosition,
191     options->outFileStream);
192 
193 	if(options->progress)
194 		kexisBlock->sumTotalSize += length;
195 
196   if(length != kexisBlock->dataPosition) {
197 		sprintf(options->errorString, "Error writing to %s!",
198 			options->outFileName);
199 		handle_error(options);
200 	}
201 }
202