summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Rondeau <trondeau@vt.edu>2013-03-09 09:35:43 -0500
committerTom Rondeau <trondeau@vt.edu>2013-03-09 09:35:43 -0500
commitb359d899af0771f3b3d66180af46df38d1c141f6 (patch)
tree9970d6cb0fe1b101d5e5163a9bb005f746ddd752
parentfe70fa72ea22a133874337da2d03d30611e5ab1c (diff)
parent21c95a82a495f0cf9dfe511f9ef370bae9ac0eeb (diff)
Merge branch 'master' into next
-rw-r--r--gr-blocks/grc/blocks_block_tree.xml3
-rw-r--r--gr-blocks/grc/blocks_tagged_file_sink.xml60
-rw-r--r--gr-blocks/grc/blocks_wavfile_sink.xml43
-rw-r--r--gr-blocks/grc/blocks_wavfile_source.xml44
-rw-r--r--gr-blocks/include/blocks/CMakeLists.txt4
-rw-r--r--gr-blocks/include/blocks/tagged_file_sink.h65
-rw-r--r--gr-blocks/include/blocks/wavfile.h101
-rw-r--r--gr-blocks/include/blocks/wavfile_sink.h87
-rw-r--r--gr-blocks/include/blocks/wavfile_source.h70
-rw-r--r--gr-blocks/lib/CMakeLists.txt4
-rw-r--r--gr-blocks/lib/tagged_file_sink_impl.cc235
-rw-r--r--gr-blocks/lib/tagged_file_sink_impl.h61
-rw-r--r--gr-blocks/lib/wavfile.cc258
-rw-r--r--gr-blocks/lib/wavfile_sink_impl.cc293
-rw-r--r--gr-blocks/lib/wavfile_sink_impl.h95
-rw-r--r--gr-blocks/lib/wavfile_source_impl.cc174
-rw-r--r--gr-blocks/lib/wavfile_source_impl.h70
-rw-r--r--gr-blocks/python/qa_burst_tagger.py1
-rw-r--r--gr-blocks/python/qa_tag_file_sink.py72
-rwxr-xr-xgr-blocks/python/qa_wavfile.py69
-rw-r--r--gr-blocks/python/test_16bit_1chunk.wavbin0 -> 52 bytes
-rw-r--r--gr-blocks/swig/blocks_swig.i9
22 files changed, 1817 insertions, 1 deletions
diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml
index 36e5e95f2a..573558466d 100644
--- a/gr-blocks/grc/blocks_block_tree.xml
+++ b/gr-blocks/grc/blocks_block_tree.xml
@@ -36,6 +36,7 @@
<block>blocks_random_pdu</block>
<block>blocks_message_source</block>
<block>blocks_message_burst_source</block>
+ <block>blocks_wavfile_source</block>
</cat>
<cat>
<name>Sinks (New)</name>
@@ -43,6 +44,8 @@
<block>blocks_tagged_stream_to_pdu</block>
<block>blocks_tag_debug</block>
<block>blocks_message_sink</block>
+ <block>blocks_tagged_file_sink</block>
+ <block>blocks_wavfile_sink</block>
</cat>
<cat>
<name>Math Operations (New) </name>
diff --git a/gr-blocks/grc/blocks_tagged_file_sink.xml b/gr-blocks/grc/blocks_tagged_file_sink.xml
new file mode 100644
index 0000000000..a849c48c9a
--- /dev/null
+++ b/gr-blocks/grc/blocks_tagged_file_sink.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##Tagged File Sink
+###################################################
+ -->
+<block>
+ <name>Tagged File Sink</name>
+ <key>blocks_tagged_file_sink</key>
+ <import>from gnuradio import blocks</import>
+ <make>blocks.tagged_file_sink($type.size*$vlen, $samp_rate)</make>
+ <param>
+ <name>Input Type</name>
+ <key>type</key>
+ <type>enum</type>
+ <option>
+ <name>Complex</name>
+ <key>complex</key>
+ <opt>size:gr.sizeof_gr_complex</opt>
+ </option>
+ <option>
+ <name>Float</name>
+ <key>float</key>
+ <opt>size:gr.sizeof_float</opt>
+ </option>
+ <option>
+ <name>Int</name>
+ <key>int</key>
+ <opt>size:gr.sizeof_int</opt>
+ </option>
+ <option>
+ <name>Short</name>
+ <key>short</key>
+ <opt>size:gr.sizeof_short</opt>
+ </option>
+ <option>
+ <name>Byte</name>
+ <key>byte</key>
+ <opt>size:gr.sizeof_char</opt>
+ </option>
+ </param>
+ <param>
+ <name>Sample Rate</name>
+ <key>samp_rate</key>
+ <value>samp_rate</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Vec Length</name>
+ <key>vlen</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <check>$vlen &gt; 0</check>
+ <sink>
+ <name>in</name>
+ <type>$type</type>
+ <vlen>$vlen</vlen>
+ </sink>
+</block>
diff --git a/gr-blocks/grc/blocks_wavfile_sink.xml b/gr-blocks/grc/blocks_wavfile_sink.xml
new file mode 100644
index 0000000000..8e81d8d202
--- /dev/null
+++ b/gr-blocks/grc/blocks_wavfile_sink.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##Wav File Sink
+###################################################
+ -->
+<block>
+ <name>Wav File Sink</name>
+ <key>blocks_wavfile_sink</key>
+ <import>from gnuradio import blocks</import>
+ <make>blocks.wavfile_sink($file, $nchan, $samp_rate, $bits_per_sample)</make>
+ <callback>open($file)</callback>
+ <param>
+ <name>File</name>
+ <key>file</key>
+ <value></value>
+ <type>file_save</type>
+ </param>
+ <param>
+ <name>N Channels</name>
+ <key>nchan</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Sample Rate</name>
+ <key>samp_rate</key>
+ <value>samp_rate</value>
+ <type>int</type>
+ </param>
+ <param>
+ <name>Bits per Sample</name>
+ <key>bits_per_sample</key>
+ <value>8</value>
+ <type>int</type>
+ </param>
+ <check>1 &lt;= $nchan</check>
+ <sink>
+ <name>in</name>
+ <type>float</type>
+ <nports>$nchan</nports>
+ </sink>
+</block>
diff --git a/gr-blocks/grc/blocks_wavfile_source.xml b/gr-blocks/grc/blocks_wavfile_source.xml
new file mode 100644
index 0000000000..deb48472a4
--- /dev/null
+++ b/gr-blocks/grc/blocks_wavfile_source.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<!--
+###################################################
+##Wav File Source
+###################################################
+ -->
+<block>
+ <name>Wav File Source</name>
+ <key>blocks_wavfile_source</key>
+ <import>from gnuradio import blocks</import>
+ <make>blocks.wavfile_source($file, $repeat)</make>
+ <param>
+ <name>File</name>
+ <key>file</key>
+ <value></value>
+ <type>file_open</type>
+ </param>
+ <param>
+ <name>Repeat</name>
+ <key>repeat</key>
+ <value>True</value>
+ <type>enum</type>
+ <option>
+ <name>Yes</name>
+ <key>True</key>
+ </option>
+ <option>
+ <name>No</name>
+ <key>False</key>
+ </option>
+ </param>
+ <param>
+ <name>N Channels</name>
+ <key>nchan</key>
+ <value>1</value>
+ <type>int</type>
+ </param>
+ <check>1 &lt;= $nchan</check>
+ <source>
+ <name>out</name>
+ <type>float</type>
+ <nports>$nchan</nports>
+ </source>
+</block>
diff --git a/gr-blocks/include/blocks/CMakeLists.txt b/gr-blocks/include/blocks/CMakeLists.txt
index 6609ede243..f101dedaab 100644
--- a/gr-blocks/include/blocks/CMakeLists.txt
+++ b/gr-blocks/include/blocks/CMakeLists.txt
@@ -106,6 +106,7 @@ install(FILES
log2_const.h
nco.h
vco.h
+ wavfile.h
add_ff.h
bin_statistics_f.h
burst_tagger.h
@@ -165,6 +166,7 @@ install(FILES
streams_to_vector.h
stretch_ff.h
tag_debug.h
+ tagged_file_sink.h
tagged_stream_to_pdu.h
threshold_ff.h
throttle.h
@@ -175,6 +177,8 @@ install(FILES
vco_f.h
vector_to_stream.h
vector_to_streams.h
+ wavfile_sink.h
+ wavfile_source.h
DESTINATION ${GR_INCLUDE_DIR}/gnuradio/blocks
COMPONENT "blocks_devel"
)
diff --git a/gr-blocks/include/blocks/tagged_file_sink.h b/gr-blocks/include/blocks/tagged_file_sink.h
new file mode 100644
index 0000000000..62da72f789
--- /dev/null
+++ b/gr-blocks/include/blocks/tagged_file_sink.h
@@ -0,0 +1,65 @@
+/* -*- 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_H
+#define INCLUDED_GR_TAGGED_FILE_SINK_H
+
+#include <blocks/api.h>
+#include <gr_sync_block.h>
+
+namespace gr {
+ namespace blocks {
+
+ /*!
+ * \brief A file sink that uses tags to save files.
+ * \ingroup sink_blk
+ *
+ * The sink uses a tag with the key 'burst' to trigger the saving
+ * of the burst data to a new file. If the value of this tag is
+ * True, it will open a new file and start writing all incoming
+ * data to it. If the tag is False, it will close the file (if
+ * already opened). The file names are based on the time when the
+ * burst tag was seen. If there is an 'rx_time' tag (standard with
+ * UHD sources), that is used as the time. If no 'rx_time' tag is
+ * found, the new time is calculated based off the sample rate of
+ * the block.
+ */
+ class BLOCKS_API tagged_file_sink : virtual public gr_sync_block
+ {
+ public:
+ // gr::blocks::tagged_file_sink::sptr
+ typedef boost::shared_ptr<tagged_file_sink> sptr;
+
+ /*!
+ * \brief Build a tagged_file_sink block.
+ *
+ * \param itemsize The item size of the input data stream.
+ * \param samp_rate The sample rate used to determine the time
+ * difference between bursts
+ */
+ static sptr make(size_t itemsize, double samp_rate);
+ };
+
+ } /* namespace blocks */
+} /* namespace gr */
+
+#endif /* INCLUDED_GR_TAGGED_FILE_SINK_H */
diff --git a/gr-blocks/include/blocks/wavfile.h b/gr-blocks/include/blocks/wavfile.h
new file mode 100644
index 0000000000..690f8fc22a
--- /dev/null
+++ b/gr-blocks/include/blocks/wavfile.h
@@ -0,0 +1,101 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 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.
+ */
+
+// This file stores all the RIFF file type knowledge for the gr_wavfile_*
+// blocks.
+
+#include <blocks/api.h>
+#include <cstdio>
+
+namespace gr {
+ namespace blocks {
+
+ /*!
+ * \brief Read signal information from a given WAV file.
+ *
+ * \param[in] fp File pointer to an opened, empty file.
+ * \param[out] sample_rate Stores the sample rate [S/s]
+ * \param[out] nchans Number of channels
+ * \param[out] bytes_per_sample Bytes per sample, can either be 1 or 2 (corresponding o
+ * 8 or 16 bit samples, respectively)
+ * \param[out] first_sample_pos Number of the first byte containing a sample. Use this
+ * with fseek() to jump from the end of the file to the
+ * first sample when in repeat mode.
+ * \param[out] samples_per_chan Number of samples per channel
+ * \return True on a successful read, false if the file could not be read or is
+ * not a valid WAV file.
+ */
+ bool
+ wavheader_parse(FILE *fp,
+ unsigned int &sample_rate,
+ int &nchans,
+ int &bytes_per_sample,
+ int &first_sample_pos,
+ unsigned int &samples_per_chan);
+
+ /*!
+ * \brief Read one sample from an open WAV file at the current position.
+ *
+ * Takes care of endianness.
+ */
+ short int
+ wav_read_sample(FILE *fp, int bytes_per_sample);
+
+
+ /*!
+ * \brief Write a valid RIFF file header
+ *
+ * Note: Some header values are kept blank because they're usually
+ * not known a-priori (file and chunk lengths). Use
+ * gri_wavheader_complete() to fill these in.
+ */
+ bool
+ wavheader_write(FILE *fp,
+ unsigned int sample_rate,
+ int nchans,
+ int bytes_per_sample);
+
+ /*!
+ * \brief Write one sample to an open WAV file at the current position.
+ *
+ * Takes care of endianness.
+ */
+ void
+ wav_write_sample(FILE *fp, short int sample, int bytes_per_sample);
+
+
+ /*!
+ * \brief Complete a WAV header
+ *
+ * Note: The stream position is changed during this function. If
+ * anything needs to be written to the WAV file after calling this
+ * function (which shouldn't happen), you need to fseek() to the
+ * end of the file (or whereever).
+ *
+ * \param[in] fp File pointer to an open WAV file with a blank header
+ * \param[in] byte_count Length of all samples written to the file in bytes.
+ */
+ bool
+ wavheader_complete(FILE *fp, unsigned int byte_count);
+
+ } /* namespace blocks */
+} /* namespace gr */
diff --git a/gr-blocks/include/blocks/wavfile_sink.h b/gr-blocks/include/blocks/wavfile_sink.h
new file mode 100644
index 0000000000..b095191d07
--- /dev/null
+++ b/gr-blocks/include/blocks/wavfile_sink.h
@@ -0,0 +1,87 @@
+/* -*- 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_H
+#define INCLUDED_GR_WAVFILE_SINK_H
+
+#include <blocks/api.h>
+#include <gr_sync_block.h>
+
+namespace gr {
+ namespace blocks {
+
+ /*!
+ * \brief Write stream to a Microsoft PCM (.wav) file.
+ *
+ * Values must be floats within [-1;1].
+ * Check gr_make_wavfile_sink() for extra info.
+ *
+ * \ingroup sink_blk
+ */
+ class BLOCKS_API wavfile_sink : virtual public gr_sync_block
+ {
+ public:
+ // gr::blocks::wavfile_sink::sptr
+ typedef boost::shared_ptr<wavfile_sink> sptr;
+
+ /*
+ * \p filename The .wav file to be opened
+ * \p n_channels Number of channels (2 = stereo or I/Q output)
+ * \p sample_rate Sample rate [S/s]
+ * \p bits_per_sample 16 or 8 bit, default is 16
+ */
+ static sptr make(const char *filename,
+ int n_channels,
+ unsigned int sample_rate,
+ int bits_per_sample = 16);
+
+ /*!
+ * \brief Opens a new file and writes a WAV header. Thread-safe.
+ */
+ virtual bool open(const char* filename) = 0;
+
+ /*!
+ * \brief Closes the currently active file and completes the WAV
+ * header. Thread-safe.
+ */
+ virtual void close() = 0;
+
+ /*!
+ * \brief Set the sample rate. This will not affect the WAV file
+ * currently opened. Any following open() calls will use this new
+ * sample rate.
+ */
+ virtual void set_sample_rate(unsigned int sample_rate) = 0;
+
+ /*!
+ * \brief Set bits per sample. This will not affect the WAV file
+ * currently opened (see set_sample_rate()). If the value is
+ * neither 8 nor 16, the call is ignored and the current value
+ * is kept.
+ */
+ virtual void set_bits_per_sample(int bits_per_sample) = 0;
+ };
+
+ } /* namespace blocks */
+} /* namespace gr */
+
+#endif /* INCLUDED_GR_WAVFILE_SINK_H */
diff --git a/gr-blocks/include/blocks/wavfile_source.h b/gr-blocks/include/blocks/wavfile_source.h
new file mode 100644
index 0000000000..46cb82b698
--- /dev/null
+++ b/gr-blocks/include/blocks/wavfile_source.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_H
+#define INCLUDED_GR_WAVFILE_SOURCE_H
+
+#include <blocks/api.h>
+#include <gr_sync_block.h>
+
+namespace gr {
+ namespace blocks {
+
+ /*!
+ * \brief Read stream from a Microsoft PCM (.wav) file, output floats
+ *
+ * Unless otherwise called, values are within [-1;1].
+ * Check gr_make_wavfile_source() for extra info.
+ *
+ * \ingroup source_blk
+ */
+ class BLOCKS_API wavfile_source : virtual public gr_sync_block
+ {
+ public:
+ // gr::blocks::wavfile_source::sptr
+ typedef boost::shared_ptr<wavfile_source> sptr;
+
+ static sptr make(const char *filename, bool repeat = false);
+
+ /*!
+ * \brief Read the sample rate as specified in the wav file header
+ */
+ virtual unsigned int sample_rate() const = 0;
+
+ /*!
+ * \brief Return the number of bits per sample as specified in
+ * the wav file header. Only 8 or 16 bit are supported here.
+ */
+ virtual int bits_per_sample() const = 0;
+
+ /*!
+ * \brief Return the number of channels in the wav file as
+ * specified in the wav file header. This is also the max number
+ * of outputs you can have.
+ */
+ virtual int channels() const = 0;
+ };
+
+ } /* namespace blocks */
+} /* namespace gr */
+
+#endif /* INCLUDED_GR_WAVFILE_SOURCE_H */
diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt
index 818b8ae621..f728814dc6 100644
--- a/gr-blocks/lib/CMakeLists.txt
+++ b/gr-blocks/lib/CMakeLists.txt
@@ -143,6 +143,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
@@ -206,6 +207,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
@@ -218,6 +220,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 */
diff --git a/gr-blocks/python/qa_burst_tagger.py b/gr-blocks/python/qa_burst_tagger.py
index dfe9ec6429..52d688d103 100644
--- a/gr-blocks/python/qa_burst_tagger.py
+++ b/gr-blocks/python/qa_burst_tagger.py
@@ -33,7 +33,6 @@ class test_burst_tagger(gr_unittest.TestCase):
self.tb = None
def test_001(self):
- # Just run some data through and make sure it doesn't puke.
src_data = ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
trg_data = (-1, -1, 1, 1, -1, -1, 1, 1, -1, -1)
src = gr.vector_source_i(src_data)
diff --git a/gr-blocks/python/qa_tag_file_sink.py b/gr-blocks/python/qa_tag_file_sink.py
new file mode 100644
index 0000000000..80e41a7dd0
--- /dev/null
+++ b/gr-blocks/python/qa_tag_file_sink.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+#
+# Copyright 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.
+#
+
+from gnuradio import gr, gr_unittest
+import blocks_swig as blocks
+import os, struct
+
+class test_tag_file_sink(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001(self):
+ src_data = ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
+ trg_data = (-1, -1, 1, 1, -1, -1, 1, 1, -1, -1)
+ src = gr.vector_source_i(src_data)
+ trg = gr.vector_source_s(trg_data)
+ op = blocks.burst_tagger(gr.sizeof_int)
+ snk = blocks.tagged_file_sink(gr.sizeof_int, 1)
+ self.tb.connect(src, (op,0))
+ self.tb.connect(trg, (op,1))
+ self.tb.connect(op, snk)
+ self.tb.run()
+
+ # Tagged file sink gets 2 burst tags at index 2 and index 5.
+ # Creates two new files, each with two integers in them from
+ # src_data at these indexes (3,4) and (7,8).
+ file0 = "file{0}_0_2.00000000.dat".format(snk.unique_id())
+ file1 = "file{0}_1_6.00000000.dat".format(snk.unique_id())
+
+ # Open the files and read in the data, then remove the files
+ # to clean up the directory.
+ outfile0 = file(file0, 'rb')
+ outfile1 = file(file1, 'rb')
+ data0 = outfile0.read(8)
+ data1 = outfile1.read(8)
+ outfile0.close()
+ outfile1.close()
+ os.remove(file0)
+ os.remove(file1)
+
+ # Convert the 8 bytes from the files into a tuple of 2 ints.
+ idata0 = struct.unpack('ii', data0)
+ idata1 = struct.unpack('ii', data1)
+
+ self.assertEqual(idata0, (3, 4))
+ self.assertEqual(idata1, (7, 8))
+
+if __name__ == '__main__':
+ gr_unittest.run(test_tag_file_sink, "test_tag_file_sink.xml")
diff --git a/gr-blocks/python/qa_wavfile.py b/gr-blocks/python/qa_wavfile.py
new file mode 100755
index 0000000000..b40b9b7ec3
--- /dev/null
+++ b/gr-blocks/python/qa_wavfile.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+#
+# Copyright 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.
+#
+
+from gnuradio import gr, gr_unittest
+import blocks_swig as blocks
+
+import os
+from os.path import getsize
+
+g_in_file = os.path.join(os.getenv("srcdir"), "test_16bit_1chunk.wav")
+
+class test_wavefile(gr_unittest.TestCase):
+
+ def setUp(self):
+ self.tb = gr.top_block()
+
+ def tearDown(self):
+ self.tb = None
+
+ def test_001_checkwavread(self):
+ wf = blocks.wavfile_source(g_in_file)
+ self.assertEqual(wf.sample_rate(), 8000)
+
+ def test_002_checkwavcopy(self):
+ infile = g_in_file
+ outfile = "test_out.wav"
+
+ wf_in = blocks.wavfile_source(infile)
+ wf_out = blocks.wavfile_sink(outfile,
+ wf_in.channels(),
+ wf_in.sample_rate(),
+ wf_in.bits_per_sample())
+ self.tb.connect(wf_in, wf_out)
+ self.tb.run()
+ wf_out.close()
+
+ self.assertEqual(getsize(infile), getsize(outfile))
+
+ in_f = file(infile, 'rb')
+ out_f = file(outfile, 'rb')
+
+ in_data = in_f.read()
+ out_data = out_f.read()
+ out_f.close()
+ os.remove(outfile)
+
+ self.assertEqual(in_data, out_data)
+
+if __name__ == '__main__':
+ gr_unittest.run(test_wavefile, "test_wavefile.xml")
diff --git a/gr-blocks/python/test_16bit_1chunk.wav b/gr-blocks/python/test_16bit_1chunk.wav
new file mode 100644
index 0000000000..0fe12a7a13
--- /dev/null
+++ b/gr-blocks/python/test_16bit_1chunk.wav
Binary files differ
diff --git a/gr-blocks/swig/blocks_swig.i b/gr-blocks/swig/blocks_swig.i
index d63a5ae0e8..5e71bb0007 100644
--- a/gr-blocks/swig/blocks_swig.i
+++ b/gr-blocks/swig/blocks_swig.i
@@ -166,6 +166,7 @@
#include "blocks/sub_ii.h"
#include "blocks/sub_cc.h"
#include "blocks/tag_debug.h"
+#include "blocks/tagged_file_sink.h"
#include "blocks/tagged_stream_to_pdu.h"
#include "blocks/threshold_ff.h"
#include "blocks/throttle.h"
@@ -179,6 +180,8 @@
#include "blocks/vco_f.h"
#include "blocks/vector_to_stream.h"
#include "blocks/vector_to_streams.h"
+#include "blocks/wavfile_sink.h"
+#include "blocks/wavfile_source.h"
#include "blocks/xor_bb.h"
#include "blocks/xor_ss.h"
#include "blocks/xor_ii.h"
@@ -319,6 +322,7 @@
%include "blocks/sub_ss.h"
%include "blocks/sub_ii.h"
%include "blocks/sub_cc.h"
+%include "blocks/tagged_file_sink.h"
%include "blocks/tagged_stream_to_pdu.h"
%include "blocks/threshold_ff.h"
%include "blocks/throttle.h"
@@ -332,6 +336,8 @@
%include "blocks/vco_f.h"
%include "blocks/vector_to_stream.h"
%include "blocks/vector_to_streams.h"
+%include "blocks/wavfile_sink.h"
+%include "blocks/wavfile_source.h"
%include "blocks/xor_bb.h"
%include "blocks/xor_ss.h"
%include "blocks/xor_ii.h"
@@ -471,6 +477,7 @@ GR_SWIG_BLOCK_MAGIC2(blocks, sub_ss);
GR_SWIG_BLOCK_MAGIC2(blocks, sub_ii);
GR_SWIG_BLOCK_MAGIC2(blocks, sub_cc);
GR_SWIG_BLOCK_MAGIC2(blocks, tag_debug);
+GR_SWIG_BLOCK_MAGIC2(blocks, tagged_file_sink);
GR_SWIG_BLOCK_MAGIC2(blocks, tagged_stream_to_pdu);
GR_SWIG_BLOCK_MAGIC2(blocks, threshold_ff);
GR_SWIG_BLOCK_MAGIC2(blocks, throttle);
@@ -484,6 +491,8 @@ GR_SWIG_BLOCK_MAGIC2(blocks, unpacked_to_packed_ii);
GR_SWIG_BLOCK_MAGIC2(blocks, vco_f);
GR_SWIG_BLOCK_MAGIC2(blocks, vector_to_stream);
GR_SWIG_BLOCK_MAGIC2(blocks, vector_to_streams);
+GR_SWIG_BLOCK_MAGIC2(blocks, wavfile_sink);
+GR_SWIG_BLOCK_MAGIC2(blocks, wavfile_source);
GR_SWIG_BLOCK_MAGIC2(blocks, xor_bb);
GR_SWIG_BLOCK_MAGIC2(blocks, xor_ss);
GR_SWIG_BLOCK_MAGIC2(blocks, xor_ii);