diff options
Diffstat (limited to 'gr-blocks/lib')
-rw-r--r-- | gr-blocks/lib/CMakeLists.txt | 4 | ||||
-rw-r--r-- | gr-blocks/lib/tagged_file_sink_impl.cc | 235 | ||||
-rw-r--r-- | gr-blocks/lib/tagged_file_sink_impl.h | 61 | ||||
-rw-r--r-- | gr-blocks/lib/wavfile.cc | 258 | ||||
-rw-r--r-- | gr-blocks/lib/wavfile_sink_impl.cc | 293 | ||||
-rw-r--r-- | gr-blocks/lib/wavfile_sink_impl.h | 95 | ||||
-rw-r--r-- | gr-blocks/lib/wavfile_source_impl.cc | 174 | ||||
-rw-r--r-- | gr-blocks/lib/wavfile_source_impl.h | 70 |
8 files changed, 1190 insertions, 0 deletions
diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt index 4506eaca39..21a21b0e2a 100644 --- a/gr-blocks/lib/CMakeLists.txt +++ b/gr-blocks/lib/CMakeLists.txt @@ -138,6 +138,7 @@ list(APPEND gr_blocks_sources ${generated_sources} count_bits.cc fxpt.cc + wavfile.cc add_ff_impl.cc bin_statistics_f_impl.cc burst_tagger_impl.cc @@ -200,6 +201,7 @@ list(APPEND gr_blocks_sources streams_to_stream_impl.cc streams_to_vector_impl.cc stretch_ff_impl.cc + tagged_file_sink_impl.cc tagged_stream_to_pdu_impl.cc threshold_ff_impl.cc throttle_impl.cc @@ -212,6 +214,8 @@ list(APPEND gr_blocks_sources vco_f_impl.cc vector_to_stream_impl.cc vector_to_streams_impl.cc + wavfile_sink_impl.cc + wavfile_source_impl.cc ) #Add Windows DLL resource file if using MSVC diff --git a/gr-blocks/lib/tagged_file_sink_impl.cc b/gr-blocks/lib/tagged_file_sink_impl.cc new file mode 100644 index 0000000000..a2e9ab3853 --- /dev/null +++ b/gr-blocks/lib/tagged_file_sink_impl.cc @@ -0,0 +1,235 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,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 "tagged_file_sink_impl.h" +#include <gr_io_signature.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdexcept> +#include <iostream> + +#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 { + + tagged_file_sink::sptr + tagged_file_sink::make(size_t itemsize, double samp_rate) + { + return gnuradio::get_initial_sptr + (new tagged_file_sink_impl(itemsize, samp_rate)); + } + + tagged_file_sink_impl::tagged_file_sink_impl(size_t itemsize, double samp_rate) + : gr_sync_block("tagged_file_sink", + gr_make_io_signature(1, 1, itemsize), + gr_make_io_signature(0, 0, 0)), + d_itemsize (itemsize), d_n(0), d_sample_rate(samp_rate) + { + d_state = NOT_IN_BURST; + d_last_N = 0; + d_timeval = 0; + } + + tagged_file_sink_impl::~tagged_file_sink_impl() + { + } + + int + tagged_file_sink_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + char *inbuf = (char*)input_items[0]; + + uint64_t start_N = nitems_read(0); + uint64_t end_N = start_N + (uint64_t)(noutput_items); + pmt::pmt_t bkey = pmt::pmt_string_to_symbol("burst"); + pmt::pmt_t tkey = pmt::pmt_string_to_symbol("rx_time"); // use gr_tags::key_time + + std::vector<gr_tag_t> all_tags; + get_tags_in_range(all_tags, 0, start_N, end_N); + + std::sort(all_tags.begin(), all_tags.end(), gr_tag_t::offset_compare); + + std::vector<gr_tag_t>::iterator vitr = all_tags.begin(); + + // Look for a time tag and initialize d_timeval. + std::vector<gr_tag_t> time_tags_outer; + get_tags_in_range(time_tags_outer, 0, start_N, end_N, tkey); + if(time_tags_outer.size() > 0) { + const gr_tag_t tag = time_tags_outer[0]; + uint64_t offset = tag.offset; + pmt::pmt_t time = tag.value; + uint64_t tsecs = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(time, 0)); + double tfrac = pmt::pmt_to_double(pmt::pmt_tuple_ref(time, 1)); + double delta = (double)offset / d_sample_rate; + d_timeval = (double)tsecs + tfrac + delta; + d_last_N = offset; + } + + int idx = 0, idx_stop = 0; + while(idx < noutput_items) { + if(d_state == NOT_IN_BURST) { + while(vitr != all_tags.end()) { + if((pmt::pmt_eqv((*vitr).key, bkey)) && + pmt::pmt_is_true((*vitr).value)) { + + uint64_t N = (*vitr).offset; + idx = (int)(N - start_N); + + //std::cout << std::endl << "Found start of burst: " + // << idx << ", " << N << std::endl; + + // Find time burst occurred by getting latest time tag and extrapolating + // to new time based on sample rate of this block. + std::vector<gr_tag_t> time_tags; + //get_tags_in_range(time_tags, 0, d_last_N, N, gr_tags::key_time); + get_tags_in_range(time_tags, 0, d_last_N, N, tkey); + if(time_tags.size() > 0) { + const gr_tag_t tag = time_tags[time_tags.size()-1]; + + uint64_t time_nitems = tag.offset; + + // Get time based on last time tag from USRP + pmt::pmt_t time = tag.value; + uint64_t tsecs = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(time, 0)); + double tfrac = pmt::pmt_to_double(pmt::pmt_tuple_ref(time, 1)); + + // Get new time from last time tag + difference in time to when + // burst tag occured based on the sample rate + double delta = (double)(N - time_nitems) / d_sample_rate; + d_timeval = (double)tsecs + tfrac + delta; + + //std::cout.setf(std::ios::fixed, std::ios::floatfield); + //std::cout.precision(8); + //std::cout << "Time found: " << (double)tsecs + tfrac << std::endl; + //std::cout << " time: " << d_timeval << std::endl; + //std::cout << " time at N = " << time_nitems << " burst N = " << N << std::endl; + } + else { + // if no time tag, use last seen tag and update time based on + // sample rate of the block + d_timeval += (double)(N - d_last_N) / d_sample_rate; + //std::cout << "Time not found" << std::endl; + //std::cout << " time: " << d_timeval << std::endl; + } + d_last_N = N; + + std::stringstream filename; + filename.setf(std::ios::fixed, std::ios::floatfield); + filename.precision(8); + filename << "file" << unique_id() << "_" << d_n << "_" << d_timeval << ".dat"; + d_n++; + + int fd; + if((fd = ::open(filename.str().c_str(), + O_WRONLY|O_CREAT|O_TRUNC|OUR_O_LARGEFILE|OUR_O_BINARY, + 0664)) < 0){ + perror(filename.str().c_str()); + return -1; + } + + // FIXME: + //if((d_handle = fdopen (fd, d_is_binary ? "wb" : "w")) == NULL) { + if((d_handle = fdopen (fd, "wb")) == NULL) { + perror(filename.str().c_str()); + ::close(fd); // don't leak file descriptor if fdopen fails. + } + + //std::cout << "Created new file: " << filename.str() << std::endl; + + d_state = IN_BURST; + break; + } + + vitr++; + } + if(d_state == NOT_IN_BURST) + return noutput_items; + } + else { // In burst + while(vitr != all_tags.end()) { + if((pmt::pmt_eqv((*vitr).key, bkey)) && + pmt::pmt_is_false((*vitr).value)) { + uint64_t N = (*vitr).offset; + idx_stop = (int)N - start_N; + + //std::cout << "Found end of burst: " + // << idx_stop << ", " << N << std::endl; + + int count = fwrite (&inbuf[d_itemsize*idx], d_itemsize, + idx_stop-idx, d_handle); + if(count == 0) { + if(ferror(d_handle)) { + perror("tagged_file_sink: error writing file"); + } + } + idx = idx_stop; + d_state = NOT_IN_BURST; + vitr++; + fclose(d_handle); + break; + } + else { + vitr++; + } + } + if(d_state == IN_BURST) { + int count = fwrite (&inbuf[d_itemsize*idx], d_itemsize, + noutput_items-idx, d_handle); + if (count == 0) { + if(ferror(d_handle)) { + perror("tagged_file_sink: error writing file"); + } + } + idx = noutput_items; + } + } + } + + return noutput_items; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/tagged_file_sink_impl.h b/gr-blocks/lib/tagged_file_sink_impl.h new file mode 100644 index 0000000000..f64cedf2f9 --- /dev/null +++ b/gr-blocks/lib/tagged_file_sink_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010,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. + */ + +#ifndef INCLUDED_GR_TAGGED_FILE_SINK_IMPL_H +#define INCLUDED_GR_TAGGED_FILE_SINK_IMPL_H + +#include <blocks/tagged_file_sink.h> +#include <cstdio> // for FILE + +namespace gr { + namespace blocks { + + class tagged_file_sink_impl : public tagged_file_sink + { + private: + enum { + NOT_IN_BURST = 0, + IN_BURST + }; + + size_t d_itemsize; + int d_state; + FILE *d_handle; + int d_n; + double d_sample_rate; + uint64_t d_last_N; + double d_timeval; + + public: + tagged_file_sink_impl(size_t itemsize, double samp_rate); + ~tagged_file_sink_impl(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + + +#endif /* INCLUDED_GR_TAGGED_FILE_SINK_IMPL_H */ diff --git a/gr-blocks/lib/wavfile.cc b/gr-blocks/lib/wavfile.cc new file mode 100644 index 0000000000..16d80adc83 --- /dev/null +++ b/gr-blocks/lib/wavfile.cc @@ -0,0 +1,258 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2008,2012-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 <blocks/wavfile.h> +#include <cstring> +#include <stdint.h> +#include <boost/detail/endian.hpp> //BOOST_BIG_ENDIAN + +namespace gr { + namespace blocks { + +#define VALID_COMPRESSION_TYPE 0x0001 + + // Basically, this is the opposite of htonx() and ntohx() + // Define host to/from worknet (little endian) short and long +#ifdef BOOST_BIG_ENDIAN + + static inline uint16_t __wav_bs16(uint16_t x) + { + return (x>>8) | (x<<8); + } + + static inline uint32_t __wav_bs32(uint32_t x) + { + return (uint32_t(__wav_bs16(uint16_t(x&0xfffful)))<<16) | (__wav_bs16(uint16_t(x>>16))); + } + + #define htowl(x) __gri_wav_bs32(x) + #define wtohl(x) __gri_wav_bs32(x) + #define htows(x) __gri_wav_bs16(x) + #define wtohs(x) __gri_wav_bs16(x) + +#else + + #define htowl(x) uint32_t(x) + #define wtohl(x) uint32_t(x) + #define htows(x) uint16_t(x) + #define wtohs(x) uint16_t(x) + +#endif // BOOST_BIG_ENDIAN + + // WAV files are always little-endian, so we need some byte switching macros + static inline uint32_t host_to_wav(uint32_t x) { return htowl(x); } + static inline uint16_t host_to_wav(uint16_t x) { return htows(x); } + static inline int16_t host_to_wav(int16_t x) { return htows(x); } + static inline uint32_t wav_to_host(uint32_t x) { return wtohl(x); } + static inline uint16_t wav_to_host(uint16_t x) { return wtohs(x); } + static inline int16_t wav_to_host(int16_t x) { return wtohs(x); } + + bool + wavheader_parse(FILE *fp, + unsigned int &sample_rate_o, + int &nchans_o, + int &bytes_per_sample_o, + int &first_sample_pos_o, + unsigned int &samples_per_chan_o) + { + // _o variables take return values + char str_buf[8] = {0}; + + uint32_t file_size; + uint32_t fmt_hdr_skip; + uint16_t compression_type; + uint16_t nchans; + uint32_t sample_rate; + uint32_t avg_bytes_per_sec; + uint16_t block_align; + uint16_t bits_per_sample; + uint32_t chunk_size; + + size_t fresult; + + fresult = fread(str_buf, 1, 4, fp); + if(fresult != 4 || strncmp(str_buf, "RIFF", 4) || feof(fp)) { + return false; + } + + fresult = fread(&file_size, 1, 4, fp); + + fresult = fread(str_buf, 1, 8, fp); + if(fresult != 8 || strncmp(str_buf, "WAVEfmt ", 8) || feof(fp)) { + return false; + } + + fresult = fread(&fmt_hdr_skip, 1, 4, fp); + + fresult = fread(&compression_type, 1, 2, fp); + if(wav_to_host(compression_type) != VALID_COMPRESSION_TYPE) { + return false; + } + + fresult = fread(&nchans, 1, 2, fp); + fresult = fread(&sample_rate, 1, 4, fp); + fresult = fread(&avg_bytes_per_sec, 1, 4, fp); + fresult = fread(&block_align, 1, 2, fp); + fresult = fread(&bits_per_sample, 1, 2, fp); + + if(ferror(fp)) { + return false; + } + + fmt_hdr_skip = wav_to_host(fmt_hdr_skip); + nchans = wav_to_host(nchans); + sample_rate = wav_to_host(sample_rate); + bits_per_sample = wav_to_host(bits_per_sample); + + if(bits_per_sample != 8 && bits_per_sample != 16) { + return false; + } + + fmt_hdr_skip -= 16; + if(fmt_hdr_skip) { + fseek(fp, fmt_hdr_skip, SEEK_CUR); + } + + // data chunk + fresult = fread(str_buf, 1, 4, fp); + if(strncmp(str_buf, "data", 4)) { + return false; + } + + fresult = fread(&chunk_size, 1, 4, fp); + if(ferror(fp)) { + return false; + } + + // More byte swapping + chunk_size = wav_to_host(chunk_size); + + // Output values + sample_rate_o = (unsigned)sample_rate; + nchans_o = (int)nchans; + bytes_per_sample_o = (int)(bits_per_sample / 8); + first_sample_pos_o = (int)ftell(fp); + samples_per_chan_o = (unsigned)(chunk_size / (bytes_per_sample_o * nchans)); + return true; + } + + + short int + wav_read_sample(FILE *fp, int bytes_per_sample) + { + int16_t buf_16bit; + + if(!fread(&buf_16bit, bytes_per_sample, 1, fp)) { + return 0; + } + if(bytes_per_sample == 1) { + return (short)buf_16bit; + } + return (short)wav_to_host(buf_16bit); + } + + + bool + wavheader_write(FILE *fp, + unsigned int sample_rate, + int nchans, + int bytes_per_sample) + { + const int header_len = 44; + char wav_hdr[header_len] = "RIFF\0\0\0\0WAVEfmt \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0data\0\0\0"; + uint16_t nchans_f = (uint16_t) nchans; + uint32_t sample_rate_f = (uint32_t) sample_rate; + uint16_t block_align = bytes_per_sample * nchans; + uint32_t avg_bytes = sample_rate * block_align; + uint16_t bits_per_sample = bytes_per_sample * 8; + + nchans_f = host_to_wav(nchans_f); + sample_rate_f = host_to_wav(sample_rate_f); + block_align = host_to_wav(block_align); + avg_bytes = host_to_wav(avg_bytes); + bits_per_sample = host_to_wav(bits_per_sample); + + wav_hdr[16] = 0x10; // no extra bytes + wav_hdr[20] = 0x01; // no compression + memcpy((void*)(wav_hdr + 22), (void*)&nchans_f, 2); + memcpy((void*)(wav_hdr + 24), (void*)&sample_rate_f, 4); + memcpy((void*)(wav_hdr + 28), (void*)&avg_bytes, 4); + memcpy((void*)(wav_hdr + 32), (void*)&block_align, 2); + memcpy((void*)(wav_hdr + 34), (void*)&bits_per_sample, 2); + + fwrite(&wav_hdr, 1, header_len, fp); + if(ferror(fp)) { + return false; + } + + return true; + } + + + void + wav_write_sample(FILE *fp, short int sample, int bytes_per_sample) + { + void *data_ptr; + unsigned char buf_8bit; + int16_t buf_16bit; + + if(bytes_per_sample == 1) { + buf_8bit = (unsigned char)sample; + data_ptr = (void*)&buf_8bit; + } + else { + buf_16bit = host_to_wav((int16_t) sample); + data_ptr = (void *) &buf_16bit; + } + + fwrite(data_ptr, 1, bytes_per_sample, fp); + } + + + bool + wavheader_complete(FILE *fp, unsigned int byte_count) + { + uint32_t chunk_size = (uint32_t)byte_count; + chunk_size = host_to_wav(chunk_size); + + fseek(fp, 40, SEEK_SET); + fwrite(&chunk_size, 1, 4, fp); + + chunk_size = (uint32_t)byte_count + 36; // fmt chunk and data header + chunk_size = host_to_wav(chunk_size); + fseek(fp, 4, SEEK_SET); + + fwrite(&chunk_size, 1, 4, fp); + + if(ferror(fp)) { + return false; + } + + return true; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/wavfile_sink_impl.cc b/gr-blocks/lib/wavfile_sink_impl.cc new file mode 100644 index 0000000000..f8b09a114b --- /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 <gruel/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) + { + gruel::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() + { + gruel::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; + + gruel::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) + { + gruel::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) + { + gruel::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 */ diff --git a/gr-blocks/lib/wavfile_sink_impl.h b/gr-blocks/lib/wavfile_sink_impl.h new file mode 100644 index 0000000000..8a364f2b90 --- /dev/null +++ b/gr-blocks/lib/wavfile_sink_impl.h @@ -0,0 +1,95 @@ +/* -*- c++ -*- */ +/* + * Copyright 2008,2009,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. + */ + +#ifndef INCLUDED_GR_WAVFILE_SINK_IMPL_H +#define INCLUDED_GR_WAVFILE_SINK_IMPL_H + +#include <blocks/wavfile_sink.h> +#include <gr_file_sink_base.h> + +namespace gr { + namespace blocks { + + class wavfile_sink_impl : public wavfile_sink + { + private: + unsigned d_sample_rate; + int d_nchans; + unsigned d_sample_count; + int d_bytes_per_sample; + int d_bytes_per_sample_new; + int d_max_sample_val; + int d_min_sample_val; + int d_normalize_shift; + int d_normalize_fac; + + FILE *d_fp; + FILE *d_new_fp; + bool d_updated; + boost::mutex d_mutex; + + /*! + * \brief Convert a sample value within [-1;+1] to a corresponding + * short integer value + */ + short convert_to_short(float sample); + + /*! + * \brief If any file changes have occurred, update now. This is called + * internally by work() and thus doesn't usually need to be called by + * hand. + */ + void do_update(); + + /*! + * \brief Writes information to the WAV header which is not available + * a-priori (chunk size etc.) and closes the file. Not thread-safe and + * assumes d_fp is a valid file pointer, should thus only be called by + * other methods. + */ + void close_wav(); + + public: + wavfile_sink_impl(const char *filename, + int n_channels, + unsigned int sample_rate, + int bits_per_sample); + ~wavfile_sink_impl(); + + bool open(const char* filename); + void close(); + + void set_sample_rate(unsigned int sample_rate); + void set_bits_per_sample(int bits_per_sample); + + int bits_per_sample(); + unsigned int sample_rate(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_WAVFILE_SINK_IMPL_H */ diff --git a/gr-blocks/lib/wavfile_source_impl.cc b/gr-blocks/lib/wavfile_source_impl.cc new file mode 100644 index 0000000000..2e3b0e240c --- /dev/null +++ b/gr-blocks/lib/wavfile_source_impl.cc @@ -0,0 +1,174 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2008,2010,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_source_impl.h" +#include <gr_io_signature.h> +#include <blocks/wavfile.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdexcept> + +// 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_source::sptr + wavfile_source::make(const char *filename, bool repeat) + { + return gnuradio::get_initial_sptr + (new wavfile_source_impl(filename, repeat)); + } + + wavfile_source_impl::wavfile_source_impl (const char *filename, bool repeat) + : gr_sync_block("wavfile_source", + gr_make_io_signature(0, 0, 0), + gr_make_io_signature(1, 2, sizeof(float))), + d_fp(NULL), d_repeat(repeat), + d_sample_rate(1), d_nchans(1), d_bytes_per_sample(2), d_first_sample_pos(0), + d_samples_per_chan(0), d_sample_idx(0) + { + // we use "open" to use to the O_LARGEFILE flag + + int fd; + if((fd = open (filename, O_RDONLY | OUR_O_LARGEFILE | OUR_O_BINARY)) < 0) { + perror(filename); + throw std::runtime_error("can't open file"); + } + + if((d_fp = fdopen(fd, "rb")) == NULL) { + perror(filename); + throw std::runtime_error("can't open file"); + } + + // Scan headers, check file validity + if(!wavheader_parse(d_fp, + d_sample_rate, + d_nchans, + d_bytes_per_sample, + d_first_sample_pos, + d_samples_per_chan)) { + throw std::runtime_error("is not a valid wav file"); + } + + if(d_samples_per_chan == 0) { + throw std::runtime_error("WAV file does not contain any samples"); + } + + if(d_bytes_per_sample == 1) { + d_normalize_fac = 128; + d_normalize_shift = 1; + } + else { + d_normalize_fac = 0x7FFF; + d_normalize_shift = 0; + } + + // Re-set the output signature + set_output_signature(gr_make_io_signature(1, d_nchans, sizeof(float))); + } + + wavfile_source_impl::~wavfile_source_impl () + { + fclose(d_fp); + } + + float + wavfile_source_impl::convert_to_float(short int sample) + { + float sample_out = (float)sample; + sample_out /= d_normalize_fac; + sample_out -= d_normalize_shift; + return sample_out; + } + + int + wavfile_source_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + float **out = (float**)&output_items[0]; + int n_out_chans = output_items.size(); + + int i; + short sample; + + for(i = 0; i < noutput_items; i++) { + if(d_sample_idx >= d_samples_per_chan) { + if(!d_repeat) { + // if nothing was read at all, say we're done. + return i ? i : -1; + } + + if(fseek (d_fp, d_first_sample_pos, SEEK_SET) == -1) { + fprintf(stderr, "[%s] fseek failed\n", __FILE__); + exit(-1); + } + + d_sample_idx = 0; + } + + for(int chan = 0; chan < d_nchans; chan++) { + sample = wav_read_sample(d_fp, d_bytes_per_sample); + + if(chan < n_out_chans) { + out[chan][i] = convert_to_float(sample); + } + } + + d_sample_idx++; + + // OK, EOF is not necessarily an error. But we're not going to + // deal with handling corrupt wav files, so if they give us any + // trouble they won't be processed. Serves them bloody right. + if(feof(d_fp) || ferror(d_fp)) { + if(i == 0) { + fprintf(stderr, "[%s] WAV file has corrupted header or i/o error\n", __FILE__); + return -1; + } + return i; + } + } + + return noutput_items; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/wavfile_source_impl.h b/gr-blocks/lib/wavfile_source_impl.h new file mode 100644 index 0000000000..4875731a08 --- /dev/null +++ b/gr-blocks/lib/wavfile_source_impl.h @@ -0,0 +1,70 @@ +/* -*- c++ -*- */ +/* + * Copyright 2004,2008,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. + */ + +#ifndef INCLUDED_GR_WAVFILE_SOURCE_IMPL_H +#define INCLUDED_GR_WAVFILE_SOURCE_IMPL_H + +#include <blocks/wavfile_source.h> +#include <cstdio> // for FILE + +namespace gr { + namespace blocks { + + class wavfile_source_impl : public wavfile_source + { + private: + FILE *d_fp; + bool d_repeat; + + unsigned d_sample_rate; + int d_nchans; + int d_bytes_per_sample; + int d_first_sample_pos; + unsigned d_samples_per_chan; + unsigned d_sample_idx; + int d_normalize_shift; + int d_normalize_fac; + + /*! + * \brief Convert an integer sample value to a float value within [-1;1] + */ + float convert_to_float(short int sample); + + public: + wavfile_source_impl(const char *filename, bool repeat); + ~wavfile_source_impl(); + + unsigned int sample_rate() const { return d_sample_rate; }; + + int bits_per_sample() const { return d_bytes_per_sample * 8; }; + + int channels() const { return d_nchans; }; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_WAVFILE_SOURCE_IMPL_H */ |