From 18f64ba685cfc2533e2d7726aa43c524b007d97b Mon Sep 17 00:00:00 2001
From: Ron Economos <w6rz@comcast.net>
Date: Tue, 23 Jun 2020 14:40:40 -0700
Subject: gr-blocks: Transition the WAV sink and source blocks to libsndfile.
 Compressed input and output with FLAC and Ogg Vorbis now supported.

---
 gr-blocks/lib/wavfile_source_impl.cc | 117 ++++++++++++++++-------------------
 1 file changed, 53 insertions(+), 64 deletions(-)

(limited to 'gr-blocks/lib/wavfile_source_impl.cc')

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;
-- 
cgit v1.2.3