summaryrefslogtreecommitdiff
path: root/gr-blocks
diff options
context:
space:
mode:
Diffstat (limited to 'gr-blocks')
-rw-r--r--gr-blocks/CMakeLists.txt4
-rw-r--r--gr-blocks/grc/blocks_wavfile_sink.block.yml105
-rw-r--r--gr-blocks/include/gnuradio/blocks/wavfile.h90
-rw-r--r--gr-blocks/include/gnuradio/blocks/wavfile_sink.h8
-rw-r--r--gr-blocks/lib/CMakeLists.txt2
-rw-r--r--gr-blocks/lib/wavfile.cc279
-rw-r--r--gr-blocks/lib/wavfile_sink_impl.cc311
-rw-r--r--gr-blocks/lib/wavfile_sink_impl.h23
-rw-r--r--gr-blocks/lib/wavfile_source_impl.cc117
-rw-r--r--gr-blocks/lib/wavfile_source_impl.h15
-rw-r--r--gr-blocks/python/blocks/bindings/CMakeLists.txt7
-rw-r--r--gr-blocks/python/blocks/bindings/docstrings/wavfile_sink_pydoc_template.h3
-rw-r--r--gr-blocks/python/blocks/bindings/wavfile_python.cc62
-rw-r--r--gr-blocks/python/blocks/bindings/wavfile_sink_python.cc16
-rw-r--r--gr-blocks/python/blocks/qa_wavfile.py19
-rw-r--r--gr-blocks/python/blocks/test_16bit_1chunk.wavbin74 -> 74 bytes
-rw-r--r--gr-blocks/python/blocks/test_16bit_1chunk_normal.wavbin0 -> 52 bytes
17 files changed, 404 insertions, 657 deletions
diff --git a/gr-blocks/CMakeLists.txt b/gr-blocks/CMakeLists.txt
index 204f98a266..f584bf0d6b 100644
--- a/gr-blocks/CMakeLists.txt
+++ b/gr-blocks/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright 2012 Free Software Foundation, Inc.
+# Copyright 2012,2020 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -10,6 +10,8 @@
########################################################################
include(GrBoost)
+find_package(SNDFILE)
+
########################################################################
# Register component
########################################################################
diff --git a/gr-blocks/grc/blocks_wavfile_sink.block.yml b/gr-blocks/grc/blocks_wavfile_sink.block.yml
index 4fd6aef5e5..fb7966d01c 100644
--- a/gr-blocks/grc/blocks_wavfile_sink.block.yml
+++ b/gr-blocks/grc/blocks_wavfile_sink.block.yml
@@ -15,16 +15,62 @@ parameters:
label: Sample Rate
dtype: int
default: samp_rate
-- id: bits_per_sample
+- id: format
+ label: Output Format
+ dtype: enum
+ options: [FORMAT_WAV, FORMAT_FLAC, FORMAT_OGG, FORMAT_RF64]
+ option_labels: [WAV, FLAC, Ogg Vorbis, 64-bit WAV]
+ default: FORMAT_WAV
+ option_attributes:
+ hide_wav: [none, all, all, all]
+ hide_flac: [all, none, all, all]
+ hide_ogg: [all, all, none, all]
+ hide_rf64: [all, all, all, none]
+ hide_append: [none, all, all, none]
+ val: [blocks.FORMAT_WAV, blocks.FORMAT_FLAC, blocks.FORMAT_OGG, blocks.FORMAT_RF64]
+- id: bits_per_sample1
label: Bits per Sample
- dtype: int
- default: '8'
+ dtype: enum
+ options: [FORMAT_PCM_16, FORMAT_PCM_24, FORMAT_PCM_32, FORMAT_PCM_U8, FORMAT_FLOAT, FORMAT_DOUBLE]
+ option_labels: [16-bit, 24-bit, 32-bit, unsigned 8-bit, float, double]
+ default: FORMAT_PCM_16
+ option_attributes:
+ val: [blocks.FORMAT_PCM_16, blocks.FORMAT_PCM_24, blocks.FORMAT_PCM_32, blocks.FORMAT_PCM_U8, blocks.FORMAT_FLOAT, blocks.FORMAT_DOUBLE]
+ hide: ${ format.hide_wav }
+- id: bits_per_sample2
+ label: Bits per Sample
+ dtype: enum
+ options: [FORMAT_PCM_S8, FORMAT_PCM_16, FORMAT_PCM_24]
+ option_labels: [signed 8-bit, 16-bit, 24-bit]
+ default: FORMAT_PCM_16
+ option_attributes:
+ val: [blocks.FORMAT_PCM_S8, blocks.FORMAT_PCM_16, blocks.FORMAT_PCM_24]
+ hide: ${ format.hide_flac }
+- id: bits_per_sample3
+ label: Bits per Sample
+ dtype: enum
+ options: [FORMAT_VORBIS]
+ option_labels: [Vorbis]
+ default: FORMAT_VORBIS
+ option_attributes:
+ val: [blocks.FORMAT_VORBIS]
+ hide: ${ format.hide_ogg }
+- id: bits_per_sample4
+ label: Bits per Sample
+ dtype: enum
+ options: [FORMAT_PCM_16, FORMAT_PCM_24, FORMAT_PCM_32, FORMAT_PCM_U8, FORMAT_FLOAT, FORMAT_DOUBLE]
+ option_labels: [16-bit, 24-bit, 32-bit, unsigned 8-bit, float, double]
+ default: FORMAT_PCM_16
+ option_attributes:
+ val: [blocks.FORMAT_PCM_16, blocks.FORMAT_PCM_24, blocks.FORMAT_PCM_32, blocks.FORMAT_PCM_U8, blocks.FORMAT_FLOAT, blocks.FORMAT_DOUBLE]
+ hide: ${ format.hide_rf64 }
- id: append
label: Append to existing file
dtype: enum
default: 'False'
options: ['True', 'False']
option_labels: ['Yes', 'No']
+ hide: ${ format.hide_append }
inputs:
- domain: stream
@@ -36,7 +82,32 @@ asserts:
templates:
imports: from gnuradio import blocks
- make: blocks.wavfile_sink(${file}, ${nchan}, ${samp_rate}, ${bits_per_sample}, ${append})
+ make: |-
+ blocks.wavfile_sink(
+ ${file},
+ ${nchan},
+ ${samp_rate},
+ ${format.val},
+ % if str(format) == 'FORMAT_WAV':
+ ${bits_per_sample1.val},
+ % elif str(format) == 'FORMAT_FLAC':
+ ${bits_per_sample2.val},
+ % elif str(format) == 'FORMAT_OGG':
+ ${bits_per_sample3.val},
+ % elif str(format) == 'FORMAT_RF64':
+ ${bits_per_sample4.val},
+ % endif
+ % if str(format) == 'FORMAT_WAV':
+ ${append}
+ % elif str(format) == 'FORMAT_FLAC':
+ ${False}
+ % elif str(format) == 'FORMAT_OGG':
+ ${False},
+ % elif str(format) == 'FORMAT_RF64':
+ ${append}
+ % endif
+ )
+
callbacks:
- open(${file})
@@ -44,7 +115,31 @@ cpp_templates:
includes: ['#include <gnuradio/blocks/wavfile_sink.h>']
declarations: 'blocks::wavfile_sink::sptr ${id};'
make: |-
- this->${id} = blocks::wavfile_sink::make(${file}${'.c_str()' if str(file)[0] != "'" and str(file)[0] != "\"" else ''}, ${nchan}, ${samp_rate}, ${bits_per_sample}, ${append});
+ this->${id} = blocks::wavfile_sink::make(
+ ${file},
+ ${nchan},
+ ${samp_rate},
+ ${format.val},
+ % if str(format) == 'FORMAT_WAV':
+ ${bits_per_sample1.val},
+ % elif str(format) == 'FORMAT_FLAC':
+ ${bits_per_sample2.val},
+ % elif str(format) == 'FORMAT_OGG':
+ ${bits_per_sample3.val},
+ % elif str(format) == 'FORMAT_RF64':
+ ${bits_per_sample4.val},
+ % endif
+ % if str(format) == 'FORMAT_WAV':
+ ${append}
+ % elif str(format) == 'FORMAT_FLAC':
+ ${False}
+ % elif str(format) == 'FORMAT_OGG':
+ ${False},
+ % elif str(format) == 'FORMAT_RF64':
+ ${append}
+ % endif
+ );
+
callbacks:
## TODO Handle std::string type when const char* argument is needed
- this->${id}->open(${file})
diff --git a/gr-blocks/include/gnuradio/blocks/wavfile.h b/gr-blocks/include/gnuradio/blocks/wavfile.h
index 9f21834947..578900bf30 100644
--- a/gr-blocks/include/gnuradio/blocks/wavfile.h
+++ b/gr-blocks/include/gnuradio/blocks/wavfile.h
@@ -14,93 +14,55 @@
#ifndef _GR_WAVFILE_H_
#define _GR_WAVFILE_H_
-#include <gnuradio/blocks/api.h>
#include <cstdio>
namespace gr {
namespace blocks {
-
//! WAV file header information.
struct wav_header_info {
- // TODO: refactor to use correct types (int16/32, etc.).
//! sample rate [S/s]
- unsigned sample_rate;
+ int sample_rate;
//! Number of channels
int nchans;
//! Bytes per sample
- /** Can either be 1 or 2 (corresponding to 8 or 16 bit samples, respectively) */
int bytes_per_sample;
- //! 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.
- */
- int first_sample_pos;
-
//! Number of samples per channel
- unsigned samples_per_chan;
-
- //! Size of DATA chunk
- unsigned data_chunk_size;
-};
-
-/*!
- * \brief Read signal information from a given WAV file.
- *
- * \param[in] fp File pointer to an opened, empty file.
- * \param[out] info Parsed information.
- * \return True on a successful read, false if the file could not be read or is
- * not a valid or incompatible WAV file.
- */
-BLOCKS_API bool wavheader_parse(FILE* fp, wav_header_info& info);
-
-/*!
- * \brief Read one sample from an open WAV file at the current position.
- *
- * \details
- * Takes care of endianness.
- */
-BLOCKS_API short int wav_read_sample(FILE* fp, int bytes_per_sample);
-
+ long long samples_per_chan;
-/*!
- * \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.
- */
-BLOCKS_API bool
-wavheader_write(FILE* fp, unsigned int sample_rate, int nchans, int bytes_per_sample);
+ //! sndfile format
+ int format;
-/*!
- * \brief Write one sample to an open WAV file at the current position.
- *
- * \details
- * Takes care of endianness.
- */
-BLOCKS_API void wav_write_sample(FILE* fp, short int sample, int bytes_per_sample);
+ //! sndfile format
+ int subformat;
+};
+enum wavfile_format_t {
+ FORMAT_WAV = 0x010000,
+ FORMAT_FLAC = 0x170000,
+ FORMAT_OGG = 0x200000,
+ FORMAT_RF64 = 0x220000,
+};
-/*!
- * \brief Complete a WAV header
- *
- * \details
- * 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 wherever).
- *
- * \param[in] fp File pointer to an open WAV file with a blank header
- * \param[in] first_sample_pos Position of the first sample in DATA chunk.
- */
-BLOCKS_API bool wavheader_complete(FILE* fp, unsigned first_sample_pos);
+enum wavfile_subformat_t {
+ FORMAT_PCM_S8 = 1,
+ FORMAT_PCM_16,
+ FORMAT_PCM_24,
+ FORMAT_PCM_32,
+ FORMAT_PCM_U8,
+ FORMAT_FLOAT,
+ FORMAT_DOUBLE,
+ FORMAT_VORBIS = 0x0060,
+};
} /* namespace blocks */
} /* namespace gr */
+typedef gr::blocks::wavfile_format_t wavfile_format_t;
+typedef gr::blocks::wavfile_subformat_t wavfile_subformat_t;
+
#endif /* _GR_WAVFILE_H_ */
diff --git a/gr-blocks/include/gnuradio/blocks/wavfile_sink.h b/gr-blocks/include/gnuradio/blocks/wavfile_sink.h
index 144f446cce..f228f06168 100644
--- a/gr-blocks/include/gnuradio/blocks/wavfile_sink.h
+++ b/gr-blocks/include/gnuradio/blocks/wavfile_sink.h
@@ -12,6 +12,7 @@
#define INCLUDED_GR_WAVFILE_SINK_H
#include <gnuradio/blocks/api.h>
+#include <gnuradio/blocks/wavfile.h>
#include <gnuradio/sync_block.h>
namespace gr {
@@ -35,12 +36,15 @@ public:
* \param filename The .wav file to be opened
* \param n_channels Number of channels (2 = stereo or I/Q output)
* \param sample_rate Sample rate [S/s]
- * \param bits_per_sample 16 or 8 bit, default is 16
+ * \param format Output format (WAV, FLAC, Ogg Vorbis, RF64)
+ * \param subformat Bits per sample
+ * \param append Append to existing file
*/
static sptr make(const char* filename,
int n_channels,
unsigned int sample_rate,
- int bits_per_sample = 16,
+ wavfile_format_t format,
+ wavfile_subformat_t subformat,
bool append = false);
/*!
diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt
index 7a268f1522..c867d0ab09 100644
--- a/gr-blocks/lib/CMakeLists.txt
+++ b/gr-blocks/lib/CMakeLists.txt
@@ -53,7 +53,6 @@ add_library(gnuradio-blocks
file_sink_base.cc
pack_k_bits.cc
unpack_k_bits.cc
- wavfile.cc
add_const_bb_impl.cc
add_const_ss_impl.cc
add_const_ii_impl.cc
@@ -176,6 +175,7 @@ add_library(gnuradio-blocks
target_link_libraries(gnuradio-blocks
gnuradio-runtime
Volk::volk
+ sndfile::sndfile
)
target_include_directories(gnuradio-blocks
diff --git a/gr-blocks/lib/wavfile.cc b/gr-blocks/lib/wavfile.cc
deleted file mode 100644
index 2558d5c454..0000000000
--- a/gr-blocks/lib/wavfile.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-/* -*- c++ -*- */
-/*
- * Copyright 2004,2008,2012-2013,2020 Free Software Foundation, Inc.
- *
- * This file is part of GNU Radio
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gnuradio/blocks/wavfile.h>
-#include <gnuradio/logger.h>
-#include <stdint.h>
-#include <cstring>
-
-
-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 GR_IS_BIG_ENDIAN
-
-static inline uint16_t __gri_wav_bs16(uint16_t x) { return (x >> 8) | (x << 8); }
-
-static inline uint32_t __gri_wav_bs32(uint32_t x)
-{
- return (uint32_t(__gri_wav_bs16(uint16_t(x & 0xfffful))) << 16) |
- (__gri_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 // GR_IS_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, wav_header_info& info)
-{
- // _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;
- long real_file_size;
-
- size_t fresult;
-
- fseek(fp, 0L, SEEK_END);
- real_file_size = ftell(fp);
- rewind(fp);
-
- 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);
- file_size = wav_to_host(file_size);
- if (fresult != 4 || file_size != real_file_size - 8L) {
- // FIXME use predefined loggers
- gr::logger_ptr logger, debug_logger;
- gr::configure_default_loggers(logger, debug_logger, "wavfile");
- GR_LOG_ERROR(logger,
- boost::format("invalid file size (expected: %d; actual: %d)") %
- (file_size + 8L) % real_file_size);
- return false;
- }
-
- 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) {
- if (fseek(fp, fmt_hdr_skip, SEEK_CUR) != 0) {
- return false;
- }
- }
-
- // find data chunk
- fresult = fread(str_buf, 1, 4, fp);
- // keep parsing chunk until we hit the data chunk
- while (fresult != 4 || strncmp(str_buf, "data", 4)) {
- // all good?
- if (fresult != 4 || ferror(fp) || feof(fp)) {
- return false;
- }
- // get chunk body size and skip
- fresult = fread(&chunk_size, 1, 4, fp);
- if (fresult != 4 || ferror(fp) || feof(fp)) {
- return false;
- }
- chunk_size = wav_to_host(chunk_size);
- if (fseek(fp, chunk_size, SEEK_CUR) != 0) {
- return false;
- }
- // read next chunk type
- fresult = fread(str_buf, 1, 4, fp);
- }
-
- fresult = fread(&chunk_size, 1, 4, fp);
- if (ferror(fp)) {
- return false;
- }
-
- // More byte swapping
- chunk_size = wav_to_host(chunk_size);
-
- // Output values
- info.sample_rate = (unsigned)sample_rate;
- info.nchans = (int)nchans;
- info.bytes_per_sample = (int)(bits_per_sample / 8);
- info.first_sample_pos = (int)ftell(fp);
- info.samples_per_chan = (unsigned)(chunk_size / (info.bytes_per_sample * nchans));
- info.data_chunk_size = (unsigned)chunk_size;
-
- return true;
-}
-
-
-short int wav_read_sample(FILE* fp, int bytes_per_sample)
-{
- int16_t buf_16bit = 0;
-
- if (fread(&buf_16bit, bytes_per_sample, 1, fp) != 1) {
- 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);
-}
-
-inline bool fwrite_field_32(FILE* fp, long position, uint32_t data)
-{
- data = host_to_wav(data);
- if (fseek(fp, position, SEEK_SET) != 0) {
- return false;
- }
-
- return 4 == fwrite(&data, 1, 4, fp);
-}
-
-bool wavheader_complete(FILE* fp, unsigned first_sample_pos)
-{
- fseek(fp, 0L, SEEK_END);
- long real_file_size = ftell(fp);
-
- if (first_sample_pos >= real_file_size) {
- return false;
- }
-
- uint32_t field_data;
- bool ok;
-
- // Write "data chunk size".
- field_data = uint32_t(real_file_size - first_sample_pos);
- ok = fwrite_field_32(fp, first_sample_pos - 4, field_data);
- if (!ok) {
- return false;
- }
-
- // Write "total file size" - 8
- field_data = uint32_t(real_file_size - 8);
- ok = fwrite_field_32(fp, 4, field_data);
-
- return ok;
-}
-
-} /* namespace blocks */
-} /* namespace gr */
diff --git a/gr-blocks/lib/wavfile_sink_impl.cc b/gr-blocks/lib/wavfile_sink_impl.cc
index bcd33a52cb..38325856bb 100644
--- a/gr-blocks/lib/wavfile_sink_impl.cc
+++ b/gr-blocks/lib/wavfile_sink_impl.cc
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2004,2006-2011,2013 Free Software Foundation, Inc.
+ * Copyright 2004,2006-2011,2013,2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -13,50 +13,30 @@
#endif
#include "wavfile_sink_impl.h"
-#include <gnuradio/blocks/wavfile.h>
#include <gnuradio/io_signature.h>
#include <gnuradio/thread/thread.h>
-#include <fcntl.h>
-#include <boost/math/special_functions/round.hpp>
-#include <climits>
-#include <cmath>
#include <cstring>
#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_sink::sptr wavfile_sink::make(const char* filename,
int n_channels,
unsigned int sample_rate,
- int bits_per_sample,
+ wavfile_format_t format,
+ wavfile_subformat_t subformat,
bool append)
{
return gnuradio::make_block_sptr<wavfile_sink_impl>(
- filename, n_channels, sample_rate, bits_per_sample, append);
+ filename, n_channels, sample_rate, format, subformat, append);
}
wavfile_sink_impl::wavfile_sink_impl(const char* filename,
int n_channels,
unsigned int sample_rate,
- int bits_per_sample,
+ wavfile_format_t format,
+ wavfile_subformat_t subformat,
bool append)
: sync_block("wavfile_sink",
io_signature::make(1, n_channels, sizeof(float)),
@@ -67,135 +47,178 @@ wavfile_sink_impl::wavfile_sink_impl(const char* filename,
d_new_fp(nullptr),
d_updated(false)
{
+ int bits_per_sample;
+
+ if (n_channels > 24) {
+ throw std::runtime_error("Number of channels greater than 24 not supported.");
+ }
+
d_h.sample_rate = sample_rate;
d_h.nchans = n_channels;
-
+ d_h.format = format;
+ d_h.subformat = subformat;
+ switch (subformat) {
+ case FORMAT_PCM_S8:
+ bits_per_sample = 8;
+ break;
+ case FORMAT_PCM_16:
+ bits_per_sample = 16;
+ break;
+ case FORMAT_PCM_24:
+ bits_per_sample = 24;
+ break;
+ case FORMAT_PCM_32:
+ bits_per_sample = 32;
+ break;
+ case FORMAT_PCM_U8:
+ bits_per_sample = 8;
+ break;
+ case FORMAT_FLOAT:
+ bits_per_sample = 32;
+ break;
+ case FORMAT_DOUBLE:
+ bits_per_sample = 64;
+ break;
+ case FORMAT_VORBIS:
+ bits_per_sample = 32;
+ break;
+ }
set_bits_per_sample_unlocked(bits_per_sample);
-
d_h.bytes_per_sample = d_bytes_per_sample_new;
if (!open(filename)) {
- throw std::runtime_error("can't open WAV file");
+ throw std::runtime_error("Can't open WAV file.");
}
}
bool wavfile_sink_impl::open(const char* filename)
{
- gr::thread::scoped_lock guard(d_mutex);
+ SF_INFO sfinfo;
- // we use the open system call to get access to the O_LARGEFILE flag.
- int flags = OUR_O_LARGEFILE | OUR_O_BINARY;
-
- if (!d_append) {
- // We are generating a new file.
- flags |= O_CREAT | O_WRONLY | O_TRUNC;
- } else {
- flags |= O_RDWR;
- }
-
- int fd;
- if ((fd = ::open(filename, flags, 0664)) < 0) {
- if (errno == ENOENT) {
- throw std::runtime_error("WAV append mode requires target file to exist");
- } else {
- GR_LOG_ERROR(d_logger,
- boost::format("::open: %s: %s") % filename % strerror(errno));
- }
- return false;
- }
+ gr::thread::scoped_lock guard(d_mutex);
if (d_new_fp) { // if we've already got a new one open, close it
- fclose(d_new_fp);
+ sf_close(d_new_fp);
d_new_fp = nullptr;
}
- if (!(d_new_fp = fdopen(fd, d_append ? "r+b" : "wb"))) {
- GR_LOG_ERROR(d_logger,
- boost::format("fdopen: %s: %s") % filename % strerror(errno));
-
- ::close(fd); // don't leak file descriptor if fdopen fails.
- return false;
- }
-
if (d_append) {
// We are appending to an existing file, be extra careful here.
- if (!check_append_compat_file(d_new_fp)) {
- fclose(d_new_fp);
+ sfinfo.format = 0;
+ if (!(d_new_fp = sf_open(filename, SFM_RDWR, &sfinfo))) {
+ GR_LOG_ERROR(d_logger,
+ boost::format("sf_open failed: %s: %s") % filename %
+ strerror(errno));
+ return false;
+ }
+ if (d_h.sample_rate != sfinfo.samplerate || d_h.nchans != sfinfo.channels ||
+ d_h.format != (sfinfo.format & SF_FORMAT_TYPEMASK) ||
+ d_h.subformat != (sfinfo.format & SF_FORMAT_SUBMASK)) {
+ GR_LOG_ERROR(d_logger,
+ "Existing WAV file is incompatible with configured options.");
+ sf_close(d_new_fp);
return false;
}
+ if (sf_seek(d_new_fp, 0, SEEK_END) == -1) {
+ GR_LOG_ERROR(d_logger, "Seek error.");
+ return false; // This can only happen if the file disappears under our feet.
+ }
} else {
- d_h.first_sample_pos = 44;
- if (!wavheader_write(
- d_new_fp, d_h.sample_rate, d_h.nchans, d_bytes_per_sample_new)) {
- GR_LOG_ERROR(d_logger, "could not save WAV header");
- fclose(d_new_fp);
+ memset(&sfinfo, 0, sizeof(sfinfo));
+ sfinfo.samplerate = d_h.sample_rate;
+ sfinfo.channels = d_h.nchans;
+ switch (d_h.format) {
+ case FORMAT_WAV:
+ switch (d_h.subformat) {
+ case FORMAT_PCM_U8:
+ sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_U8);
+ break;
+ case FORMAT_PCM_16:
+ sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_16);
+ break;
+ case FORMAT_PCM_24:
+ sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_24);
+ break;
+ case FORMAT_PCM_32:
+ sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_PCM_32);
+ break;
+ case FORMAT_FLOAT:
+ sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_FLOAT);
+ break;
+ case FORMAT_DOUBLE:
+ sfinfo.format = (SF_FORMAT_WAV | SF_FORMAT_DOUBLE);
+ break;
+ }
+ break;
+ case FORMAT_FLAC:
+ switch (d_h.subformat) {
+ case FORMAT_PCM_S8:
+ sfinfo.format = (SF_FORMAT_FLAC | SF_FORMAT_PCM_S8);
+ break;
+ case FORMAT_PCM_16:
+ sfinfo.format = (SF_FORMAT_FLAC | SF_FORMAT_PCM_16);
+ break;
+ case FORMAT_PCM_24:
+ sfinfo.format = (SF_FORMAT_FLAC | SF_FORMAT_PCM_24);
+ break;
+ }
+ break;
+ case FORMAT_OGG:
+ switch (d_h.subformat) {
+ case FORMAT_VORBIS:
+ sfinfo.format = (SF_FORMAT_OGG | SF_FORMAT_VORBIS);
+ break;
+ }
+ break;
+ case FORMAT_RF64:
+ switch (d_h.subformat) {
+ case FORMAT_PCM_U8:
+ sfinfo.format = (SF_FORMAT_RF64 | SF_FORMAT_PCM_U8);
+ break;
+ case FORMAT_PCM_16:
+ sfinfo.format = (SF_FORMAT_RF64 | SF_FORMAT_PCM_16);
+ break;
+ case FORMAT_PCM_24:
+ sfinfo.format = (SF_FORMAT_RF64 | SF_FORMAT_PCM_24);
+ break;
+ case FORMAT_PCM_32:
+ sfinfo.format = (SF_FORMAT_RF64 | SF_FORMAT_PCM_32);
+ break;
+ case FORMAT_FLOAT:
+ sfinfo.format = (SF_FORMAT_RF64 | SF_FORMAT_FLOAT);
+ break;
+ case FORMAT_DOUBLE:
+ sfinfo.format = (SF_FORMAT_RF64 | SF_FORMAT_DOUBLE);
+ break;
+ }
+ break;
+ }
+ if (!(d_new_fp = sf_open(filename, SFM_WRITE, &sfinfo))) {
+ GR_LOG_ERROR(d_logger,
+ boost::format("sf_open failed: %s: %s") % filename %
+ strerror(errno));
return false;
}
}
-
d_updated = true;
return true;
}
-bool wavfile_sink_impl::check_append_compat_file(FILE* fp)
-{
-
- if (d_bytes_per_sample_new != d_h.bytes_per_sample) {
- GR_LOG_ERROR(d_logger,
- "bytes_per_sample is not allowed to change in append mode");
- return false;
- }
-
- wav_header_info h_tmp{};
- std::swap(d_h, h_tmp);
-
- if (!wavheader_parse(fp, d_h)) {
- GR_LOG_ERROR(d_logger, "invalid or incompatible WAV file");
- return false;
- }
-
- if (d_h.sample_rate != h_tmp.sample_rate || d_h.nchans != h_tmp.nchans ||
- d_h.bytes_per_sample != h_tmp.bytes_per_sample) {
- GR_LOG_ERROR(d_logger,
- "existing WAV file is incompatible with configured options");
- return false;
- }
-
- // TODO: use GR_FSEEK, GR_FTELL.
- if (fseek(d_new_fp, 0, SEEK_END) != 0) {
- return false; // This can only happen if the file disappears under our feet.
- }
-
- long file_size = ftell(fp);
- if (file_size - d_h.first_sample_pos != d_h.data_chunk_size) {
- // This is complicated to properly implement for too little benefit.
- GR_LOG_ERROR(d_logger,
- "existing WAV file is incompatible (extra chunks at the end)");
- return false;
- }
-
- return true;
-}
-
-
void wavfile_sink_impl::close()
{
gr::thread::scoped_lock guard(d_mutex);
- if (!d_fp)
+ if (!d_fp) {
return;
-
+ }
close_wav();
}
void wavfile_sink_impl::close_wav()
{
- if (!wavheader_complete(d_fp, d_h.first_sample_pos)) {
- GR_LOG_ERROR(d_logger, boost::format("could not save WAV header"));
- }
-
- fclose(d_fp);
+ sf_write_sync(d_fp);
+ sf_close(d_fp);
d_fp = nullptr;
}
@@ -204,10 +227,9 @@ wavfile_sink_impl::~wavfile_sink_impl() { stop(); }
bool wavfile_sink_impl::stop()
{
if (d_new_fp) {
- fclose(d_new_fp);
+ sf_close(d_new_fp);
d_new_fp = nullptr;
}
-
close();
return true;
@@ -219,53 +241,42 @@ int wavfile_sink_impl::work(int noutput_items,
{
auto in = (float**)&input_items[0];
int n_in_chans = input_items.size();
-
- short int sample_buf_s;
-
int nwritten;
+ int errnum;
+ float sample[24];
gr::thread::scoped_lock guard(d_mutex); // hold mutex for duration of this block
do_update(); // update: d_fp is read
- if (!d_fp) // drop output on the floor
+ if (!d_fp) { // drop output on the floor
return noutput_items;
+ }
int nchans = d_h.nchans;
- int bytes_per_sample = d_h.bytes_per_sample;
for (nwritten = 0; nwritten < noutput_items; nwritten++) {
for (int chan = 0; chan < 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]);
+ sample[chan] = in[chan][nwritten];
} else {
- sample_buf_s = 0;
+ sample[chan] = 0;
}
+ }
- wav_write_sample(d_fp, sample_buf_s, bytes_per_sample);
+ sf_write_float(d_fp, &sample[0], nchans);
- if (feof(d_fp) || ferror(d_fp)) {
- GR_LOG_ERROR(d_logger,
- boost::format("file i/o error %s") % strerror(errno));
- close();
- exit(-1);
- }
+ errnum = sf_error(d_fp);
+ if (errnum) {
+ GR_LOG_ERROR(d_logger,
+ boost::format("sf_error: %s") % sf_error_number(errnum));
+ close();
+ throw std::runtime_error("File I/O error.");
}
}
return nwritten;
}
-short int wavfile_sink_impl::convert_to_short(float sample)
-{
- sample += d_normalize_shift;
- sample *= d_normalize_fac;
-
- // In C++17, use std::clamp.
- sample = std::max(std::min(sample, d_max_sample_val), 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);
@@ -274,22 +285,6 @@ void wavfile_sink_impl::set_bits_per_sample(int bits_per_sample)
void wavfile_sink_impl::set_bits_per_sample_unlocked(int bits_per_sample)
{
- switch (bits_per_sample) {
- case 8:
- d_max_sample_val = float(UINT8_MAX);
- d_min_sample_val = float(0);
- d_normalize_fac = d_max_sample_val / 2;
- d_normalize_shift = 1;
- break;
- case 16:
- d_max_sample_val = float(INT16_MAX);
- d_min_sample_val = float(INT16_MIN);
- d_normalize_fac = d_max_sample_val;
- d_normalize_shift = 0;
- break;
- default:
- throw std::runtime_error("Invalid bits per sample (only 8 and 16 are supported)");
- }
d_bytes_per_sample_new = bits_per_sample / 8;
}
diff --git a/gr-blocks/lib/wavfile_sink_impl.h b/gr-blocks/lib/wavfile_sink_impl.h
index d6e687dd03..c837592ad9 100644
--- a/gr-blocks/lib/wavfile_sink_impl.h
+++ b/gr-blocks/lib/wavfile_sink_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2008,2009,2013 Free Software Foundation, Inc.
+ * Copyright 2008,2009,2013,2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -13,6 +13,7 @@
#include <gnuradio/blocks/wavfile.h>
#include <gnuradio/blocks/wavfile_sink.h>
+#include <sndfile.h> // for SNDFILE
namespace gr {
namespace blocks {
@@ -29,18 +30,12 @@ private:
float d_normalize_shift;
float d_normalize_fac;
- FILE* d_fp;
- FILE* d_new_fp;
+ SNDFILE* d_fp;
+ SNDFILE* 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.
@@ -60,13 +55,6 @@ private:
*/
void close_wav();
- /*!
- * \brief Checks if the given WAV file is compatible with the current
- * configuration in order to open it to append information.
- * This also finds the value of d_first_sample_pos.
- */
- bool check_append_compat_file(FILE* fp);
-
protected:
bool stop();
@@ -74,7 +62,8 @@ public:
wavfile_sink_impl(const char* filename,
int n_channels,
unsigned int sample_rate,
- int bits_per_sample,
+ wavfile_format_t format,
+ wavfile_subformat_t subformat,
bool append);
~wavfile_sink_impl();
diff --git a/gr-blocks/lib/wavfile_source_impl.cc b/gr-blocks/lib/wavfile_source_impl.cc
index b6bb559a5d..06db72c7e5 100644
--- a/gr-blocks/lib/wavfile_source_impl.cc
+++ b/gr-blocks/lib/wavfile_source_impl.cc
@@ -13,28 +13,10 @@
#endif
#include "wavfile_source_impl.h"
-#include <gnuradio/blocks/wavfile.h>
#include <gnuradio/io_signature.h>
-#include <fcntl.h>
#include <sys/types.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 {
@@ -52,51 +34,56 @@ wavfile_source_impl::wavfile_source_impl(const char* filename, bool repeat)
d_h{}, // Init with zeros
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) {
- GR_LOG_ERROR(d_logger,
- boost::format("::open: %s: %s") % filename % strerror(errno));
- throw std::runtime_error("can't open file");
- }
+ SF_INFO sfinfo;
- if ((d_fp = fdopen(fd, "rb")) == NULL) {
+ sfinfo.format = 0;
+ if (!(d_fp = sf_open(filename, SFM_READ, &sfinfo))) {
GR_LOG_ERROR(d_logger,
- boost::format("fdopen: %s: %s") % filename % strerror(errno));
- throw std::runtime_error("can't open file");
+ boost::format("sf_open failed: %s: %s") % filename %
+ strerror(errno));
+ throw std::runtime_error("Can't open WAV file.");
}
- // Scan headers, check file validity
- if (!wavheader_parse(d_fp, d_h)) {
- throw std::runtime_error("is not a valid wav file");
+ d_h.sample_rate = (unsigned)sfinfo.samplerate;
+ d_h.nchans = sfinfo.channels;
+ if (sfinfo.channels > 24) {
+ throw std::runtime_error("Number of channels greater than 24 not supported.");
}
-
- if (d_h.samples_per_chan == 0) {
- throw std::runtime_error("WAV file does not contain any samples");
+ switch (sfinfo.format & SF_FORMAT_SUBMASK) {
+ case SF_FORMAT_PCM_S8:
+ d_h.bytes_per_sample = 1;
+ break;
+ case SF_FORMAT_PCM_16:
+ d_h.bytes_per_sample = 2;
+ break;
+ case SF_FORMAT_PCM_24:
+ d_h.bytes_per_sample = 3;
+ break;
+ case SF_FORMAT_PCM_32:
+ d_h.bytes_per_sample = 4;
+ break;
+ case SF_FORMAT_PCM_U8:
+ d_h.bytes_per_sample = 1;
+ break;
+ case SF_FORMAT_FLOAT:
+ d_h.bytes_per_sample = 4;
+ break;
+ case SF_FORMAT_DOUBLE:
+ d_h.bytes_per_sample = 8;
+ break;
}
+ d_h.samples_per_chan = sfinfo.frames;
- if (d_h.bytes_per_sample == 1) {
- d_normalize_fac = UINT8_MAX / 2;
- d_normalize_shift = 1;
- } else {
- d_normalize_fac = INT16_MAX;
- d_normalize_shift = 0;
+ if (d_h.samples_per_chan == 0) {
+ throw std::runtime_error("WAV file does not contain any samples.");
}
// Re-set the output signature
set_output_signature(io_signature::make(1, d_h.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;
-}
+wavfile_source_impl::~wavfile_source_impl() { sf_close(d_fp); }
int wavfile_source_impl::work(int noutput_items,
gr_vector_const_void_star& input_items,
@@ -104,9 +91,9 @@ int wavfile_source_impl::work(int noutput_items,
{
auto out = (float**)&output_items[0];
int n_out_chans = output_items.size();
-
int i;
- short sample;
+ int errnum;
+ float sample[24];
for (i = 0; i < noutput_items; i++) {
if (d_sample_idx >= d_h.samples_per_chan) {
@@ -115,33 +102,35 @@ int wavfile_source_impl::work(int noutput_items,
return i ? i : -1;
}
- if (fseek(d_fp, d_h.first_sample_pos, SEEK_SET) == -1) {
+ if (sf_seek(d_fp, 0, SEEK_SET) == -1) {
GR_LOG_ERROR(d_logger,
- boost::format("fseek failed %s") % strerror(errno));
- exit(-1);
+ boost::format("sf_seek failed: %s") % strerror(errno));
+ throw std::runtime_error("Seek error.");
}
d_sample_idx = 0;
}
+ sf_read_float(d_fp, &sample[0], d_h.nchans);
for (int chan = 0; chan < d_h.nchans; chan++) {
- sample = wav_read_sample(d_fp, d_h.bytes_per_sample);
-
if (chan < n_out_chans) {
- out[chan][i] = convert_to_float(sample);
+ out[chan][i] = sample[chan];
}
}
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)) {
+ // 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.
+
+ errnum = sf_error(d_fp);
+ if (errnum) {
if (i == 0) {
- GR_LOG_ERROR(d_logger,
- boost::format("WAV file has corrupted header or i/o error") %
- strerror(errno));
+ GR_LOG_ERROR(
+ d_logger,
+ boost::format("WAV file has corrupted header or I/O error, %s") %
+ sf_error_number(errnum));
return -1;
}
return i;
diff --git a/gr-blocks/lib/wavfile_source_impl.h b/gr-blocks/lib/wavfile_source_impl.h
index 8504413fec..9554feab67 100644
--- a/gr-blocks/lib/wavfile_source_impl.h
+++ b/gr-blocks/lib/wavfile_source_impl.h
@@ -1,6 +1,6 @@
/* -*- c++ -*- */
/*
- * Copyright 2004,2008,2013 Free Software Foundation, Inc.
+ * Copyright 2004,2008,2013,2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
@@ -13,7 +13,7 @@
#include <gnuradio/blocks/wavfile.h>
#include <gnuradio/blocks/wavfile_source.h>
-#include <cstdio> // for FILE
+#include <sndfile.h> // for SNDFILE
namespace gr {
namespace blocks {
@@ -21,18 +21,11 @@ namespace blocks {
class wavfile_source_impl : public wavfile_source
{
private:
- FILE* d_fp;
+ SNDFILE* d_fp;
bool d_repeat;
wav_header_info d_h;
- 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);
+ long long d_sample_idx;
public:
wavfile_source_impl(const char* filename, bool repeat);
diff --git a/gr-blocks/python/blocks/bindings/CMakeLists.txt b/gr-blocks/python/blocks/bindings/CMakeLists.txt
index 976e12d180..4385c82a0f 100644
--- a/gr-blocks/python/blocks/bindings/CMakeLists.txt
+++ b/gr-blocks/python/blocks/bindings/CMakeLists.txt
@@ -1,3 +1,10 @@
+# Copyright 2020 Free Software Foundation, Inc.
+#
+# This file is part of GNU Radio
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+
include(GrPybind)
########################################################################
diff --git a/gr-blocks/python/blocks/bindings/docstrings/wavfile_sink_pydoc_template.h b/gr-blocks/python/blocks/bindings/docstrings/wavfile_sink_pydoc_template.h
index 137dc82cd1..dc7a57fb7a 100644
--- a/gr-blocks/python/blocks/bindings/docstrings/wavfile_sink_pydoc_template.h
+++ b/gr-blocks/python/blocks/bindings/docstrings/wavfile_sink_pydoc_template.h
@@ -37,3 +37,6 @@ static const char* __doc_gr_blocks_wavfile_sink_set_sample_rate = R"doc()doc";
static const char* __doc_gr_blocks_wavfile_sink_set_bits_per_sample = R"doc()doc";
+
+
+static const char* __doc_gr_blocks_wavfile_sink_set_append = R"doc()doc";
diff --git a/gr-blocks/python/blocks/bindings/wavfile_python.cc b/gr-blocks/python/blocks/bindings/wavfile_python.cc
index 950289d156..ff84bab7eb 100644
--- a/gr-blocks/python/blocks/bindings/wavfile_python.cc
+++ b/gr-blocks/python/blocks/bindings/wavfile_python.cc
@@ -14,7 +14,7 @@
/* BINDTOOL_GEN_AUTOMATIC(0) */
/* BINDTOOL_USE_PYGCCXML(0) */
/* BINDTOOL_HEADER_FILE(wavfile.h) */
-/* BINDTOOL_HEADER_FILE_HASH(85a13746fd109dbc7b94ba38aaf61e4c) */
+/* BINDTOOL_HEADER_FILE_HASH(69b513925578f5e61e5c98a083388fae) */
/***********************************************************************************/
#include <pybind11/complex.h>
@@ -29,49 +29,31 @@ namespace py = pybind11;
void bind_wavfile(py::module& m)
{
- using wav_header_info = gr::blocks::wav_header_info;
+ using wav_header_info = ::gr::blocks::wav_header_info;
py::class_<wav_header_info, std::shared_ptr<wav_header_info>>(m, "wav_header_info")
.def_readwrite("sample_rate", &wav_header_info::sample_rate)
.def_readwrite("nchans", &wav_header_info::nchans)
.def_readwrite("bytes_per_sample", &wav_header_info::bytes_per_sample)
- .def_readwrite("first_sample_pos", &wav_header_info::first_sample_pos)
.def_readwrite("samples_per_chan", &wav_header_info::samples_per_chan)
- .def_readwrite("data_chunk_size", &wav_header_info::data_chunk_size);
-
- m.def("wavheader_parse",
- &::gr::blocks::wavheader_parse,
- py::arg("fp"),
- py::arg("info"),
- D(wavheader_parse));
-
- m.def("wav_read_sample",
- &::gr::blocks::wav_read_sample,
- py::arg("fp"),
- py::arg("bytes_per_sample"),
- D(wav_read_sample));
-
-
- m.def("wavheader_write",
- &::gr::blocks::wavheader_write,
- py::arg("fp"),
- py::arg("sample_rate"),
- py::arg("nchans"),
- py::arg("bytes_per_sample"),
- D(wavheader_write));
-
-
- m.def("wav_write_sample",
- &::gr::blocks::wav_write_sample,
- py::arg("fp"),
- py::arg("sample"),
- py::arg("bytes_per_sample"),
- D(wav_write_sample));
-
-
- m.def("wavheader_complete",
- &::gr::blocks::wavheader_complete,
- py::arg("fp"),
- py::arg("byte_count"),
- D(wavheader_complete));
+ .def_readwrite("format", &wav_header_info::format)
+ .def_readwrite("subformat", &wav_header_info::subformat);
+
+ py::enum_<::gr::blocks::wavfile_format_t>(m, "wavfile_format_t")
+ .value("FORMAT_WAV", ::gr::blocks::FORMAT_WAV) // 65536
+ .value("FORMAT_FLAC", ::gr::blocks::FORMAT_FLAC) // 1507328
+ .value("FORMAT_OGG", ::gr::blocks::FORMAT_OGG) // 2097152
+ .value("FORMAT_RF64", ::gr::blocks::FORMAT_RF64) // 2228224
+ .export_values();
+
+ py::enum_<::gr::blocks::wavfile_subformat_t>(m, "wavfile_subformat_t")
+ .value("FORMAT_PCM_S8", ::gr::blocks::FORMAT_PCM_S8) // 1
+ .value("FORMAT_PCM_16", ::gr::blocks::FORMAT_PCM_16) // 2
+ .value("FORMAT_PCM_24", ::gr::blocks::FORMAT_PCM_24) // 3
+ .value("FORMAT_PCM_32", ::gr::blocks::FORMAT_PCM_32) // 4
+ .value("FORMAT_PCM_U8", ::gr::blocks::FORMAT_PCM_U8) // 5
+ .value("FORMAT_FLOAT", ::gr::blocks::FORMAT_FLOAT) // 6
+ .value("FORMAT_DOUBLE", ::gr::blocks::FORMAT_DOUBLE) // 7
+ .value("FORMAT_VORBIS", ::gr::blocks::FORMAT_VORBIS) // 96
+ .export_values();
}
diff --git a/gr-blocks/python/blocks/bindings/wavfile_sink_python.cc b/gr-blocks/python/blocks/bindings/wavfile_sink_python.cc
index af7046d086..5a6e236eff 100644
--- a/gr-blocks/python/blocks/bindings/wavfile_sink_python.cc
+++ b/gr-blocks/python/blocks/bindings/wavfile_sink_python.cc
@@ -14,7 +14,7 @@
/* BINDTOOL_GEN_AUTOMATIC(0) */
/* BINDTOOL_USE_PYGCCXML(0) */
/* BINDTOOL_HEADER_FILE(wavfile_sink.h) */
-/* BINDTOOL_HEADER_FILE_HASH(b8184d24184e3bd7aa4a08f5fca8ac0d) */
+/* BINDTOOL_HEADER_FILE_HASH(aef0fe37adeb3a7bb763e4ed9e807a17) */
/***********************************************************************************/
#include <pybind11/complex.h>
@@ -29,10 +29,8 @@ namespace py = pybind11;
void bind_wavfile_sink(py::module& m)
{
-
using wavfile_sink = ::gr::blocks::wavfile_sink;
-
py::class_<wavfile_sink,
gr::sync_block,
gr::block,
@@ -43,27 +41,27 @@ void bind_wavfile_sink(py::module& m)
py::arg("filename"),
py::arg("n_channels"),
py::arg("sample_rate"),
- py::arg("bits_per_sample") = 16,
+ py::arg("format"),
+ py::arg("subformat"),
py::arg("append") = false,
D(wavfile_sink, make))
-
.def("open", &wavfile_sink::open, py::arg("filename"), D(wavfile_sink, open))
-
.def("close", &wavfile_sink::close, D(wavfile_sink, close))
-
.def("set_sample_rate",
&wavfile_sink::set_sample_rate,
py::arg("sample_rate"),
D(wavfile_sink, set_sample_rate))
-
.def("set_bits_per_sample",
&wavfile_sink::set_bits_per_sample,
py::arg("bits_per_sample"),
D(wavfile_sink, set_bits_per_sample))
- ;
+ .def("set_append",
+ &wavfile_sink::set_append,
+ py::arg("append"),
+ D(wavfile_sink, set_append));
}
diff --git a/gr-blocks/python/blocks/qa_wavfile.py b/gr-blocks/python/blocks/qa_wavfile.py
index f22934d71b..0b08ce3290 100644
--- a/gr-blocks/python/blocks/qa_wavfile.py
+++ b/gr-blocks/python/blocks/qa_wavfile.py
@@ -15,6 +15,7 @@ import os
from os.path import getsize
g_in_file = os.path.join(os.getenv("srcdir"), "test_16bit_1chunk.wav")
+g_in_file_normal = os.path.join(os.getenv("srcdir"), "test_16bit_1chunk_normal.wav")
g_extra_header_offset = 36
g_extra_header_len = 22
@@ -38,7 +39,8 @@ class test_wavefile(gr_unittest.TestCase):
wf_out = blocks.wavfile_sink(outfile,
wf_in.channels(),
wf_in.sample_rate(),
- wf_in.bits_per_sample())
+ blocks.FORMAT_WAV,
+ blocks.FORMAT_PCM_16)
self.tb.connect(wf_in, wf_out)
self.tb.run()
wf_out.close()
@@ -72,7 +74,7 @@ class test_wavefile(gr_unittest.TestCase):
in_data[g_extra_header_offset + g_extra_header_len:], out_data)
def test_003_checkwav_append_copy(self):
- infile = g_in_file
+ infile = g_in_file_normal
outfile = "test_out_append.wav"
# 1. Copy input to output
@@ -84,7 +86,8 @@ class test_wavefile(gr_unittest.TestCase):
wf_out = blocks.wavfile_sink(outfile,
wf_in.channels(),
wf_in.sample_rate(),
- wf_in.bits_per_sample(),
+ blocks.FORMAT_WAV,
+ blocks.FORMAT_PCM_16,
True)
self.tb.connect(wf_in, wf_out)
self.tb.run()
@@ -96,7 +99,8 @@ class test_wavefile(gr_unittest.TestCase):
wf_out = blocks.wavfile_sink(outfile,
wf_in.channels(),
wf_in.sample_rate(),
- wf_in.bits_per_sample(),
+ blocks.FORMAT_WAV,
+ blocks.FORMAT_PCM_16,
True)
self.tb.connect(wf_in, halver, wf_out)
self.tb.run()
@@ -130,12 +134,15 @@ class test_wavefile(gr_unittest.TestCase):
data_in_halved = [int(round(d/2)) for d in data_in]
self.assertEqual(data_in_halved, data_out[2*len(data_in):])
+ os.remove(outfile)
+
def test_003_checkwav_append_non_existent_should_error(self):
outfile = "no_file.wav"
- with self.assertRaisesRegex(RuntimeError, 'WAV append mode requires target file to exist'):
- blocks.wavfile_sink(outfile, 1, 44100, 16, True)
+ with self.assertRaisesRegex(RuntimeError, "Can't open WAV file."):
+ blocks.wavfile_sink(outfile, 1, 44100, blocks.FORMAT_WAV, blocks.FORMAT_PCM_16, True)
+ os.remove(outfile)
def wav_read_frames(w):
import struct
diff --git a/gr-blocks/python/blocks/test_16bit_1chunk.wav b/gr-blocks/python/blocks/test_16bit_1chunk.wav
index 3d028a9582..e984f847a4 100644
--- a/gr-blocks/python/blocks/test_16bit_1chunk.wav
+++ b/gr-blocks/python/blocks/test_16bit_1chunk.wav
Binary files differ
diff --git a/gr-blocks/python/blocks/test_16bit_1chunk_normal.wav b/gr-blocks/python/blocks/test_16bit_1chunk_normal.wav
new file mode 100644
index 0000000000..42989ed588
--- /dev/null
+++ b/gr-blocks/python/blocks/test_16bit_1chunk_normal.wav
Binary files differ