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