diff options
Diffstat (limited to 'gr-blocks/lib/wavfile_sink_impl.cc')
-rw-r--r-- | gr-blocks/lib/wavfile_sink_impl.cc | 97 |
1 files changed, 83 insertions, 14 deletions
diff --git a/gr-blocks/lib/wavfile_sink_impl.cc b/gr-blocks/lib/wavfile_sink_impl.cc index 431919e7ed..09ff801822 100644 --- a/gr-blocks/lib/wavfile_sink_impl.cc +++ b/gr-blocks/lib/wavfile_sink_impl.cc @@ -46,20 +46,23 @@ namespace blocks { wavfile_sink::sptr wavfile_sink::make(const char* filename, int n_channels, unsigned int sample_rate, - int bits_per_sample) + int bits_per_sample, + bool append) { return gnuradio::get_initial_sptr(new wavfile_sink_impl( - filename, n_channels, sample_rate, bits_per_sample)); + filename, n_channels, sample_rate, bits_per_sample, append)); } wavfile_sink_impl::wavfile_sink_impl(const char* filename, int n_channels, unsigned int sample_rate, - int bits_per_sample) + int bits_per_sample, + bool append) : sync_block("wavfile_sink", io_signature::make(1, n_channels, sizeof(float)), io_signature::make(0, 0, 0)), d_h{}, // Init with zeros + d_append(append), d_fp(nullptr), d_new_fp(nullptr), d_updated(false) @@ -81,12 +84,23 @@ bool wavfile_sink_impl::open(const char* filename) gr::thread::scoped_lock guard(d_mutex); // we use the open system call to get access to the O_LARGEFILE flag. - int flags = OUR_O_LARGEFILE | OUR_O_BINARY | O_CREAT | O_WRONLY | O_TRUNC; - int fd; + int flags = OUR_O_LARGEFILE | OUR_O_BINARY; + + if (!d_append) { + // We are generating a new file. + flags |= O_CREAT | O_WRONLY | O_TRUNC; + } else { + flags |= O_RDWR; + } + int fd; if ((fd = ::open(filename, flags, 0664)) < 0) { - GR_LOG_ERROR(d_logger, - boost::format("::open: %s: %s") % filename % strerror(errno)); + if (errno == ENOENT) { + throw std::runtime_error("WAV append mode requires target file to exist"); + } else { + GR_LOG_ERROR(d_logger, + boost::format("::open: %s: %s") % filename % strerror(errno)); + } return false; } @@ -95,7 +109,7 @@ bool wavfile_sink_impl::open(const char* filename) d_new_fp = nullptr; } - if (!(d_new_fp = fdopen(fd, "wb"))) { + if (!(d_new_fp = fdopen(fd, d_append ? "r+b" : "wb"))) { GR_LOG_ERROR(d_logger, boost::format("fdopen: %s: %s") % filename % strerror(errno)); @@ -103,12 +117,20 @@ bool wavfile_sink_impl::open(const char* filename) return false; } - d_h.first_sample_pos = 44; - if (!wavheader_write( - d_new_fp, d_h.sample_rate, d_h.nchans, d_bytes_per_sample_new)) { - GR_LOG_ERROR(d_logger, boost::format("could not save WAV header")); - fclose(d_new_fp); - return false; + if (d_append) { + // We are appending to an existing file, be extra careful here. + if (!check_append_compat_file(d_new_fp)) { + fclose(d_new_fp); + return false; + } + } else { + d_h.first_sample_pos = 44; + if (!wavheader_write( + d_new_fp, d_h.sample_rate, d_h.nchans, d_bytes_per_sample_new)) { + GR_LOG_ERROR(d_logger, boost::format("could not save WAV header")); + fclose(d_new_fp); + return false; + } } d_updated = true; @@ -116,6 +138,47 @@ bool wavfile_sink_impl::open(const char* filename) return true; } +bool wavfile_sink_impl::check_append_compat_file(FILE* fp) +{ + + if (d_bytes_per_sample_new != d_h.bytes_per_sample) { + GR_LOG_ERROR(d_logger, + "bytes_per_sample is not allowed to change in append mode"); + return false; + } + + wav_header_info h_tmp{}; + std::swap(d_h, h_tmp); + + if (!wavheader_parse(fp, d_h)) { + GR_LOG_ERROR(d_logger, "invalid or incompatible WAV file"); + return false; + } + + if (d_h.sample_rate != h_tmp.sample_rate || d_h.nchans != h_tmp.nchans || + d_h.bytes_per_sample != h_tmp.bytes_per_sample) { + GR_LOG_ERROR(d_logger, + "existing WAV file is incompatible with configured options"); + return false; + } + + // TODO: use GR_FSEEK, GR_FTELL. + if (fseek(d_new_fp, 0, SEEK_END) != 0) { + return false; // This can only happen if the file disappears under our feet. + } + + long file_size = ftell(fp); + if (file_size - d_h.first_sample_pos != d_h.data_chunk_size) { + // This is complicated to properly implement for too little benefit. + GR_LOG_ERROR(d_logger, + "existing WAV file is incompatible (extra chunks at the end)"); + return false; + } + + return true; +} + + void wavfile_sink_impl::close() { gr::thread::scoped_lock guard(d_mutex); @@ -230,6 +293,12 @@ void wavfile_sink_impl::set_bits_per_sample_unlocked(int bits_per_sample) d_bytes_per_sample_new = bits_per_sample / 8; } +void wavfile_sink_impl::set_append(bool append) +{ + gr::thread::scoped_lock guard(d_mutex); + d_append = append; +} + void wavfile_sink_impl::set_sample_rate(unsigned int sample_rate) { gr::thread::scoped_lock guard(d_mutex); |