X-Git-Url: http://git.kpe.io/?p=wdq2wav.git;a=blobdiff_plain;f=wdq2wav.cpp;h=2ea87bf9ce0af8c94e7dc8b9e3e246f2196b3cd9;hp=90120796e7df159c6ac4b459896e3c03ac29fc0b;hb=HEAD;hpb=94b57dc19edff11caf16bc311d247b899e69f421 diff --git a/wdq2wav.cpp b/wdq2wav.cpp index 9012079..2ea87bf 100644 --- a/wdq2wav.cpp +++ b/wdq2wav.cpp @@ -8,16 +8,6 @@ ** ** Copyright (c) 2003 Kevin Rosenberg ** -** NOTES -** Quick & Dirty hack, but efficient and good error checking -** Allocates memory equal to the size of channel (2 * number of samples) -** -** BUGS -** Need command line options for verbose and debug output -** Should comment the reading and writing of files to document the file formats -** -** $Id: wdq2wav.cpp,v 1.4 2003/01/21 03:05:03 kevin Exp $ -** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License (version 2) as ** published by the Free Software Foundation. @@ -32,94 +22,291 @@ ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ******************************************************************************/ +#include #include -static bool g_verbose = true; -static bool g_debug = true; +bool g_quiet = false; +bool g_verbose = false; +bool g_debug = false; +bool g_dry_run = false; +bool g_ignore_zero = false; +bool g_dont_demean = false; + + +#ifdef WIN32 +#define lseek _lseek +#define close _close +#define open _open +#define read _read +#define write _write +#define O_BINARY _O_BINARY +#define O_RDONLY _O_RDONLY +#define O_WRONLY _O_WRONLY +#define O_RDWR _O_RDRW +#define O_TRUNC _O_TRUNC +#define O_CREAT _O_CREAT +const int g_fileMode = _S_IWRITE | _S_IREAD; +struct fpos_t std::_Fpz = {0,0}; +#else +const int g_fileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; +// Define as NULL for non-Windows platforms +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#endif + void -error_msg (char *msg) +error_msg (const char *msg) { - fprintf (stderr, "%s\n",msg); + std::cerr << msg << std::endl; } void -info_msg (char* msg) +info_msg (const char* msg) { - fprintf (stdout, "%s\n", msg); + std::cout << msg << std::endl; } void -usage () +info_msg_sans_newline (const char* msg) +{ + std::cout << msg; +} + +const char* +fileBasename (const char* filename) +{ + const char* pslash = strrchr (filename, '/'); + const char* pbackslash = strrchr (filename, '\\'); + const char* p = filename; + if (pbackslash && (! pslash || pbackslash >= pslash)) + p = pbackslash+1; + else if (pslash && (! pbackslash || pslash >= pbackslash)) + p = pslash+1; + + return p; +} + +char * +str_rm_tail (char *str, const char* const charlist) +{ + int i; + + for (i = strlen(str) - 1; i >= 0; i--) + if (strchr (charlist, str[i]) != NULL) + str[i] = 0; + else + break; /* found non-specified char, all done */ + + return (str); +} + +char * +str_wrm_tail (char *str) { - error_msg ("usage: wdq2wav "); + return (str_rm_tail(str, "\b\t\n\r")); +} + + +void +usage (const char* progname) +{ + std::cout << "usage: " << fileBasename (progname) << " [OPTIONS] \n"; + std::cout << "OPTIONS\n"; + std::cout << " -p Play channel through audio system\n"; + std::cout << " -q Supress all messages\n"; + std::cout << " -z Scale output without preserving zero point\n"; + std::cout << " -m Do not demean the data (don't subtract the mean value from each sample)\n"; + std::cout << " -n Dry run (do not create any output\n"; + std::cout << " -v Verbose mode\n"; + std::cout << " -d Debug mode\n"; + std::cout << " -h Print this help message\n"; } -bool wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname); int main (int argc, char *argv[]) { - if (argc != 4) { - usage(); - exit(1); + int c; + bool play = false; + + const char* progname = argv[0]; + + while ((c = getopt (argc, argv, "rqvzmndph")) != -1) { + switch (c) { + case 'q': + g_quiet = true; + break; + case 'm': + g_dont_demean = true; + break; + case 'n': + g_dry_run = true; + break; + case 'z': + g_ignore_zero = true; + break; + case 'v': + g_verbose = true; + break; + case 'd': + g_debug = true; + break; + case 'p': + play = true; + break; + case 'h': + usage (progname); + return (0); + case '?': + default: + usage (progname); + return (1); + } + } + + argc -= optind; + argv += optind; + if (argc > 3) { + std::cerr << "Too many parameters\n"; + usage (progname); + return (1); + } + + char wdq_fname[MAX_INPUT_STR]; + if (argc >= 1) + strncpy (wdq_fname, argv [0], MAX_INPUT_STR); + else { + std::cout << "Enter input WinDAQ filename: "; + std::cin.getline (wdq_fname, MAX_INPUT_STR); } - char *wdq_fname = argv[1]; + char channel_buf [MAX_INPUT_STR]; + if (argc >= 2) + strncpy (channel_buf, argv[1], MAX_INPUT_STR); + else { + std::cout << "Enter channel number: "; + std::cin.getline (channel_buf, MAX_INPUT_STR); + } + char *channel_endptr; - int channel = (int) strtol (argv[2], &channel_endptr, 10); - char *wav_fname = argv[3]; - + int channel = static_cast(strtol (channel_buf, &channel_endptr, 10)); if (*channel_endptr != 0) { std::ostringstream os; - os << "Error: Channel " << argv[2] << " is not an integer"; + os << "Error: Channel " << channel_buf << " is not an integer"; error_msg (os.str().c_str()); - usage(); - return(1); + usage (progname); + return (1); } + + char wav_fname[MAX_INPUT_STR]; + if (! g_dry_run) { + if (argc >= 3) + strncpy (wav_fname, argv[2], MAX_INPUT_STR); + else { + std::cout << "Enter output wav filename: "; + std::cin.getline (wav_fname, MAX_INPUT_STR); + } + } + if (! wdq2wav (wdq_fname, channel, wav_fname, play)) + return 1; - if (! wdq2wav (wdq_fname, channel, wav_fname)) - return 1; - - return 0; + return 0; } bool -wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname) +wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname, bool play) { WindaqFile wdq (wdq_fname); if (! wdq.ReadHeader()) { + if (wdq.m_error.size()) { + std::ostringstream os; + os << "Error reading file " << wdq_fname << ": " << wdq.m_error.c_str(); + error_msg (os.str().c_str()); + } else { + std::ostringstream os; + os << "Error reading file " << wdq_fname; + error_msg (os.str().c_str()); + } return false; } - if (g_verbose) { + + if (wdq.any_packed_channels()) { + std::ostringstream os; + os << "File contains 'packed' channels." << std::endl; + os << "Convert to 'Advanced CODAS headers' before processing with wdq2wav."; + error_msg (os.str().c_str()); + return false; + } + + if (! g_quiet || g_verbose || g_debug) { + std::ostringstream os1; + os1 << "File: " << wdq_fname; + info_msg (os1.str().c_str()); std::ostringstream os; - os << wdq_fname << ": Samples " << wdq.m_nSamples << - ", Channels " << wdq.m_nChannels << - ", Sample Rate " << wdq.m_sample_rate; - info_msg (os.str().c_str()); + os << "Format: " << (wdq.m_bLegacy_format ? "Legacy" : "Non-legacy"); + info_msg(os.str().c_str()); + std::ostringstream os2; + time_t time = wdq.m_time_acq_start; + struct tm* tm = gmtime (&time); + os2 << "Time File Creation: " << asctime(tm); + info_msg_sans_newline (os2.str().c_str()); + std::ostringstream os3; + time = wdq.m_time_acq_stop; + tm = gmtime (&time); + os3 << "Time File Written: " << asctime(tm); + info_msg_sans_newline (os3.str().c_str()); + std::ostringstream os4; + os4 << "Samples: " << wdq.m_nSamples << + ", Channels: " << wdq.m_nChannels << + ", Sample Rate: " << wdq.m_sample_rate; + info_msg (os4.str().c_str()); } - + WindaqChannel wdq_channel (wdq, channel); if (! wdq_channel.m_valid) { error_msg ("Error reading data from channel"); return false; } - if (g_verbose) { + if (! g_quiet || g_verbose || g_debug) { std::ostringstream os1; - os1 << "Channel units: " << wdq_channel.m_units.c_str(); + os1 << "Channel " << channel << ", Units: " << wdq_channel.m_units + << ", Minimum: " << wdq_channel.m_min_raw_data + << " (" << wdq_channel.raw2measured(wdq_channel.m_min_raw_data) + << "), Maximum: " << wdq_channel.m_max_raw_data + << " (" << wdq_channel.raw2measured(wdq_channel.m_max_raw_data) << ")"; info_msg (os1.str().c_str()); - std::ostringstream os2; - os2 << "Raw data: minimum " << wdq_channel.m_min_raw_data << - ", maximum " << wdq_channel.m_max_raw_data; - info_msg (os2.str().c_str()); - std::ostringstream os3; - os3 << "Slope " << wdq_channel.m_slope << - ", Intercept " << wdq_channel.m_intercept; - info_msg (os3.str().c_str()); + + std::cout << "Mean: " << wdq_channel.raw2measured(wdq_channel.m_raw_mean) << + " " << wdq_channel.m_units; + } + + if (! g_dry_run && !g_dont_demean) { + std::cout << " (removing)\n"; + int mean = nearest(wdq_channel.m_raw_mean); + for (unsigned int i = 0; i < wdq.m_nSamples; i++) + wdq_channel.m_data[i] -= mean; + } else { + std::cout << " (not removing)\n"; } + if (g_debug) { + std::ostringstream os4; + os4 << " Scaled minimum: " << wdq_channel.m_min_scaled_data << + ", maximum: " << wdq_channel.m_max_scaled_data; + info_msg (os4.str().c_str()); + std::ostringstream os5; + os5 << " Slope " << wdq_channel.m_slope << + ", Intercept " << wdq_channel.m_intercept; + info_msg (os5.str().c_str()); + } + + if (g_dry_run) + return true; + WavFile wav (wdq_channel, wav_fname); if (! wav.m_valid) { @@ -132,12 +319,15 @@ wdq2wav (const char* wdq_fname, const int channel, const char *wav_fname) return false; } + if (play) + wav.Play(); + return true; } WindaqFile::WindaqFile (const char* fname) - : m_fd(0), m_nChannels(0), m_nSamples(0), m_sample_rate(0), m_valid(false), + : m_valid(false), m_fd(0), m_nChannels(0), m_nSamples(0), m_sample_rate(0), m_strFile (fname) { } @@ -148,62 +338,174 @@ WindaqFile::~WindaqFile () close (m_fd); } -bool -WindaqFile::ReadHeader () +bool read_int1 (int fd, unsigned char& n) +{ + unsigned char tmp1; + if (read (fd, &tmp1, 1) != 1) + return false; + + n = tmp1; + return true; +} + +bool read_int2 (int fd, unsigned short int& n) +{ + unsigned char tmp1; + unsigned int tmp2; + if (read (fd, &tmp1, 1) != 1) + return false; + tmp2 = tmp1; + if (read (fd, &tmp1, 1) != 1) + return false; + + n = tmp2 + (tmp1 * 256); + return true; +} + +bool read_int4 (int fd, unsigned int& n) { - unsigned short int tmp2; unsigned int tmp4; + unsigned short int tmp2; + if (! read_int2 (fd, tmp2)) + return false; + tmp4 = tmp2; + if (! read_int2 (fd, tmp2)) + return false; + n = tmp4 + (tmp2 * 65536); + return true; +} + +bool read_float8 (int fd, double& f) +{ + unsigned char p[8]; + if (read (fd, p, 8) != 8) + return false; + +#if WORDS_BIG_ENDIAN + unsigned char c; + c = p[0]; p[0] = p[7]; p[7] = c; + c = p[1]; p[1] = p[6]; p[6] = c; + c = p[2]; p[2] = p[5]; p[5] = c; + c = p[3]; p[3] = p[4]; p[4] = c; +#endif + + double *pd = reinterpret_cast(&p[0]); + f = *pd; + + return true; +} + +bool +WindaqFile::ReadHeader () +{ m_valid = false; - if ((m_fd = open (m_strFile.c_str(), O_RDONLY)) == 0) + if ((m_fd = open (m_strFile.c_str(), O_RDONLY | O_BINARY)) < 0) { + m_error = "Unable to open file"; return false; + } lseek (0, 0, SEEK_SET); - if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error reading file"); + unsigned short int element1; + if (! read_int2 (m_fd, element1)) return false; - } - m_nChannels = tmp2 & 0x1f; - m_sr_denom = (tmp2 & 0x7fff) >> 5; - m_sr_numer = (tmp2 & 0x8000) << 1; - - if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error reading file"); + + short unsigned int byte1 = (element1 & 0xFF00) >> 8; + short unsigned int byte2 = element1 & 0xFF; + if (byte1 == 0 || byte1 == 1) { + m_bLegacy_format = false; + m_sr_denom = m_sr_numer = 0; + } else { + m_sr_denom = (element1 & 0x7fff) >> 5; + m_sr_numer = (element1 & 0x8000) << 1; + m_bLegacy_format = true; + } + unsigned short int element2; + if (! read_int2 (m_fd, element2)) return false; - } - m_sr_numer |= tmp2; - m_sample_rate = (double) m_sr_numer / (double) (m_sr_denom * m_nChannels); - if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error reading file"); + if (m_bLegacy_format) + m_sr_numer |= element2; + + unsigned char element3; + if (! read_int1 (m_fd, element3)) return false; - } - m_channel_offset = tmp2 & 0xFF; - m_nBytes_channel_header = tmp2 >> 8; - - if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error reading file"); + m_channel_offset = element3; + if (g_debug) + std::cout << "Channel offset: " << m_channel_offset << std::endl; + + unsigned char element4; + if (! read_int1 (m_fd, element4)) return false; - } - m_nHeader_bytes = tmp2; - - if (read (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) { - error_msg ("Error reading file"); + m_nBytes_channel_header = element4; + if (g_debug) + std::cout << "Channel header bytes: " << m_nBytes_channel_header << std::endl; + + unsigned short int element5; + if (! read_int2 (m_fd, element5)) return false; - } - m_nData_bytes = tmp4; + m_nHeader_bytes = element5; + if (g_debug) + std::cout << "Header bytes: " << m_nHeader_bytes << std::endl; + + m_nMaxChannels = (m_nHeader_bytes - 112) / 36; + if (m_nMaxChannels >= 144) + m_nChannels = byte2 & 0xFF; + else + m_nChannels = byte2 & 0x1F; + + unsigned int element6; + if (! read_int4 (m_fd, element6)) + return false; + m_nData_bytes = element6; + if (g_debug) + std::cout << "Data bytes: " << m_nData_bytes << std::endl; + m_nSamples = (m_nData_bytes / m_nChannels) / 2; + lseek (m_fd, 28, SEEK_SET); + double element13; + if (! read_float8 (m_fd, element13)) + return false; + m_time_between_channel_samples = element13; + + if (m_bLegacy_format) + m_sample_rate = (double) m_sr_numer / (double) (m_sr_denom * m_nChannels); + else + m_sample_rate = (double) 1 / m_time_between_channel_samples; + + lseek (m_fd, 36, SEEK_SET); + if (! read_int4 (m_fd, m_time_acq_start)) + return false; + + if (! read_int4 (m_fd, m_time_acq_stop)) + return false; + + lseek (m_fd, 100, SEEK_SET); + unsigned short int element27; + if (! read_int2 (m_fd, element27)) + return false; + + m_bHires = (element27 & 0x0001) ? true : false; + if (g_debug) { + std::cout << "High resolution: "; + if (m_bHires) + std::cout << "Yes"; + else + std::cout << "No"; + + std::cout << std::endl; + } + // Verify Windaq signature lseek (m_fd, m_nHeader_bytes - 2, SEEK_SET); - if (read (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error reading file"); + unsigned short int element35; + if (! read_int2 (m_fd, element35)) return false; - } - if (tmp2 != 0x8001) { + + if (element35 != 0x8001) { std::ostringstream os; - os << m_strFile << " is not a WinDAQ file"; - error_msg (os.str().c_str()); + m_error = "Incorrect signagure: file is not a valid WinDAQ file"; return false; } @@ -211,19 +513,44 @@ WindaqFile::ReadHeader () return true; } - +bool +WindaqFile::any_packed_channels () +{ + for (int iChannel = 0; iChannel < m_nChannels; iChannel++) + if (is_channel_packed (iChannel)) + return true; + + return false; +} + +bool +WindaqFile::is_channel_packed (const int channel) +{ + long iStart = m_channel_offset + channel * m_nBytes_channel_header; + + lseek (m_fd, iStart + 31, SEEK_SET); + unsigned char iReadings_per_data_point; + if (! read_int1 (m_fd, iReadings_per_data_point)) + return false; + + if (iReadings_per_data_point > 1) + return true; + + return false; +} + WindaqChannel::WindaqChannel (WindaqFile& wdq, const int channel) : r_wdq(wdq), m_data(0), m_slope(0), m_intercept (0), m_channel(channel), m_valid(false) { if (wdq.m_valid) { if (channel >= 1 && channel <= wdq.m_nChannels) { - m_valid = true; - read_channel_data(); + if (read_channel_data()) + m_valid = true; } else { std::ostringstream os; os << "Channel " << channel << " is invalid, valid range 1-" << - wdq.m_nChannels; + wdq.m_nChannels; error_msg (os.str().c_str()); } } @@ -238,30 +565,18 @@ WindaqChannel::~WindaqChannel () bool WindaqChannel::read_channel_data () { - unsigned short int tmp2; - unsigned int tmp4; - double float8; - int fd = r_wdq.m_fd; - if (! m_valid) - return false; - m_data = new signed short int [r_wdq.m_nSamples * 2]; - lseek (fd, r_wdq.m_channel_offset + 8 + - (m_channel - 1) * r_wdq.m_nBytes_channel_header, - SEEK_SET); - if (read (fd, &float8, sizeof(float8)) != sizeof(float8)) { - error_msg ("Error reading file"); + lseek (fd, r_wdq.m_channel_offset + 8 + + (m_channel - 1) * r_wdq.m_nBytes_channel_header, + SEEK_SET); + if (! read_float8 (fd, m_slope)) return false; - } - m_slope = float8; - if (read (fd, &float8, sizeof(float8)) != sizeof(float8)) { - error_msg ("Error reading file"); + + if (! read_float8 (fd, m_intercept)) return false; - } - m_intercept = float8; char units[7]; units[6] = 0; @@ -270,34 +585,47 @@ WindaqChannel::read_channel_data () return false; } m_units = units; - - unsigned int row_bytes = 2 * r_wdq.m_nChannels; - signed short int sample_row [row_bytes]; - + + long int row_bytes = 2 * r_wdq.m_nChannels; + + signed short int *sample_row = new signed short int [row_bytes]; + signed short int* psample = &sample_row[m_channel - 1]; lseek (fd, r_wdq.m_nHeader_bytes, SEEK_SET); - long int i; - signed short int data_max, data_min; + unsigned long int i; + signed short int data_max = 0, data_min = 0; + double total_data = 0; for (i = 0; i < r_wdq.m_nSamples; i++) { if (read (fd, sample_row, row_bytes) != row_bytes) { std::ostringstream os; os << "Error reading file at " << i; error_msg (os.str().c_str()); + delete sample_row; return false; } - - signed short int value = *psample >> 2; + signed short int v = *psample; + +#if WORDS_BIG_ENDIAN + unsigned char* p = reinterpret_cast(&v); + unsigned char c = p[0]; p[0] = p[1]; p[1] = c; +#endif + + signed short int value = v; + if (! r_wdq.m_bHires) + value >>= 2; + m_data[i] = value; - + total_data += value; + if (i == 0) { data_max = value; data_min = value; } else { if (value > data_max) - data_max = value; + data_max = value; else if (value < data_min) - data_min = value; + data_min = value; } } @@ -306,6 +634,9 @@ WindaqChannel::read_channel_data () m_max_scaled_data = (m_slope * data_max) + m_intercept; m_min_scaled_data = (m_slope * data_min) + m_intercept; + m_raw_mean = total_data / static_cast(r_wdq.m_nSamples); + + delete sample_row; return true; } @@ -319,28 +650,60 @@ WavFile::WavFile (WindaqChannel& wdq_channel, const char* fname) m_nBitsPerSample = 16; m_nBytesPerSample = 2; m_rate = wdq_channel.r_wdq.m_sample_rate; - m_data = new signed short int [m_nSamples]; - - double data_offset = -wdq_channel.m_min_scaled_data; - double data_scale = 0.; - if (wdq_channel.m_max_scaled_data != wdq_channel.m_min_scaled_data) - data_scale = 65535. / (wdq_channel.m_max_scaled_data - - wdq_channel.m_min_scaled_data); - - if (g_debug) - printf ("data_scale: %f, data_offset: %f\n", data_scale, data_offset); - + + double data_offset = 0, data_scale = 0; + if (g_ignore_zero) { + data_offset = -wdq_channel.m_min_scaled_data; + if (wdq_channel.m_max_scaled_data != wdq_channel.m_min_scaled_data) + data_scale = 65535. / (wdq_channel.m_max_scaled_data - + wdq_channel.m_min_scaled_data); + } else { + double max_value = fabs(wdq_channel.m_max_scaled_data); + if (fabs (wdq_channel.m_min_scaled_data) > max_value) + max_value = fabs (wdq_channel.m_min_scaled_data); + if (max_value != 0.) + data_scale = 32767. / max_value; + } + + if (g_debug) { + std::ostringstream os; + os << " Wav data_scale: " << data_scale << ", data_offset: " << data_offset; + info_msg (os.str().c_str()); + } + + m_nHeaderBytes = 44; + m_nDataBytes = m_nSamples * m_nBytesPerSample * m_nChannels; + m_nFileBytes = m_nHeaderBytes + m_nDataBytes; + + unsigned int nHeaderShortInts = m_nHeaderBytes / sizeof(signed short int); + + m_data = new signed short int [m_nSamples + nHeaderShortInts]; signed short int* input = wdq_channel.m_data; + signed short int* output = &m_data[nHeaderShortInts]; double slope = wdq_channel.m_slope; double intercept = wdq_channel.m_intercept; - - long int i; + + if (! fill_header ()) + return; + + unsigned long int i; + for (i = 0; i < m_nSamples; i++) { double value = input[i]; value = (slope * value) + intercept; - value = (value + data_offset) * data_scale; - value += 0.5 - 32768; - m_data[i] = static_cast(value); + if (g_ignore_zero) { + value = (value + data_offset) * data_scale; + value += 0.5 - 32768; + } else { + value = value * data_scale; + } + + signed short int v = static_cast(value); +#if WORDS_BIG_ENDIAN + unsigned char* p = reinterpret_cast(&v); + unsigned char c = p[0]; p[0] = p[1]; p[1] = c; +#endif + output[i] = v; } } @@ -352,114 +715,158 @@ WavFile::~WavFile () { if (m_fd != 0) close (m_fd); - + if (m_data != NULL) delete m_data; } +void +put_int4 (char* p, unsigned int n) +{ + *p = n & 0xFF; + *(p+1) = 0xFF & (n >> 8); + *(p+2) = 0xFF & (n >> 16); + *(p+3) = 0xFF & (n >> 24); +} + +void +put_int2 (char* p, unsigned short int n) +{ + *p = n & 0xFF; + *(p+1) = 0xFF & (n >> 8); +} + bool -WavFile::WriteFile () +WavFile::fill_header () { - unsigned short int tmp2; - unsigned int tmp4; - unsigned long data_bytes = m_nSamples * 2; + char* pData = reinterpret_cast (m_data); + + strncpy (pData, "RIFF", 4); + + // Length of file after 8 byte header + put_int4 (pData + 4, 36 + m_nDataBytes); + + strncpy (pData + 8, "WAVEfmt ", 8); + + // Length of header block + put_int4 (pData + 16, 0x10); + + // Always 1 + put_int2 (pData + 20, 1); + + /* Number of channels */ + put_int2 (pData + 22, m_nChannels); + // Sample Rate + put_int4 (pData + 24, static_cast (m_rate + 0.5)); + + // Bytes per second + put_int4 (pData + 28, static_cast (m_rate * m_nBytesPerSample + 0.5)); + + // Bytes per sample + put_int2 (pData + 32, m_nBytesPerSample * m_nChannels); + + // Bits per sample + put_int2 (pData + 34, m_nBitsPerSample); + + strncpy (pData + 36, "data", 4); + + put_int4 (pData + 40, m_nDataBytes); + + return true; +} + +bool +WavFile::WriteFile () +{ if (! m_valid) return false; if (m_fd == 0) - if ((m_fd = open (m_strFile.c_str(), O_WRONLY | O_TRUNC | O_CREAT, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == 0) { + if ((m_fd = open (m_strFile.c_str(), O_WRONLY | O_BINARY | O_TRUNC | O_CREAT, g_fileMode)) == 0) { std::ostringstream os; os << "Error opening output file " << m_strFile.c_str(); - error_msg (os); + error_msg (os.str().c_str()); return false; } lseek (m_fd, 0, SEEK_SET); - if (write (m_fd, "RIFF", 4) != 4) { + if (write (m_fd, m_data, m_nFileBytes) != m_nFileBytes) { error_msg ("Error writing file"); return false; } - /* Length of file post 8 byte header */ - tmp4 = 36 + data_bytes; - if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) { - error_msg ("Error writing file"); - return false; - } + if (close (m_fd) < 0) + error_msg ("Error closing output file"); - if (write (m_fd, "WAVEfmt ", 8) != 8) { - error_msg ("Error writing file"); - return false; - } - tmp4 = 0x10; - if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) { - error_msg ("Error writing file"); + m_fd = 0; + return true; +} - } - tmp2 = 1; - if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error writing file"); - return false; - } - /* Number of channels */ - tmp2 = m_nChannels; - if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error writing file"); +#ifdef _WIN32 +#include +#include +#elif defined(__linux__) +#include +#include +#endif + +bool +WavFile::Play () +{ +#ifdef _WIN32 + if (PlaySound ((LPCSTR) m_data, 0, SND_MEMORY | SND_NODEFAULT)) + return true; +#elif defined(__linux__) + int fd; + if ((fd = open ("/dev/dsp",O_WRONLY)) == -1) { + error_msg ("Error opening /dev/dsp"); return false; } - tmp4 = static_cast (m_rate + 0.5); - /* Sample rate */ - if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) { - error_msg ("Error writing file"); - return false; + + int format = AFMT_S16_LE; + if (ioctl (fd, SNDCTL_DSP_SETFMT, &format) == -1) { + error_msg ("Error setting DSP format"); + close(fd); return false; } - tmp4 = static_cast (m_rate * m_nBytesPerSample + 0.5); - /* Bytes per second */ - if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) { - error_msg ("Error writing file"); - return false; + if (format != AFMT_S16_LE) { + error_msg ("DSP Format not set"); + close(fd); return false; } - tmp2 = m_nBytesPerSample * m_nChannels; - /* Bytes per sample */ - if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error writing file"); - return false; + + unsigned int channels = m_nChannels; + if (ioctl (fd, SNDCTL_DSP_CHANNELS, &format) == -1) { + error_msg ("Error setting number of channels"); + close(fd); return false; } - tmp2 = m_nBitsPerSample; - /* Bits per sample */ - if (write (m_fd, &tmp2, sizeof(tmp2)) != sizeof(tmp2)) { - error_msg ("Error writing file"); - return false; + if (channels != m_nChannels) { + error_msg ("Number of channels not set"); + close(fd); return false; } - if (write (m_fd, "data", 4) != 4) { - error_msg ("Error writing file"); - return false; + + unsigned int speed = static_cast(m_rate + 0.5); + if (ioctl (fd, SNDCTL_DSP_SPEED, &speed) == -1) { + error_msg ("Error setting sample rate"); + close(fd); return false; } - tmp4 = data_bytes; - /* Data length */ - if (write (m_fd, &tmp4, sizeof(tmp4)) != sizeof(tmp4)) { - error_msg ("Error writing file"); - return false; + if (speed != m_rate && ! g_quiet) { + std::ostringstream os; + os << "Warning: Sample rate set to " << speed << ", not " << m_rate; + error_msg (os.str().c_str()); } - long int i; - for (i = 0; i < m_nSamples; i++) { - signed short int stmp2 = m_data[i]; - if (write (m_fd, &stmp2, sizeof(stmp2)) != sizeof(stmp2)) { - error_msg ("Error writing file"); - return false; - } + if (write (fd, reinterpret_cast(m_data) + m_nHeaderBytes, m_nDataBytes) != + m_nDataBytes) { + error_msg ("Error writing audio samples"); + close(fd); return false; } - - if (close (m_fd) < 0) - error_msg ("Error closing output file"); - - m_fd = 0; + + close (fd); return true; +#else +#endif + + return false; } - -