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