/* -*- 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 <gnuradio/blocks/wavfile.h> #include <gnuradio/io_signature.h> #include <stdexcept> #include <climits> #include <cstring> #include <cmath> #include <fcntl.h> #include <gnuradio/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) : sync_block("wavfile_sink", io_signature::make(1, n_channels, sizeof(float)), io_signature::make(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() { stop(); } bool wavfile_sink_impl::stop() { if(d_new_fp) { fclose(d_new_fp); d_new_fp = NULL; } close(); return true; } 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 */