Fix bug 511646
[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 **  $Id$
12 **
13 **  This program is free software; you can redistribute it and/or modify
14 **  it under the terms of the GNU General Public License (version 2) as
15 **  published by the Free Software Foundation.
16 **
17 **  This program is distributed in the hope that it will be useful,
18 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
19 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 **  GNU General Public License for more details.
21 **
22 **  You should have received a copy of the GNU General Public License
23 **  along with this program; if not, write to the Free Software
24 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25 ******************************************************************************/
26
27 #include <stdlib.h>
28 #include <wdq2wav.h>
29
30 const char* g_szIdStr = "$Id$";
31
32 bool g_quiet = false;
33 bool g_verbose = false;
34 bool g_debug = false;
35 bool g_ignore_zero = false;
36 bool g_dont_demean = false;
37
38
39 #ifdef WIN32
40 #define lseek _lseek
41 #define close _close
42 #define open _open
43 #define read _read
44 #define write _write
45 #define O_BINARY _O_BINARY
46 #define O_RDONLY _O_RDONLY
47 #define O_WRONLY _O_WRONLY
48 #define O_RDWR _O_RDRW
49 #define O_TRUNC _O_TRUNC
50 #define O_CREAT _O_CREAT
51 const int g_fileMode = _S_IWRITE | _S_IREAD;
52 struct fpos_t std::_Fpz = {0,0};
53 #else
54 const int g_fileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
55 // Define as NULL for non-Windows platforms
56 #ifndef O_BINARY
57 #define O_BINARY 0
58 #endif
59 #endif
60
61
62 void
63 error_msg (const char *msg)
64 {
65   std::cerr << msg << std::endl;
66 }
67
68 void
69 info_msg (const char* msg)
70 {
71   std::cout << msg << std::endl;
72 }
73
74 void
75 info_msg_sans_newline (const char* msg)
76 {
77   std::cout << msg;
78 }
79
80 const char*
81 fileBasename (const char* filename)
82 {
83   const char* pslash = strrchr (filename, '/');
84   const char* pbackslash = strrchr (filename, '\\');
85   const char* p = filename;
86   if (pbackslash && (! pslash || pbackslash >= pslash))
87         p = pbackslash+1;
88   else if (pslash && (! pbackslash || pslash >= pbackslash))
89     p = pslash+1;
90
91   return p;
92 }
93
94 char *
95 str_rm_tail (char *str, const char* const charlist)
96 {
97   size_t i;
98
99   for (i = strlen(str) - 1; i >= 0; i--)
100     if (strchr (charlist, str[i]) != NULL)
101       str[i] = 0;
102     else
103       break;            /* found non-specified char, all done */
104
105   return (str);
106 }
107
108 char *
109 str_wrm_tail (char *str)
110 {
111   return (str_rm_tail(str, "\b\t\n\r"));
112 }
113
114
115 void
116 usage (const char* progname)
117 {
118   std::cout << "usage: " << fileBasename (progname) << " [OPTIONS] <wdq-file> <channel-number> <wav-file>\n";
119   std::cout << "OPTIONS\n";
120   std::cout << "  -p   Play channel through audio system\n";
121   std::cout << "  -q   Supress all messages\n";
122   std::cout << "  -z   Scale output without preserving zero point\n";
123   std::cout << "  -m   Do not demean the data (don't subtract the mean value from each sample)\n";
124   std::cout << "  -v   Verbose mode\n";
125   std::cout << "  -d   Debug mode\n";
126   std::cout << "  -r   Print program version and exit\n";
127   std::cout << "  -h   Print this help message\n";
128 }
129
130
131 int
132 main (int argc, char *argv[])
133 {
134   int c;
135   bool play = false;
136
137   const char* progname = argv[0];
138
139   while ((c = getopt (argc, argv, "rqvzmdph")) != -1) {
140     switch (c) {
141     case 'r':
142         std::cout << "Version " << g_szIdStr << std::endl;
143                 exit(0);
144         break;
145     case 'q':
146       g_quiet = true;
147       break;
148     case 'm':
149       g_dont_demean = true;
150       break;
151     case 'z':
152       g_ignore_zero = true;
153       break;
154     case 'v':
155       g_verbose = true;
156       break;
157     case 'd':
158       g_debug = true;
159       break;
160     case 'p':
161       play = true;
162       break;
163     case 'h':
164       usage (progname);
165       return (0);
166     case '?':
167     default:
168         usage (progname);
169         return (1);
170       }
171     }
172
173   if (g_verbose || g_debug)
174           std::cout << "Version " << g_szIdStr << std::endl;
175
176   argc -= optind;
177   argv += optind;
178   if (argc > 3) {
179       std::cerr << "Too many parameters\n";
180       usage (progname);
181       return (1);
182     }
183
184   char wdq_fname[MAX_INPUT_STR];
185   if (argc >= 1)
186       strncpy (wdq_fname, argv [0], MAX_INPUT_STR);
187   else {
188     std::cout << "Enter input WinDAQ filename: ";
189       std::cin.getline (wdq_fname, MAX_INPUT_STR);
190     }
191
192     char channel_buf [MAX_INPUT_STR];
193     if (argc >= 2)
194       strncpy (channel_buf, argv[1], MAX_INPUT_STR);
195     else {
196       std::cout << "Enter channel number: ";
197       std::cin.getline (channel_buf, MAX_INPUT_STR);
198     }
199
200     char *channel_endptr;
201     int channel = static_cast<int>(strtol (channel_buf, &channel_endptr, 10));
202     if (*channel_endptr != 0) {
203       std::ostringstream os;
204       os << "Error: Channel " << channel_buf << " is not an integer";
205       error_msg (os.str().c_str());
206       usage (progname);
207       return (1);
208     }
209
210     char wav_fname[MAX_INPUT_STR];
211     if (argc >= 3)
212       strncpy (wav_fname, argv[2], MAX_INPUT_STR);
213     else {
214       std::cout << "Enter output wav filename: ";
215       std::cin.getline (wav_fname, MAX_INPUT_STR);
216     }
217
218     if (! wdq2wav (wdq_fname, channel, wav_fname, play))
219       return 1;
220
221     return 0;
222 }
223
224 bool
225 wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname, bool play)
226 {
227   WindaqFile wdq (wdq_fname);
228
229   if (! wdq.ReadHeader()) {
230     if (wdq.m_error.size()) {
231       std::ostringstream os;
232       os << "Error reading file " << wdq_fname << ": " << wdq.m_error.c_str();
233       error_msg (os.str().c_str());
234     } else {
235       std::ostringstream os;
236       os << "Error reading file " << wdq_fname;
237       error_msg (os.str().c_str());
238     }
239     return false;
240   }
241
242   if (wdq.any_packed_channels()) {
243           std::ostringstream os;
244           os << "File contains 'packed' channels." << std::endl;
245           os << "Convert to 'Advanced CODAS headers' before processing with wdq2wav.";
246           error_msg (os.str().c_str());
247           return false;
248   }
249
250   if (! g_quiet || g_verbose || g_debug) {
251     std::ostringstream os1;
252         os1 << "File: " << wdq_fname;
253     info_msg (os1.str().c_str());
254         std::ostringstream os;
255         os << "Legacy Format: ";
256         if (wdq.m_bLegacy_format)
257                 os << "Yes";
258         else
259                 os << "No";
260         info_msg(os.str().c_str());
261     std::ostringstream os2;
262     time_t time = wdq.m_time_acq_start;
263     struct tm* tm = gmtime (&time);
264     os2 << "  Time File Creation: " << asctime(tm);
265     info_msg_sans_newline (os2.str().c_str());
266     std::ostringstream os3;
267     time = wdq.m_time_acq_stop;
268     tm = gmtime (&time);
269     os3 << "  Time File Written: " << asctime(tm);
270     info_msg_sans_newline (os3.str().c_str());
271     std::ostringstream os4;
272     os4 << "  Samples: " << wdq.m_nSamples <<
273       ", Channels: " << wdq.m_nChannels <<
274       ", Sample Rate: " << wdq.m_sample_rate;
275     info_msg (os4.str().c_str());
276   }
277
278   WindaqChannel wdq_channel (wdq, channel);
279   if (! wdq_channel.m_valid) {
280     error_msg ("Error reading data from channel");
281     return false;
282   }
283
284   if (! g_quiet || g_verbose || g_debug) {
285     std::ostringstream os1;
286     os1 << "Channel " << channel;
287     info_msg (os1.str().c_str());
288     std::ostringstream os2;
289     os2 << "  Units: " << wdq_channel.m_units.c_str();
290     info_msg (os2.str().c_str());
291     std::ostringstream os3;
292     os3 << "  Raw minimum: " << wdq_channel.m_min_raw_data <<
293       ", maximum: " << wdq_channel.m_max_raw_data;
294     info_msg (os3.str().c_str());
295   }
296
297   if (g_debug) {
298     std::ostringstream os4;
299     os4 << "  Scaled minimum: " << wdq_channel.m_min_scaled_data <<
300       ", maximum: " << wdq_channel.m_max_scaled_data;
301     info_msg (os4.str().c_str());
302     std::ostringstream os5;
303     os5 << "  Slope " <<  wdq_channel.m_slope <<
304       ", Intercept " <<  wdq_channel.m_intercept;
305     info_msg (os5.str().c_str());
306   }
307
308   WavFile wav (wdq_channel, wav_fname);
309
310   if (! wav.m_valid) {
311     error_msg ("Error extracting wav from channel");
312     return false;
313   }
314
315   if (! wav.WriteFile ()) {
316     error_msg ("Error writing file");
317     return false;
318   }
319
320   if (play)
321     wav.Play();
322
323   return true;
324 }
325
326
327 WindaqFile::WindaqFile (const char* fname)
328   : m_valid(false), m_fd(0), m_nChannels(0), m_nSamples(0), m_sample_rate(0),
329     m_strFile (fname)
330 {
331 }
332
333 WindaqFile::~WindaqFile ()
334 {
335   if (m_fd != 0)
336     close (m_fd);
337 }
338
339 bool read_int1 (int fd, unsigned char& n)
340 {
341   unsigned char tmp1;
342   if (read (fd, &tmp1, 1) != 1)
343     return false;
344
345   n = tmp1;
346   return true;
347 }
348
349 bool read_int2 (int fd, unsigned short int& n)
350 {
351   unsigned char tmp1;
352   unsigned int tmp2;
353   if (read (fd, &tmp1, 1) != 1)
354     return false;
355   tmp2 = tmp1;
356   if (read (fd, &tmp1, 1) != 1)
357     return false;
358
359   n = tmp2 + (tmp1 * 256);
360   return true;
361 }
362
363 bool read_int4 (int fd, unsigned int& n)
364 {
365   unsigned int tmp4;
366   unsigned short int tmp2;
367   if (! read_int2 (fd, tmp2))
368     return false;
369   tmp4 = tmp2;
370   if (! read_int2 (fd, tmp2))
371     return false;
372
373   n = tmp4 + (tmp2 * 65536);
374   return true;
375 }
376
377 bool read_float8 (int fd, double& f)
378 {
379   unsigned char buf[8];
380   if (read (fd, &buf, 8) != 8)
381     return false;
382
383 #if WORDS_BIG_ENDIAN
384   unsigned char c;
385   c = buf[0]; buf[0] = buf[7]; buf[7] = c;
386   c = buf[1]; buf[1] = buf[6]; buf[6] = c;
387   c = buf[2]; buf[2] = buf[5]; buf[5] = c;
388   c = buf[3]; buf[3] = buf[4]; buf[4] = c;
389 #endif
390
391   f = *(reinterpret_cast<double*>(buf));
392
393   return true;
394 }
395
396 bool
397 WindaqFile::ReadHeader ()
398 {
399   m_valid = false;
400   if ((m_fd = open (m_strFile.c_str(), O_RDONLY | O_BINARY)) < 0) {
401     m_error = "Unable to open file";
402     return false;
403   }
404
405   lseek (0, 0, SEEK_SET);
406   unsigned short int element1;
407   if (! read_int2 (m_fd, element1))
408     return false;
409
410   short unsigned int byte1 = (element1 & 0xFF00) >> 8;
411   short unsigned int byte2 = element1 & 0xFF;
412   if (byte1 == 0 || byte1 == 1) {
413           m_bLegacy_format = false;
414           m_sr_denom = m_sr_numer = 0;
415   } else {
416           m_sr_denom = (element1 & 0x7fff) >> 5;
417           m_sr_numer = (element1 & 0x8000) << 1;
418       m_bLegacy_format = true;
419   }
420   unsigned short int element2;
421   if (! read_int2 (m_fd, element2))
422     return false;
423
424   if (m_bLegacy_format)
425           m_sr_numer |= element2;
426
427   unsigned char element3;
428   if (! read_int1 (m_fd, element3))
429     return false;
430   m_channel_offset = element3;
431   if (g_debug)
432           std::cout << "Channel offset: " << m_channel_offset << std::endl;
433
434   unsigned char element4;
435   if (! read_int1 (m_fd, element4))
436     return false;
437   m_nBytes_channel_header = element4;
438   if (g_debug)
439           std::cout << "Channel header bytes: " << m_nBytes_channel_header << std::endl;
440
441   unsigned short int element5;
442   if (! read_int2 (m_fd, element5))
443     return false;
444   m_nHeader_bytes = element5;
445   if (g_debug)
446           std::cout << "Header bytes: " << m_nHeader_bytes << std::endl;
447
448   m_nMaxChannels = (m_nHeader_bytes - 112) / 36;
449   if (m_nMaxChannels >= 144)
450         m_nChannels = byte2 & 0xFF;
451   else
452     m_nChannels = byte2 & 0x1F;
453
454   unsigned int element6;
455   if (! read_int4 (m_fd, element6))
456     return false;
457   m_nData_bytes = element6;
458   if (g_debug)
459           std::cout << "Data bytes: " << m_nData_bytes << std::endl;
460
461   m_nSamples = (m_nData_bytes / m_nChannels) / 2;
462
463   lseek (m_fd, 28, SEEK_SET);
464   double element13;
465   if (! read_float8 (m_fd, element13))
466           return false;
467   m_time_between_channel_samples = element13;
468
469   if (m_bLegacy_format)
470      m_sample_rate = (double) m_sr_numer / (double) (m_sr_denom * m_nChannels);
471   else
472          m_sample_rate = (double) m_nChannels / m_time_between_channel_samples;
473
474   lseek (m_fd, 36, SEEK_SET);
475   if (! read_int4 (m_fd, m_time_acq_start))
476     return false;
477
478   if (! read_int4 (m_fd, m_time_acq_stop))
479     return false;
480
481   lseek (m_fd, 100, SEEK_SET);
482   unsigned short int element27;
483   if (! read_int2 (m_fd, element27))
484           return false;
485
486   m_bHires = (element27 & 0x0001) ? true : false;
487   if (g_debug) {
488           std::cout << "High resolution: ";
489           if (m_bHires)
490                   std::cout << "Yes";
491           else
492                   std::cout << "No";
493
494           std::cout << std::endl;
495   }
496
497   // Verify Windaq signature
498   lseek (m_fd, m_nHeader_bytes - 2, SEEK_SET);
499   unsigned short int element35;
500   if (! read_int2 (m_fd, element35))
501     return false;
502
503   if (element35 != 0x8001) {
504     std::ostringstream os;
505         m_error = "Incorrect signagure: file is not a valid WinDAQ file";
506     return false;
507   }
508
509   m_valid = true;
510   return true;
511 }
512
513 bool
514 WindaqFile::any_packed_channels ()
515 {
516         for (int iChannel = 0; iChannel < m_nChannels; iChannel++)
517                 if (is_channel_packed (iChannel))
518                         return true;
519
520         return false;
521 }
522
523 bool
524 WindaqFile::is_channel_packed (const int channel)
525 {
526   long iStart = m_channel_offset + channel * m_nBytes_channel_header;
527
528   lseek (m_fd, iStart + 31, SEEK_SET);
529   unsigned char iReadings_per_data_point;
530   if (! read_int1 (m_fd, iReadings_per_data_point))
531         return false;
532
533   if (iReadings_per_data_point > 1)
534           return true;
535
536   return false;
537 }
538
539 WindaqChannel::WindaqChannel (WindaqFile& wdq, const int channel)
540   : r_wdq(wdq), m_data(0), m_slope(0), m_intercept (0), m_channel(channel),
541     m_valid(false)
542 {
543   if (wdq.m_valid) {
544     if (channel >= 1 && channel <= wdq.m_nChannels) {
545       if (read_channel_data())
546         m_valid = true;
547     } else {
548       std::ostringstream os;
549       os << "Channel " << channel << " is invalid, valid range 1-" <<
550         wdq.m_nChannels;
551       error_msg (os.str().c_str());
552     }
553   }
554 }
555
556 WindaqChannel::~WindaqChannel ()
557 {
558   if (m_data)
559     delete m_data;
560 }
561
562 bool
563 WindaqChannel::read_channel_data ()
564 {
565   int fd = r_wdq.m_fd;
566
567   m_data = new signed short int [r_wdq.m_nSamples * 2];
568
569   lseek (fd, r_wdq.m_channel_offset + 8 +
570          (m_channel - 1) * r_wdq.m_nBytes_channel_header,
571          SEEK_SET);
572   if (! read_float8 (fd, m_slope))
573     return false;
574
575   if (! read_float8 (fd, m_intercept))
576     return false;
577
578   char units[7];
579   units[6] = 0;
580   if (read (fd, units, 6) != 6) {
581     error_msg ("Error reading file");
582     return false;
583   }
584   m_units = units;
585
586   long int row_bytes = 2 * r_wdq.m_nChannels;
587
588   signed short int *sample_row = new signed short int [row_bytes];
589
590   signed short int* psample = &sample_row[m_channel - 1];
591
592   lseek (fd, r_wdq.m_nHeader_bytes, SEEK_SET);
593   unsigned long int i;
594   signed short int data_max = 0, data_min = 0;
595   double total_data = 0;
596   for (i = 0; i < r_wdq.m_nSamples; i++) {
597     if (read (fd, sample_row, row_bytes) != row_bytes) {
598       std::ostringstream os;
599       os << "Error reading file at " << i;
600       error_msg (os.str().c_str());
601       delete sample_row;
602       return false;
603     }
604     signed short int v = *psample;
605
606 #if WORDS_BIG_ENDIAN
607     unsigned char* p = reinterpret_cast<unsigned char*>(&v);
608     unsigned char c = p[0]; p[0] = p[1]; p[1] = c;
609 #endif
610
611     signed short int value = v;
612         if (! r_wdq.m_bHires)
613                 value >>= 2;
614
615     m_data[i] = value;
616     total_data += value;
617
618     if (i == 0) {
619       data_max = value;
620       data_min = value;
621     } else {
622       if (value > data_max)
623         data_max = value;
624       else if (value < data_min)
625         data_min = value;
626     }
627   }
628
629   m_max_raw_data = data_max;
630   m_min_raw_data = data_min;
631   m_max_scaled_data = (m_slope * data_max) + m_intercept;
632   m_min_scaled_data = (m_slope * data_min) + m_intercept;
633
634   if (! g_dont_demean) {
635     double dmean = total_data / static_cast<double>(r_wdq.m_nSamples);
636     int mean = nearest<int>(dmean);
637     std::cout << "Removing mean: " << (dmean * m_slope) + m_intercept <<
638       " " << m_units << std::endl;
639
640     for (i = 0; i < r_wdq.m_nSamples; i++)
641       m_data[i] -= mean;
642   }
643
644   delete sample_row;
645   return true;
646 }
647
648
649 WavFile::WavFile (WindaqChannel& wdq_channel, const char* fname)
650   : m_valid(false), m_data(0), m_nSamples(0), m_strFile(fname), m_fd(0)
651 {
652   if (wdq_channel.m_valid) {
653     m_nSamples = wdq_channel.r_wdq.m_nSamples;
654     m_nChannels = 1;
655     m_nBitsPerSample = 16;
656     m_nBytesPerSample = 2;
657     m_rate = wdq_channel.r_wdq.m_sample_rate;
658
659     double data_offset = 0, data_scale = 0;
660     if (g_ignore_zero) {
661       data_offset = -wdq_channel.m_min_scaled_data;
662       if (wdq_channel.m_max_scaled_data != wdq_channel.m_min_scaled_data)
663         data_scale = 65535. / (wdq_channel.m_max_scaled_data -
664                                wdq_channel.m_min_scaled_data);
665     } else {
666       double max_value = fabs(wdq_channel.m_max_scaled_data);
667       if (fabs (wdq_channel.m_min_scaled_data) > max_value)
668         max_value = fabs (wdq_channel.m_min_scaled_data);
669       if (max_value != 0.)
670         data_scale = 32767. / max_value;
671     }
672
673     if (g_debug) {
674       std::ostringstream os;
675       os << "  Wav data_scale: " << data_scale << ", data_offset: " << data_offset;
676       info_msg (os.str().c_str());
677     }
678
679     m_nHeaderBytes = 44;
680     m_nDataBytes = m_nSamples * m_nBytesPerSample * m_nChannels;
681     m_nFileBytes = m_nHeaderBytes + m_nDataBytes;
682
683     unsigned int nHeaderShortInts = m_nHeaderBytes / sizeof(signed short int);
684
685     m_data = new signed short int [m_nSamples + nHeaderShortInts];
686     signed short int* input = wdq_channel.m_data;
687     signed short int* output = &m_data[nHeaderShortInts];
688     double slope = wdq_channel.m_slope;
689     double intercept = wdq_channel.m_intercept;
690
691     if (! fill_header ())
692       return;
693
694     unsigned long int i;
695
696     for (i = 0; i < m_nSamples; i++) {
697       double value = input[i];
698       value = (slope * value) + intercept;
699       if (g_ignore_zero) {
700         value = (value + data_offset) * data_scale;
701         value += 0.5 - 32768;
702       } else {
703         value = value * data_scale;
704       }
705
706       signed short int v = static_cast<signed short int>(value);
707 #if WORDS_BIG_ENDIAN
708       unsigned char* p = reinterpret_cast<unsigned char*>(&v);
709       unsigned char c = p[0]; p[0] = p[1]; p[1] = c;
710 #endif
711       output[i] = v;
712     }
713   }
714
715   m_valid = true;
716 }
717
718
719 WavFile::~WavFile ()
720 {
721   if (m_fd != 0)
722     close (m_fd);
723
724   if (m_data != NULL)
725     delete m_data;
726 }
727
728 void
729 put_int4 (char* p, unsigned int n)
730 {
731   *p = n & 0xFF;
732   *(p+1) = 0xFF & (n >> 8);
733   *(p+2) = 0xFF & (n >> 16);
734   *(p+3) = 0xFF & (n >> 24);
735 }
736
737 void
738 put_int2 (char* p, unsigned short int n)
739 {
740   *p = n & 0xFF;
741   *(p+1) = 0xFF & (n >> 8);
742 }
743
744 bool
745 WavFile::fill_header ()
746 {
747   char* pData = reinterpret_cast<char*> (m_data);
748
749   strncpy (pData, "RIFF", 4);
750
751   // Length of file after 8 byte header
752   put_int4 (pData + 4, 36 + m_nDataBytes);
753
754   strncpy (pData + 8, "WAVEfmt ", 8);
755
756   // Length of header block
757   put_int4 (pData + 16, 0x10);
758
759   // Always 1
760   put_int2 (pData + 20, 1);
761
762   /* Number of channels */
763   put_int2 (pData + 22, m_nChannels);
764
765   // Sample Rate
766   put_int4 (pData + 24, static_cast<int> (m_rate + 0.5));
767
768   // Bytes per second
769   put_int4 (pData + 28, static_cast<int> (m_rate * m_nBytesPerSample + 0.5));
770
771   // Bytes per sample
772   put_int2 (pData + 32, m_nBytesPerSample * m_nChannels);
773
774   // Bits per sample
775   put_int2 (pData + 34, m_nBitsPerSample);
776
777   strncpy (pData + 36, "data", 4);
778
779   put_int4 (pData + 40, m_nDataBytes);
780
781   return true;
782 }
783
784 bool
785 WavFile::WriteFile ()
786 {
787   if (! m_valid)
788     return false;
789
790   if (m_fd == 0)
791     if ((m_fd = open (m_strFile.c_str(), O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, g_fileMode)) == 0) {
792       std::ostringstream os;
793       os << "Error opening output file " << m_strFile.c_str();
794       error_msg (os.str().c_str());
795       return false;
796     }
797
798   lseek (m_fd, 0, SEEK_SET);
799   if (write (m_fd, m_data, m_nFileBytes) != m_nFileBytes) {
800     error_msg ("Error writing file");
801     return false;
802   }
803
804   if (close (m_fd) < 0)
805     error_msg ("Error closing output file");
806
807   m_fd = 0;
808   return true;
809 }
810
811 #ifdef WIN32
812 #include <windows.h>
813 #include <mmsystem.h>
814 #elif defined(LINUX)
815 #include <sys/ioctl.h>
816 #include <sys/soundcard.h>
817 #endif
818
819 bool
820 WavFile::Play ()
821 {
822 #ifdef WIN32
823   if (PlaySound ((LPCSTR) m_data, 0, SND_MEMORY | SND_NODEFAULT))
824     return true;
825 #elif defined(LINUX)
826   int fd;
827   if ((fd = open ("/dev/dsp",O_WRONLY)) == -1) {
828     error_msg ("Error opening /dev/dsp");
829     return false;
830   }
831
832   int format = AFMT_S16_LE;
833   if (ioctl (fd, SNDCTL_DSP_SETFMT, &format) == -1) {
834     error_msg ("Error setting DSP format");
835     close(fd); return false;
836   }
837   if (format != AFMT_S16_LE) {
838     error_msg ("DSP Format not set");
839     close(fd); return false;
840   }
841
842   unsigned int channels = m_nChannels;
843   if (ioctl (fd, SNDCTL_DSP_CHANNELS, &format) == -1) {
844     error_msg ("Error setting number of channels");
845     close(fd); return false;
846   }
847   if (channels != m_nChannels) {
848     error_msg ("Number of channels not set");
849     close(fd); return false;
850   }
851
852   unsigned int speed = static_cast<int>(m_rate + 0.5);
853   if (ioctl (fd, SNDCTL_DSP_SPEED, &speed) == -1) {
854     error_msg ("Error setting sample rate");
855     close(fd); return false;
856   }
857   if (speed != m_rate && ! g_quiet) {
858     std::ostringstream os;
859     os << "Warning: Sample rate set to " << speed << ", not " << m_rate;
860     error_msg (os.str().c_str());
861   }
862
863   if (write (fd, reinterpret_cast<char*>(m_data) + m_nHeaderBytes, m_nDataBytes) !=
864       m_nDataBytes) {
865     error_msg ("Error writing audio samples");
866     close(fd); return false;
867   }
868
869   close (fd);
870   return true;
871 #else
872 #endif
873
874   return false;
875 }
876
877