r3820: *** empty log message ***
[wdq2wav.git] / wdq2wav.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **   Name:          wdq2wav.cpp
5 **   Purpose:       Converts a channel of WinDAQ file to .WAV format
6 **   Programmer:    Kevin Rosenberg <kevin@rosenberg.net>
7 **   Date Started:  Jan 2003
8 **
9 **  Copyright (c) 2003 Kevin Rosenberg
10 **
11 **  NOTES
12 **   Quick & Dirty hack, but efficient and good error checking
13 **   Allocates memory equal to the size of channel (2 * number of samples)
14 **
15 **  BUGS
16 **   Need to abstract error checking so it is not so redundant
17 **   Need command line options for verbose and debug output
18 **   Should comment the reading and writing of files to document the file formats
19 **
20 **  $Id: wdq2wav.cpp,v 1.1 2003/01/21 01:58:32 kevin Exp $
21 **
22 **  This program is free software; you can redistribute it and/or modify
23 **  it under the terms of the GNU General Public License (version 2) as
24 **  published by the Free Software Foundation.
25 **
26 **  This program is distributed in the hope that it will be useful,
27 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
28 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29 **  GNU General Public License for more details.
30 **
31 **  You should have received a copy of the GNU General Public License
32 **  along with this program; if not, write to the Free Software
33 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
34 ******************************************************************************/
35
36 #include <wdq2wav.h>
37
38 static bool g_verbose = true;
39 static bool g_debug = true;
40
41 void
42 error_msg (char *msg)
43 {
44   fprintf (stderr, "%s\n",msg);
45 }
46
47 void
48 usage ()
49 {
50   fprintf (stderr, "usage: wdq2wav <wdq-file> <channel-number> <wav-file>\n");
51 }
52
53 bool wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname);
54
55 int
56 main (int argc, char *argv[])
57 {
58   if (argc != 4) {
59     usage();
60     exit(1);
61   }
62
63   char *wdq_fname = argv[1];
64   char *channel_endptr;
65   int channel = (int) strtol (argv[2], &channel_endptr, 10);
66   char *wav_fname = argv[3];
67
68   if (*channel_endptr != 0) {
69     fprintf (stderr, "Error: Channel %s is not an integer\n", argv[2]);
70     usage();
71     return(1);
72   }
73
74   if (! wdq2wav (wdq_fname, channel, wav_fname))
75     return 1;
76
77   return 0;
78 }
79
80 bool wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname)
81 {
82   WindaqFile wdq (wdq_fname);
83
84   if (! wdq.ReadHeader()) {
85     fprintf (stderr, "Error reading header %s\n", wdq_fname);
86     return false;
87   }
88   if (g_verbose) {
89     printf ("Number of channels: %d\n", wdq.m_nChannels);
90     printf ("Number of Samples: %d\n", wdq.m_nSamples);
91     printf ("Sample Rate: %.1f\n", wdq.m_sample_rate);
92   }
93   
94   WindaqChannel wdq_channel (wdq, channel);
95   if (! wdq_channel.m_valid) {
96     error_msg ("Error reading data from channel");
97     return false;
98   }
99
100   if (g_verbose) {
101     printf ("Data Min: %f, Data Max: %f\n", 
102             wdq_channel.m_min_raw_data, wdq_channel.m_max_raw_data);
103   }
104
105   WavFile wav (wdq_channel, wav_fname);
106
107   if (! wav.m_valid) {
108     error_msg ("Error extracting wav from channel");
109     return false;
110   }
111
112   if (! wav.WriteFile ()) {
113     error_msg ("Error writing file");
114     return false;
115   }
116
117   return true;
118 }
119
120
121 WindaqFile::WindaqFile (const char* fname)
122   : m_fd(0), m_nChannels(0), m_nSamples(0), m_sample_rate(0), m_valid(false),
123     m_strFile (fname)
124 {
125 }
126
127 WindaqFile::~WindaqFile ()
128 {
129   if (m_fd != 0)
130     close (m_fd);
131 }
132
133 bool
134 WindaqFile::ReadHeader ()
135 {
136   unsigned short int tmp2;
137   unsigned int tmp4;
138
139   m_valid = false;
140   if ((m_fd = open (m_strFile.c_str(), O_RDONLY)) == 0)
141     return false;
142
143   lseek (0, 0, SEEK_SET);
144   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
145     error_msg ("Error reading file");
146     return false;
147   }
148   m_nChannels = tmp2 & 0x1f;
149   m_sr_denom = (tmp2 & 0x7fff) >> 5;
150   m_sr_numer = (tmp2 & 0x8000) << 1;
151   
152   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
153     error_msg ("Error reading file");
154     return false;
155   }
156   m_sr_numer |= tmp2;
157   m_sample_rate = (double) m_sr_numer / (double) (m_sr_denom * m_nChannels);
158
159   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
160     error_msg ("Error reading file");
161     return false;
162   }
163   m_channel_offset = tmp2 & 0xFF;
164   m_nBytes_channel_header = tmp2 >> 8;
165   
166   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
167     error_msg ("Error reading file");
168     return false;
169   }
170   m_nHeader_bytes = tmp2;
171   
172   if (read (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
173     error_msg ("Error reading file");
174     return false;
175   }
176   m_nData_bytes = tmp4;
177   m_nSamples = (m_nData_bytes / m_nChannels) / 2;
178
179   // Verify Windaq signature
180   lseek (m_fd, m_nHeader_bytes - 2, SEEK_SET);
181   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
182     error_msg ("Error reading file");
183     return false;
184   }
185   if (tmp2 != 0x8001) {
186     fprintf (stderr, "%s is not a WinDAQ file\n", m_strFile.c_str());
187     return false;
188   }
189
190   m_valid = true;
191   return true;
192 }
193
194   
195 WindaqChannel::WindaqChannel (WindaqFile& wdq, const int channel)
196   : r_wdq(wdq), m_data(0), m_slope(0), m_intercept (0), m_channel(channel),
197     m_valid(false)
198 {
199   if (wdq.m_valid) {
200     if (channel >= 1 && channel <= wdq.m_nChannels) {
201       m_valid = true;
202       read_channel_data();
203     } else
204       fprintf (stderr, "Channel %d is invalid, valid range 1-%d\n",
205               channel, wdq.m_nChannels);
206   }
207 }
208
209 WindaqChannel::~WindaqChannel ()
210 {
211   if (m_data)
212     delete m_data;
213 }
214
215 bool
216 WindaqChannel::read_channel_data ()
217 {
218   unsigned short int tmp2;
219   unsigned int tmp4;
220   double float8;
221   
222   int fd = r_wdq.m_fd;
223
224   if (! m_valid)
225     return false;
226
227   m_data = new signed short int [r_wdq.m_nSamples * 2];
228
229   lseek (fd, r_wdq.m_channel_offset + 8 + 
230          (m_channel - 1) * r_wdq.m_nBytes_channel_header,
231          SEEK_SET);
232   if (read (fd, &float8, sizeof(float8)) != sizeof(float8)) {
233     error_msg ("Error reading file");
234     return false;
235   }
236   m_slope = float8;
237   if (read (fd, &float8, sizeof(float8)) != sizeof(float8)) {
238     error_msg ("Error reading file");
239     return false;
240   }
241   m_intercept = float8;
242
243   if (g_verbose)
244     printf ("Slope: %f, Intercept: %f\n", m_slope, m_intercept);
245
246   char units[7];
247   units[6] = 0;
248   if (read (fd, units, 6) != 6) {
249     error_msg ("Error reading file");
250     return false;
251   }
252   m_units = units;
253   if (g_verbose)
254     printf ("Units: %s\n", units);
255  
256   unsigned int row_bytes = 2 * r_wdq.m_nChannels;
257   signed short int sample_row [row_bytes];
258   
259   signed short int* psample = &sample_row[m_channel - 1];
260
261   lseek (fd, r_wdq.m_nHeader_bytes, SEEK_SET);
262   long int i;
263   signed short int data_max, data_min;
264   for (i = 0; i < r_wdq.m_nSamples; i++) {
265     if (read (fd, sample_row, row_bytes) != row_bytes) {
266       fprintf (stderr, "Error reading file at %d\n", i);
267       return false;
268     }
269     
270     signed short int value = *psample >> 2;
271     m_data[i] = value;
272     
273     if (i == 0) {
274       data_max = value;
275       data_min = value;
276     } else {
277       if (value > data_max)
278         data_max = value;
279       else if (value < data_min)
280         data_min = value;
281     }
282   }
283
284   m_max_raw_data = data_max;
285   m_min_raw_data = data_min;
286   m_max_scaled_data = (m_slope * data_max) + m_intercept;
287   m_min_scaled_data = (m_slope * data_min) + m_intercept;
288
289   return true;
290 }
291
292
293 WavFile::WavFile (WindaqChannel& wdq_channel, const char* fname)
294   : m_valid(false), m_data(0), m_nSamples(0), m_strFile(fname), m_fd(0)
295 {
296   if (wdq_channel.m_valid) {
297     m_nSamples = wdq_channel.r_wdq.m_nSamples;
298     m_nChannels = 1;
299     m_nBitsPerSample = 16;
300     m_nBytesPerSample = 2;
301     m_rate = wdq_channel.r_wdq.m_sample_rate;
302     m_data = new signed short int [m_nSamples];
303     
304     double data_offset = -wdq_channel.m_min_scaled_data;
305     double data_scale = 0.;
306     if (wdq_channel.m_max_scaled_data != wdq_channel.m_min_scaled_data)
307       data_scale = 65535. / (wdq_channel.m_max_scaled_data -
308                              wdq_channel.m_min_scaled_data);
309     
310     if (g_debug)
311       printf ("data_scale: %f, data_offset: %f\n", data_scale, data_offset);
312     
313     signed short int* input = wdq_channel.m_data;
314     double slope = wdq_channel.m_slope;
315     double intercept = wdq_channel.m_intercept;
316     
317     long int i;
318     for (i = 0; i < m_nSamples; i++) {
319       double value = input[i];
320       value = (slope * value) + intercept;
321       value = (value + data_offset) * data_scale;
322       value += 0.5 - 32768;
323       m_data[i] = static_cast<signed short int>(value);
324     }
325   }
326
327   m_valid = true;
328 }
329
330
331 WavFile::~WavFile ()
332 {
333   if (m_fd != 0)
334     close (m_fd);
335     
336   if (m_data != NULL)
337     delete m_data;
338 }
339
340 bool
341 WavFile::WriteFile ()
342 {
343   unsigned short int tmp2;
344   unsigned int tmp4;
345   unsigned long data_bytes = m_nSamples * 2;
346
347   if (! m_valid)
348     return false;
349
350   if (m_fd == 0)
351     if ((m_fd = open (m_strFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 
352                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == 0) {
353       fprintf (stderr, "Error opening output file %s\n", m_strFile.c_str());
354       return false;
355     }
356
357   lseek (m_fd, 0, SEEK_SET);
358   if (write (m_fd, "RIFF", 4) != 4) {
359     error_msg ("Error writing file");
360     return false;
361   }
362
363   /* Length of file post 8 byte header */
364   tmp4 = 36 + data_bytes;
365   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
366     error_msg ("Error writing file");
367     return false;
368   }
369
370   if (write (m_fd, "WAVEfmt ", 8) != 8) {
371     error_msg ("Error writing file");
372     return false;
373   }
374   tmp4 = 0x10;
375   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
376     error_msg ("Error writing file");
377
378   }
379   tmp2 = 1;
380   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
381     error_msg ("Error writing file");
382     return false;
383   }
384   /* Number of channels */
385   tmp2 = m_nChannels;
386   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
387     error_msg ("Error writing file");
388     return false;
389   }
390   tmp4 = static_cast<int> (m_rate + 0.5);
391   /* Sample rate */
392   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
393     error_msg ("Error writing file");
394     return false;
395   }
396   tmp4 = static_cast<int> (m_rate * m_nBytesPerSample + 0.5);
397   /* Bytes per second */
398   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
399     error_msg ("Error writing file");
400     return false;
401   }
402   tmp2 = m_nBytesPerSample * m_nChannels;
403   /* Bytes per sample */
404   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
405     error_msg ("Error writing file");
406     return false;
407   }
408   tmp2 = m_nBitsPerSample;
409   /* Bits per sample */
410   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
411     error_msg ("Error writing file");
412     return false;
413   }
414   if (write (m_fd, "data", 4) != 4) {
415     error_msg ("Error writing file");
416     return false;
417   }
418   tmp4 = data_bytes;
419   /* Data length */
420   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
421     error_msg ("Error writing file");
422     return false;
423   }
424
425   long int i;
426   for (i = 0; i < m_nSamples; i++) {
427     signed short int stmp2 = m_data[i]; 
428     if (write (m_fd, &stmp2, sizeof(stmp2)) != sizeof(stmp2)) {
429       error_msg ("Error writing file");
430       return false;
431     }
432   }
433   
434   if (close (m_fd) < 0)
435     error_msg ("Error closing output file");
436   
437   m_fd = 0;
438   return true;
439 }
440
441
442   
443