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