summaryrefslogtreecommitdiff
path: root/gr-blocks/lib
diff options
context:
space:
mode:
authorRon Economos <w6rz@comcast.net>2020-06-23 14:40:40 -0700
committerMarcus Müller <marcus@hostalia.de>2020-09-01 23:26:30 +0200
commit18f64ba685cfc2533e2d7726aa43c524b007d97b (patch)
treeba7f4420cce2728ab9ccbd2c2471b851d41138fb /gr-blocks/lib
parent028b2167d7c1a6e7c4eb428b1c6cdd5225add264 (diff)
gr-blocks: Transition the WAV sink and source blocks to libsndfile.
Compressed input and output with FLAC and Ogg Vorbis now supported.
Diffstat (limited to 'gr-blocks/lib')
-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
6 files changed, 217 insertions, 530 deletions
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);