r3829: *** 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 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.7 2003/01/21 04:19:44 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 = false;
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 info_msg_sans_newline (const char* msg)
54 {
55   fprintf (stdout, "%s", msg);
56 }
57
58 void
59 usage ()
60 {
61   error_msg ("usage: wdq2wav <wdq-file> <channel-number> <wav-file>");
62 }
63
64 bool wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname);
65
66 int
67 main (int argc, char *argv[])
68 {
69   if (argc != 4) {
70     usage();
71     exit(1);
72   }
73
74   char *wdq_fname = argv[1];
75   char *channel_endptr;
76   int channel = (int) strtol (argv[2], &channel_endptr, 10);
77   char *wav_fname = argv[3];
78
79   if (*channel_endptr != 0) {
80     std::ostringstream os;
81     os << "Error: Channel " << argv[2] << " is not an integer";
82     error_msg (os.str().c_str());
83     usage();
84     return(1);
85   }
86
87   if (! wdq2wav (wdq_fname, channel, wav_fname))
88     return 1;
89
90   return 0;
91 }
92
93 bool
94 wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname)
95 {
96   WindaqFile wdq (wdq_fname);
97
98   if (! wdq.ReadHeader()) {
99     return false;
100   }
101   if (g_verbose) {
102     std::ostringstream os1;
103     os1 << "File " << wdq_fname;
104     info_msg (os1.str().c_str());
105     std::ostringstream os2;
106     time_t time = wdq.m_time_acq_start;
107     struct tm* tm = gmtime (&time);
108     os2 << "  Time File Creation: " << asctime(tm);
109     info_msg_sans_newline (os2.str().c_str());
110     std::ostringstream os3;
111     time = wdq.m_time_acq_stop;
112     tm = gmtime (&time);
113     os3 << "  Time File Written: " << asctime(tm);
114     info_msg_sans_newline (os3.str().c_str());
115     std::ostringstream os4;
116     os4 << "  Samples: " << wdq.m_nSamples <<
117       ", Channels: " << wdq.m_nChannels <<
118       ", Sample Rate: " << wdq.m_sample_rate;
119     info_msg (os4.str().c_str());
120   }
121   
122   WindaqChannel wdq_channel (wdq, channel);
123   if (! wdq_channel.m_valid) {
124     error_msg ("Error reading data from channel");
125     return false;
126   }
127
128   if (g_verbose) {
129     std::ostringstream os1;
130     os1 << "Channel " << channel;
131     info_msg (os1.str().c_str());
132     std::ostringstream os2;
133     os2 << "  Units: " << wdq_channel.m_units.c_str();
134     info_msg (os2.str().c_str());
135     std::ostringstream os3;
136     os3 << "  Raw minimum: " << wdq_channel.m_min_raw_data <<
137       ", maximum: " << wdq_channel.m_max_raw_data;
138     info_msg (os3.str().c_str());
139     std::ostringstream os4;
140     os4 << "  Scaled minimum: " << wdq_channel.m_min_scaled_data <<
141       ", maximum: " << wdq_channel.m_max_scaled_data;
142     info_msg (os4.str().c_str());
143     std::ostringstream os5;
144     os5 << "  Slope " <<  wdq_channel.m_slope <<
145       ", Intercept " <<  wdq_channel.m_intercept;
146     info_msg (os5.str().c_str());
147   }
148
149   WavFile wav (wdq_channel, wav_fname);
150
151   if (! wav.m_valid) {
152     error_msg ("Error extracting wav from channel");
153     return false;
154   }
155
156   if (! wav.WriteFile ()) {
157     error_msg ("Error writing file");
158     return false;
159   }
160
161   return true;
162 }
163
164
165 WindaqFile::WindaqFile (const char* fname)
166   : m_fd(0), m_nChannels(0), m_nSamples(0), m_sample_rate(0), m_valid(false),
167     m_strFile (fname)
168 {
169 }
170
171 WindaqFile::~WindaqFile ()
172 {
173   if (m_fd != 0)
174     close (m_fd);
175 }
176
177 bool
178 WindaqFile::ReadHeader ()
179 {
180   unsigned short int tmp2;
181   unsigned int tmp4;
182
183   m_valid = false;
184   if ((m_fd = open (m_strFile.c_str(), O_RDONLY)) == 0)
185     return false;
186
187   lseek (0, 0, SEEK_SET);
188   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
189     error_msg ("Error reading file");
190     return false;
191   }
192   m_nChannels = tmp2 & 0x1f;
193   m_sr_denom = (tmp2 & 0x7fff) >> 5;
194   m_sr_numer = (tmp2 & 0x8000) << 1;
195   
196   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
197     error_msg ("Error reading file");
198     return false;
199   }
200   m_sr_numer |= tmp2;
201   m_sample_rate = (double) m_sr_numer / (double) (m_sr_denom * m_nChannels);
202
203   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
204     error_msg ("Error reading file");
205     return false;
206   }
207   m_channel_offset = tmp2 & 0xFF;
208   m_nBytes_channel_header = tmp2 >> 8;
209   
210   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
211     error_msg ("Error reading file");
212     return false;
213   }
214   m_nHeader_bytes = tmp2;
215   
216   if (read (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
217     error_msg ("Error reading file");
218     return false;
219   }
220   m_nData_bytes = tmp4;
221   m_nSamples = (m_nData_bytes / m_nChannels) / 2;
222
223   lseek (m_fd, 36, SEEK_SET);
224   if (read (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
225     error_msg ("Error reading file");
226     return false;
227   }
228   m_time_acq_start = tmp4;
229   if (read (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
230     error_msg ("Error reading file");
231     return false;
232   }
233   m_time_acq_stop = tmp4;
234   
235   // Verify Windaq signature
236   lseek (m_fd, m_nHeader_bytes - 2, SEEK_SET);
237   if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
238     error_msg ("Error reading file");
239     return false;
240   }
241   if (tmp2 != 0x8001) {
242     std::ostringstream os;
243     os << m_strFile << " is not a WinDAQ file";
244     error_msg (os.str().c_str());
245     return false;
246   }
247
248   m_valid = true;
249   return true;
250 }
251
252   
253 WindaqChannel::WindaqChannel (WindaqFile& wdq, const int channel)
254   : r_wdq(wdq), m_data(0), m_slope(0), m_intercept (0), m_channel(channel),
255     m_valid(false)
256 {
257   if (wdq.m_valid) {
258     if (channel >= 1 && channel <= wdq.m_nChannels) {
259       m_valid = true;
260       read_channel_data();
261     } else {
262       std::ostringstream os;
263       os << "Channel " << channel << " is invalid, valid range 1-" <<
264         wdq.m_nChannels;
265       error_msg (os.str().c_str());
266     }
267   }
268 }
269
270 WindaqChannel::~WindaqChannel ()
271 {
272   if (m_data)
273     delete m_data;
274 }
275
276 bool
277 WindaqChannel::read_channel_data ()
278 {
279   unsigned short int tmp2;
280   unsigned int tmp4;
281   double float8;
282   
283   int fd = r_wdq.m_fd;
284
285   if (! m_valid)
286     return false;
287
288   m_data = new signed short int [r_wdq.m_nSamples * 2];
289
290   lseek (fd, r_wdq.m_channel_offset + 8 + 
291          (m_channel - 1) * r_wdq.m_nBytes_channel_header,
292          SEEK_SET);
293   if (read (fd, &float8, sizeof(float8)) != sizeof(float8)) {
294     error_msg ("Error reading file");
295     return false;
296   }
297   m_slope = float8;
298   if (read (fd, &float8, sizeof(float8)) != sizeof(float8)) {
299     error_msg ("Error reading file");
300     return false;
301   }
302   m_intercept = float8;
303
304   char units[7];
305   units[6] = 0;
306   if (read (fd, units, 6) != 6) {
307     error_msg ("Error reading file");
308     return false;
309   }
310   m_units = units;
311  
312   unsigned int row_bytes = 2 * r_wdq.m_nChannels;
313   signed short int sample_row [row_bytes];
314   
315   signed short int* psample = &sample_row[m_channel - 1];
316
317   lseek (fd, r_wdq.m_nHeader_bytes, SEEK_SET);
318   long int i;
319   signed short int data_max, data_min;
320   for (i = 0; i < r_wdq.m_nSamples; i++) {
321     if (read (fd, sample_row, row_bytes) != row_bytes) {
322       std::ostringstream os;
323       os << "Error reading file at " << i;
324       error_msg (os.str().c_str());
325       return false;
326     }
327     
328     signed short int value = *psample >> 2;
329     m_data[i] = value;
330     
331     if (i == 0) {
332       data_max = value;
333       data_min = value;
334     } else {
335       if (value > data_max)
336         data_max = value;
337       else if (value < data_min)
338         data_min = value;
339     }
340   }
341
342   m_max_raw_data = data_max;
343   m_min_raw_data = data_min;
344   m_max_scaled_data = (m_slope * data_max) + m_intercept;
345   m_min_scaled_data = (m_slope * data_min) + m_intercept;
346
347   return true;
348 }
349
350
351 WavFile::WavFile (WindaqChannel& wdq_channel, const char* fname)
352   : m_valid(false), m_data(0), m_nSamples(0), m_strFile(fname), m_fd(0)
353 {
354   if (wdq_channel.m_valid) {
355     m_nSamples = wdq_channel.r_wdq.m_nSamples;
356     m_nChannels = 1;
357     m_nBitsPerSample = 16;
358     m_nBytesPerSample = 2;
359     m_rate = wdq_channel.r_wdq.m_sample_rate;
360     m_data = new signed short int [m_nSamples];
361     
362     double data_offset = -wdq_channel.m_min_scaled_data;
363     double data_scale = 0.;
364     if (wdq_channel.m_max_scaled_data != wdq_channel.m_min_scaled_data)
365       data_scale = 65535. / (wdq_channel.m_max_scaled_data -
366                              wdq_channel.m_min_scaled_data);
367     
368     if (g_debug)
369       printf ("data_scale: %f, data_offset: %f\n", data_scale, data_offset);
370     
371     signed short int* input = wdq_channel.m_data;
372     double slope = wdq_channel.m_slope;
373     double intercept = wdq_channel.m_intercept;
374     
375     long int i;
376     for (i = 0; i < m_nSamples; i++) {
377       double value = input[i];
378       value = (slope * value) + intercept;
379       value = (value + data_offset) * data_scale;
380       value += 0.5 - 32768;
381       m_data[i] = static_cast<signed short int>(value);
382     }
383   }
384
385   m_valid = true;
386 }
387
388
389 WavFile::~WavFile ()
390 {
391   if (m_fd != 0)
392     close (m_fd);
393     
394   if (m_data != NULL)
395     delete m_data;
396 }
397
398 bool
399 WavFile::WriteFile ()
400 {
401   unsigned short int tmp2;
402   unsigned int tmp4;
403   unsigned long data_bytes = m_nSamples * 2;
404
405   if (! m_valid)
406     return false;
407
408   if (m_fd == 0)
409     if ((m_fd = open (m_strFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, 
410                       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == 0) {
411       std::ostringstream os;
412       os << "Error opening output file " << m_strFile.c_str();
413       error_msg (os.str().c_str());
414       return false;
415     }
416
417   lseek (m_fd, 0, SEEK_SET);
418   if (write (m_fd, "RIFF", 4) != 4) {
419     error_msg ("Error writing file");
420     return false;
421   }
422
423   /* Length of file post 8 byte header */
424   tmp4 = 36 + data_bytes;
425   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
426     error_msg ("Error writing file");
427     return false;
428   }
429
430   if (write (m_fd, "WAVEfmt ", 8) != 8) {
431     error_msg ("Error writing file");
432     return false;
433   }
434   tmp4 = 0x10;
435   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
436     error_msg ("Error writing file");
437
438   }
439   tmp2 = 1;
440   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
441     error_msg ("Error writing file");
442     return false;
443   }
444   /* Number of channels */
445   tmp2 = m_nChannels;
446   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
447     error_msg ("Error writing file");
448     return false;
449   }
450   tmp4 = static_cast<int> (m_rate + 0.5);
451   /* Sample rate */
452   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
453     error_msg ("Error writing file");
454     return false;
455   }
456   tmp4 = static_cast<int> (m_rate * m_nBytesPerSample + 0.5);
457   /* Bytes per second */
458   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
459     error_msg ("Error writing file");
460     return false;
461   }
462   tmp2 = m_nBytesPerSample * m_nChannels;
463   /* Bytes per sample */
464   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
465     error_msg ("Error writing file");
466     return false;
467   }
468   tmp2 = m_nBitsPerSample;
469   /* Bits per sample */
470   if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) {
471     error_msg ("Error writing file");
472     return false;
473   }
474   if (write (m_fd, "data", 4) != 4) {
475     error_msg ("Error writing file");
476     return false;
477   }
478   tmp4 = data_bytes;
479   /* Data length */
480   if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) {
481     error_msg ("Error writing file");
482     return false;
483   }
484
485   long int i;
486   for (i = 0; i < m_nSamples; i++) {
487     signed short int stmp2 = m_data[i]; 
488     if (write (m_fd, &stmp2, sizeof(stmp2)) != sizeof(stmp2)) {
489       error_msg ("Error writing file");
490       return false;
491     }
492   }
493   
494   if (close (m_fd) < 0)
495     error_msg ("Error closing output file");
496   
497   m_fd = 0;
498   return true;
499 }
500
501
502   
503