added flags
[swftools.git] / lib / wav.c
1 /* wav.c
2    Routines for handling .wav files
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "wav.h"
26
27 struct WAVBlock {
28     char id[5];
29     unsigned int size;
30 };
31
32 int getWAVBlock(FILE*fi, struct WAVBlock*block)
33 {
34     unsigned int size;
35     unsigned char b[4];
36     if(fread(block->id,1,4,fi)<4)
37         return 0;
38     block->id[4] = 0;
39     if(fread(b,1,4,fi)<4)
40         return 0;
41     block->size = b[0]|b[1]<<8|b[2]<<16|b[3]<<24;
42     /*printf("Chunk: [%c%c%c%c] (%d bytes)\n", 
43             block->id[0],block->id[1],
44             block->id[2],block->id[3],
45             block->size);*/
46     return 1;
47 }
48
49 int wav_read(struct WAV*wav, const char* filename)
50 {
51     FILE*fi = fopen(filename, "rb");
52     unsigned char b[16];
53     long int filesize;
54     struct WAVBlock block;
55     long int pos;
56     
57     if(!fi)
58         return 0;
59     fseek(fi, 0, SEEK_END);
60     filesize = ftell(fi);
61     fseek(fi, 0, SEEK_SET);
62
63     //printf("Filesize: %d\n", filesize);
64
65     if(!getWAVBlock (fi, &block))
66     {
67         fclose(fi);
68         return 0;
69     }
70     if(strncmp(block.id,"RIFF",4))
71     {
72         fprintf(stderr, "wav_read: not a WAV file\n");
73         fclose(fi);
74         return 0;
75     }
76     if(block.size + 8 < filesize)
77         fprintf(stderr, "wav_read: warning - more tags (%d extra bytes)\n",
78     filesize - block.size - 8);
79
80     if(block.size == filesize)
81         /* some buggy software doesn't generate the right tag length */
82         block.size = filesize - 8;
83
84     if(block.size + 8 > filesize)
85         fprintf(stderr, "wav_read: warning - short file (%d bytes missing)\n",
86     block.size + 8 -  filesize);
87     if(fread(b, 1, 4, fi) < 4)
88     {
89         fclose(fi);
90                 return 0;
91     }
92     if(strncmp((const char*)b, "WAVE", 4))
93     {
94         fprintf(stderr, "wav_read: not a WAV file (2)\n");
95         fclose(fi);
96                 return 0;
97         }
98  
99     do
100     {
101         getWAVBlock(fi, &block);
102         pos = ftell(fi);
103         if(!strncmp(block.id, "fmt ", 4))
104         {
105             if(fread(&b, 1, 16, fi)<16)
106             {
107                 fclose(fi);
108                 return 0;
109             }
110             wav->tag = b[0]|b[1]<<8;
111             wav->channels = b[2]|b[3]<<8;
112             wav->sampsPerSec = b[4]|b[5]<<8|b[6]<<16|b[7]<<24;
113             wav->bytesPerSec = b[8]|b[9]<<8|b[10]<<16|b[11]<<24;
114             wav->align = b[12]|b[13]<<8;
115             wav->bps = b[14]|b[15]<<8;
116         }
117         else
118             if (!strncmp(block.id, "LIST", 4))
119             {
120         // subchunk ICMT (comment) may exist
121             }
122             else
123                 if (!strncmp(block.id, "data", 4))
124                 {
125                     int l;
126                     wav->data = (unsigned char*)malloc(block.size);
127                     if(!wav->data)
128                     {
129                         fprintf(stderr, "Out of memory (%d bytes needed)", block.size);
130                         fclose(fi);
131                         return 0;
132                     }
133                     l = fread(wav->data, 1, block.size, fi);
134                     if(l<=0) {
135                         fprintf(stderr, "Error: Couldn't read WAV data block\n");
136                         fclose(fi);
137                         return 0;
138                     } else if(l < block.size)
139                     {
140                         fprintf(stderr, "Warning: data block of size %d is only %d bytes (%d bytes missing)\n", block.size, l, block.size-l);
141                         wav->size = l;
142                     } else {
143                         wav->size = block.size;
144                     }
145                 }
146                 pos+=block.size;
147                 fseek(fi, pos, SEEK_SET);
148     }
149     while (pos < filesize);
150     fclose(fi);
151     return 1;
152 }
153
154 int wav_write(struct WAV*wav, const char*filename)
155 {
156     FILE*fi = fopen(filename, "wb");
157     char*b="RIFFWAVEfmt \x10\0\0\0data";
158     char c[16];
159     unsigned long int w32;
160     if(!fi)
161         return 0;
162     fwrite(b, 4, 1, fi);
163     w32=(/*fmt*/8+0x10+/*data*/8+wav->size);
164     c[0] = w32;
165     c[1] = w32>>8;
166     c[2] = w32>>16;
167     c[3] = w32>>24;
168     fwrite(c, 4, 1, fi);
169     fwrite(&b[4], 12, 1, fi);
170     c[0] = wav->tag;
171     c[1] = wav->tag>>8;
172     c[2] = wav->channels;
173     c[3] = wav->channels>>8;
174     c[4] = wav->sampsPerSec;
175     c[5] = wav->sampsPerSec>>8;
176     c[6] = wav->sampsPerSec>>16;
177     c[7] = wav->sampsPerSec>>24;
178     c[8] = wav->bytesPerSec;
179     c[9] = wav->bytesPerSec>>8;
180     c[10] = wav->bytesPerSec>>16;
181     c[11] = wav->bytesPerSec>>24;
182     c[12] = wav->align;
183     c[13] = wav->align>>8;
184     c[14] = wav->bps;
185     c[15] = wav->bps>>8;
186     fwrite(c, 16, 1, fi);
187     fwrite(&b[16], 4, 1, fi);
188     c[0] = wav->size;
189     c[1] = wav->size>>8;
190     c[2] = wav->size>>16;
191     c[3] = wav->size>>24;
192     fwrite(c,4,1,fi);
193     printf("writing %d converted bytes\n", wav->size);
194     fwrite(wav->data,wav->size,1,fi);
195     fclose(fi);
196     return 1;
197 }
198
199 void wav_print(struct WAV*wav)
200 {
201     printf("tag:%04x channels:%d samples/sec:%d bytes/sec:%d align:%d bits/sample:%d size:%d\n",
202             wav->tag, wav->channels, wav->sampsPerSec, wav->bytesPerSec, 
203             wav->align, wav->bps, wav->size);
204 }
205
206 int wav_convert2mono(struct WAV*src, struct WAV*dest, int rate)
207 {
208     int samplelen=src->size/src->align;
209     int bps=src->bps;
210     double ratio;
211     double pos = 0;
212     int pos2 = 0;
213     int channels=src->channels;
214     int i;
215     int fill;
216
217     dest->sampsPerSec = rate;
218     dest->bps = 16;
219     dest->channels = 1;
220     dest->align = 2;
221     dest->tag = src->tag;
222     dest->bytesPerSec = dest->sampsPerSec*dest->align;
223
224     ratio = (double)dest->sampsPerSec/(double)src->sampsPerSec;
225     fill = (int)(ratio+1)*2;
226     
227     dest->data = (unsigned char*)malloc((int)(samplelen*ratio*2)+128);
228     if(!dest->data) 
229         return 0;
230     dest->size = (int)(samplelen*ratio)*2;
231
232     if(bps == 8) {
233         if(ratio <= 1) {
234             for(i=0; i<src->size; i+=channels) {
235                 int pos2 = ((int)pos)*2;
236                 dest->data[pos2] = 0;
237                 dest->data[pos2+1] = src->data[i]+128;
238                 pos += ratio;
239             }
240         } else {
241             for(i=0; i<src->size; i+=channels) {
242                 int j;
243                 int pos2 = ((int)pos)*2;
244                 for(j=0;j<fill;j+=2) {
245                     dest->data[pos2+j+0] = 0;
246                     dest->data[pos2+j+1] = src->data[i]+128;
247                 }
248                 pos += ratio;
249             }
250         }
251     } else if(bps == 16) {
252         if(ratio <= 1) {
253             for(i=0; i<src->size/2; i+=channels) {
254                 int pos2 = ((int)pos)*2;
255                 dest->data[pos2+0]=src->data[i*2];
256                 dest->data[pos2+1]=src->data[i*2+1];
257                 pos += ratio;
258             }
259         } else {
260             for(i=0; i<src->size/2; i+=channels) {
261                 int j;
262                 int pos2 = ((int)pos)*2;
263                 for(j=0;j<fill;j+=2) {
264                     dest->data[pos2+j+0] = src->data[i*2];
265                     dest->data[pos2+j+1] = src->data[i*2+1];
266                 }
267                 pos += ratio;
268             }
269         }
270     } else if(bps == 32) {
271         if(ratio <= 1) {
272             for(i=0; i<src->size/4; i+=channels) {
273                 int pos2 = ((int)pos)*2;
274                 dest->data[pos2+0]=src->data[i*4+2];
275                 dest->data[pos2+1]=src->data[i*4+3];
276                 pos += ratio;
277             }
278         } else {
279             for(i=0; i<src->size/4; i+=channels) {
280                 int j;
281                 int pos2 = ((int)pos)*2;
282                 for(j=0;j<fill;j+=2) {
283                     dest->data[pos2+j+0] = src->data[i*4+2];
284                     dest->data[pos2+j+1] = src->data[i*4+3];
285                 }
286                 pos += ratio;
287             }
288         }
289     } else {
290         fprintf(stderr, "Unsupported bitspersample value: %d\n", bps);
291     }
292     return 1;
293 }
294
295