diff options
Diffstat (limited to 'gr-blocks/lib/wavfile_sink_impl.cc')
-rw-r--r-- | gr-blocks/lib/wavfile_sink_impl.cc | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/gr-blocks/lib/wavfile_sink_impl.cc b/gr-blocks/lib/wavfile_sink_impl.cc new file mode 100644 index 0000000000..4591b1f9fa --- /dev/null +++ b/gr-blocks/lib/wavfile_sink_impl.cc @@ -0,0 +1,293 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2006-2011,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "wavfile_sink_impl.h" +#include <blocks/wavfile.h> +#include <gr_io_signature.h> +#include <stdexcept> +#include <climits> +#include <cstring> +#include <cmath> +#include <fcntl.h> +#include <thread/thread.h> +#include <boost/math/special_functions/round.hpp> + +// win32 (mingw/msvc) specific +#ifdef HAVE_IO_H +#include <io.h> +#endif +#ifdef O_BINARY +#define OUR_O_BINARY O_BINARY +#else +#define OUR_O_BINARY 0 +#endif + +// should be handled via configure +#ifdef O_LARGEFILE +#define OUR_O_LARGEFILE O_LARGEFILE +#else +#define OUR_O_LARGEFILE 0 +#endif + +namespace gr { + namespace blocks { + + wavfile_sink::sptr + wavfile_sink::make(const char *filename, + int n_channels, + unsigned int sample_rate, + int bits_per_sample) + { + return gnuradio::get_initial_sptr + (new wavfile_sink_impl(filename, n_channels, + sample_rate, bits_per_sample)); + } + + wavfile_sink_impl::wavfile_sink_impl(const char *filename, + int n_channels, + unsigned int sample_rate, + int bits_per_sample) + : gr_sync_block("wavfile_sink", + gr_make_io_signature(1, n_channels, sizeof(float)), + gr_make_io_signature(0, 0, 0)), + d_sample_rate(sample_rate), d_nchans(n_channels), + d_fp(0), d_new_fp(0), d_updated(false) + { + if(bits_per_sample != 8 && bits_per_sample != 16) { + throw std::runtime_error("Invalid bits per sample (supports 8 and 16)"); + } + d_bytes_per_sample = bits_per_sample / 8; + d_bytes_per_sample_new = d_bytes_per_sample; + + if(!open(filename)) { + throw std::runtime_error("can't open file"); + } + + if(bits_per_sample == 8) { + d_max_sample_val = 0xFF; + d_min_sample_val = 0; + d_normalize_fac = d_max_sample_val/2; + d_normalize_shift = 1; + } + else { + d_max_sample_val = 0x7FFF; + d_min_sample_val = -0x7FFF; + d_normalize_fac = d_max_sample_val; + d_normalize_shift = 0; + if(bits_per_sample != 16) { + fprintf(stderr, "Invalid bits per sample value requested, using 16"); + } + } + } + + 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 fd; + if((fd = ::open(filename, + O_WRONLY|O_CREAT|O_TRUNC|OUR_O_LARGEFILE|OUR_O_BINARY, + 0664)) < 0) { + perror(filename); + return false; + } + + if(d_new_fp) { // if we've already got a new one open, close it + fclose(d_new_fp); + d_new_fp = 0; + } + + if((d_new_fp = fdopen (fd, "wb")) == NULL) { + perror(filename); + ::close(fd); // don't leak file descriptor if fdopen fails. + return false; + } + d_updated = true; + + if(!wavheader_write(d_new_fp, + d_sample_rate, + d_nchans, + d_bytes_per_sample_new)) { + fprintf(stderr, "[%s] could not write to WAV file\n", __FILE__); + exit(-1); + } + + return true; + } + + void + wavfile_sink_impl::close() + { + gr::thread::scoped_lock guard(d_mutex); + + if(!d_fp) + return; + + close_wav(); + } + + void + wavfile_sink_impl::close_wav() + { + unsigned int byte_count = d_sample_count * d_bytes_per_sample; + + wavheader_complete(d_fp, byte_count); + + fclose(d_fp); + d_fp = NULL; + } + + wavfile_sink_impl::~wavfile_sink_impl() + { + if(d_new_fp) { + fclose(d_new_fp); + } + + close(); + } + + int + wavfile_sink_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + float **in = (float**)&input_items[0]; + int n_in_chans = input_items.size(); + + short int sample_buf_s; + + int nwritten; + + gr::thread::scoped_lock guard(d_mutex); // hold mutex for duration of this block + do_update(); // update: d_fp is reqd + if(!d_fp) // drop output on the floor + return noutput_items; + + for(nwritten = 0; nwritten < noutput_items; nwritten++) { + for(int chan = 0; chan < d_nchans; chan++) { + // Write zeros to channels which are in the WAV file + // but don't have any inputs here + if(chan < n_in_chans) { + sample_buf_s = + convert_to_short(in[chan][nwritten]); + } + else { + sample_buf_s = 0; + } + + wav_write_sample(d_fp, sample_buf_s, d_bytes_per_sample); + + if(feof(d_fp) || ferror(d_fp)) { + fprintf(stderr, "[%s] file i/o error\n", __FILE__); + close(); + exit(-1); + } + d_sample_count++; + } + } + + return nwritten; + } + + short int + wavfile_sink_impl::convert_to_short(float sample) + { + sample += d_normalize_shift; + sample *= d_normalize_fac; + if(sample > d_max_sample_val) { + sample = d_max_sample_val; + } + else if(sample < d_min_sample_val) { + sample = d_min_sample_val; + } + + return (short int)boost::math::iround(sample); + } + + void + wavfile_sink_impl::set_bits_per_sample(int bits_per_sample) + { + gr::thread::scoped_lock guard(d_mutex); + if(bits_per_sample == 8 || bits_per_sample == 16) { + d_bytes_per_sample_new = bits_per_sample / 8; + } + } + + void + wavfile_sink_impl::set_sample_rate(unsigned int sample_rate) + { + gr::thread::scoped_lock guard(d_mutex); + d_sample_rate = sample_rate; + } + + int + wavfile_sink_impl::bits_per_sample() + { + return d_bytes_per_sample_new; + } + + unsigned int + wavfile_sink_impl::sample_rate() + { + return d_sample_rate; + } + + void + wavfile_sink_impl::do_update() + { + if(!d_updated) { + return; + } + + if(d_fp) { + close_wav(); + } + + d_fp = d_new_fp; // install new file pointer + d_new_fp = 0; + d_sample_count = 0; + d_bytes_per_sample = d_bytes_per_sample_new; + + if(d_bytes_per_sample == 1) { + d_max_sample_val = UCHAR_MAX; + d_min_sample_val = 0; + d_normalize_fac = d_max_sample_val/2; + d_normalize_shift = 1; + } + else if(d_bytes_per_sample == 2) { + d_max_sample_val = SHRT_MAX; + d_min_sample_val = SHRT_MIN; + d_normalize_fac = d_max_sample_val; + d_normalize_shift = 0; + } + + d_updated = false; + } + + } /* namespace blocks */ +} /* namespace gr */ |