patch-gnuradio
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Patch-gnuradio] wav sources/sink


From: Martin Braun
Subject: Re: [Patch-gnuradio] wav sources/sink
Date: Wed, 23 Jan 2008 19:57:09 +0100
User-agent: Thunderbird 2.0.0.9 (X11/20070801)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Here's the WAV sink/source blocks again, with some modifications Eric
and Johnathan requested. I've also attached a qa-script and a test file.
I'd appreciate it if someone could test this on a big-endian machine; I
don't have one around here. Running a "make test" should be enough to
figure out if it's working. The byte switching definitively works.

Before running the test, make sure the AC_C_BIGENDIAN autoconf macro is
used (more precisely, the WORDS_BIGENDIAN macro is defined) and
byteswap.h is checked for.

- --
Martin Braun
Institut für Nachrichtentechnik
Universitaet Karlsruhe

http://www.int.uni-karlsruhe.de
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4-svn0 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iD8DBQFHl44EduCCkYwG6NARAmxlAKCimO2E4xxLfv9ix1UA2NIVh1BnvQCfX+5r
YqxfKr2D7QakmqUUGIiGPRc=
=Bl/1
-----END PGP SIGNATURE-----
/* -*- c++ -*- */
/*
 * Copyright 2004 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 <gri_wavfile.h>
#include <cstring>
#include <stdint.h>

# define VALID_COMPRESSION_TYPE 0x0001

// WAV files are always little-endian, so we need some byte switching macros
#ifdef WORDS_BIGENDIAN

#ifdef HAVE_BYTESWAP_H
#include <byteswap.h>
#else
#warning Using non-portable code (likely wrong other than ILP32).

static inline short int
bswap_16 (unsigned short int x)
{
  return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8));
}

static inline unsigned int
bswap_32 (unsigned int x)
{
  return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) \
        | (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24));
}
#endif // HAVE_BYTESWAP_H

static inline uint32_t
host_to_wav(uint32_t x)
{
  return bswap_32(x);
}

static inline uint16_t
host_to_wav(uint16_t x)
{
  return bswap_16(x);
}

static inline int16_t
host_to_wav(int16_t x)
{
  return bswap_32(x);
}

static inline uint32_t
wav_to_host(uint32_t x)
{
  return bswap_32(x);
}

static inline uint16_t
wav_to_host(uint16_t x)
{
  return bswap_16(x);
}

static inline int16_t
wav_to_host(int16_t x)
{
  return bswap_32(x);
}

#else

static inline uint32_t
host_to_wav(uint32_t x)
{
  return x;
}

static inline uint16_t
host_to_wav(uint16_t x)
{
  return x;
}

static inline int16_t
host_to_wav(int16_t x)
{
  return x;
}

static inline uint32_t
wav_to_host(uint32_t x)
{
  return x;
}

static inline uint16_t
wav_to_host(uint16_t x)
{
  return x;
}

static inline int16_t
wav_to_host(int16_t x)
{
  return x;
}

#endif // WORDS_BIGENDIAN


bool
gri_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;
        }

        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;
        }

        fread(&fmt_hdr_skip, 1, 4, fp);

        fread(&compression_type, 1, 2, fp);
        if (wav_to_host(compression_type) != VALID_COMPRESSION_TYPE) {
                return false;
        }

        fread(&nchans,            1, 2, fp);
        fread(&sample_rate,       1, 4, fp);
        fread(&avg_bytes_per_sec, 1, 4, fp);
        fread(&block_align,       1, 2, fp);
        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;
        }
        fread(&chunk_size, 1, 4, fp);
        if (ferror(fp)) {
                return false;
        }

        // More byte swapping 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
gri_wav_read_sample(FILE *fp, int bytes_per_sample)
{
        int16_t buf = 0;
        fread(&buf, bytes_per_sample, 1, fp);

        return (short) wav_to_host(buf);
}


bool
gri_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
gri_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
gri_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;
}
/* -*- c++ -*- */
/*
 * Copyright 2004 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 <cstdio>

/*!
 * \brief Read signal information from a given WAV file.
 *
 * \p fp File pointer to an opened, empty file.
 * \p sample_rate Stores the sample rate [S/s]
 * \p nchans      Number of channels
 * \p bytes_per_sample Bytes per sample, can either be 1 or 2 (corresponding to
 *         8 or 16 bit samples, respectively)
 * \p 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.
 * \p samples_per_chan Number of samples per channel
 * \p normalize_fac The normalization factor with which you need to divide the
 *         integer values of the samples to get them within [-1;1]
 * \p normalize_shift The value by which the sample values need to be shifted
 *         after normalization (reason being, 8-bit WAV files store samples as
 *         unsigned char and 16-bit as signed short int)
 * \return True on a successful read, false if the file could not be read or is
 *         not a valid WAV file.
 */
bool
gri_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
gri_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
gri_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 endiannes.
 */
void
gri_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).
 *
 * \p fp File pointer to an open WAV file with a blank header
 * \p byte_count Length of all samples written to the file in bytes.
 */
bool
gri_wavheader_complete(FILE *fp, unsigned int byte_count);

Attachment: gr_wavfile_i.DEFANGED-5189
Description: application/defanged-5189

/* -*- c++ -*- */
/*
 * Copyright 2004,2006,2007 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 <gr_wavfile_sink.h>
#include <gr_io_signature.h>
#include <gri_wavfile.h>
#include <stdexcept>
#include <climits>
#include <cstring>
#include <cmath>
#include <fcntl.h>

// 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


gr_wavfile_sink_sptr
gr_make_wavfile_sink (const char *filename,
                int n_channels,
                unsigned int sample_rate,
                int bits_per_sample)
{
        return gr_wavfile_sink_sptr (new gr_wavfile_sink (filename,
                                                        n_channels,
                                                        sample_rate,
                                                        bits_per_sample));
}

gr_wavfile_sink::gr_wavfile_sink(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
gr_wavfile_sink::open(const char* filename)
{
        omni_mutex_lock l(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 (!gri_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
gr_wavfile_sink::close()
{
        omni_mutex_lock l(d_mutex);

        if (!d_fp)
                return;

        close_wav();
}

void gr_wavfile_sink::close_wav()
{
        unsigned int byte_count = d_sample_count * d_bytes_per_sample;

        gri_wavheader_complete(d_fp, byte_count);

        fclose(d_fp);
        d_fp = NULL;
}


gr_wavfile_sink::~gr_wavfile_sink ()
{
        if (d_new_fp) {
                fclose(d_new_fp);
        }
        close();
}


int
gr_wavfile_sink::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;

        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;
                        }

                        gri_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
gr_wavfile_sink::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) roundf(sample);
}


void
gr_wavfile_sink::set_bits_per_sample(int bits_per_sample)
{
        omni_mutex_lock l(d_mutex);
        if (bits_per_sample == 8 || bits_per_sample == 16) {
                d_bytes_per_sample_new = bits_per_sample / 8;
        }
}


void
gr_wavfile_sink::set_sample_rate(unsigned int sample_rate)
{
        omni_mutex_lock l(d_mutex);
        d_sample_rate = sample_rate;
}


void
gr_wavfile_sink::do_update()
{
        if (!d_updated) {
                return;
        }

        omni_mutex_lock     l(d_mutex);     // hold mutex for duration of this 
block
        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;
}

/* -*- c++ -*- */
/*
 * Copyright 2004 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 <gr_sync_block.h>
#include <gr_file_sink_base.h>
#include <omnithread.h>

class gr_wavfile_sink;
typedef boost::shared_ptr<gr_wavfile_sink> gr_wavfile_sink_sptr;

/*
 * \p filename The .wav file to be opened
 * \p n_channes Number of channels (2 = stereo or I/Q output)
 * \p sample_rate Sample rate [S/s]
 * \p bits_per_sample 16 or 8 bit
 */
gr_wavfile_sink_sptr
gr_make_wavfile_sink (const char *filename,
                int n_channels,
                unsigned int sample_rate,
                int bits_per_sample = 16);

/*!
 * \brief Read stream from a Microsoft PCM (.wav) file, output floats
 *
 * Values are within [-1;1].
 * Check gr_make_wavfile_source() for extra info.
 *
 * \ingroup sink
 */
class gr_wavfile_sink : public gr_sync_block
{
        friend gr_wavfile_sink_sptr gr_make_wavfile_sink (const char *filename,
                                                int n_channels,
                                                unsigned int sample_rate,
                                                int bits_per_sample);

 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;
        omni_mutex d_mutex;

        /*!
         * \brief Convert a sample value within [-1;+1] to a corresponding
         *  short integer value
         */
        short convert_to_short(float sample);

        /*!
         * \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();

 protected:
        gr_wavfile_sink(const char *filename,
                        int n_channels,
                        unsigned int sample_rate,
                        int bits_per_sample);

 public:
        ~gr_wavfile_sink ();

        /*!
         * \brief Opens a new file and writes a WAV header. Thread-safe.
         */
        bool open(const char* filename);

        /*!
         * \brief Closes the currently active file and completes the WAV
         * header. Thread-safe.
         */
        void close();

        /*!
         * \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 Set the sample rate. This will not affect the WAV file
         * currently opened. Any following open() calls will use this new
         * sample rate.
         */
        void set_sample_rate(unsigned int sample_rate);

        /*!
         * \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.
         */
        void set_bits_per_sample(int bits_per_sample);


        int work (int noutput_items,
            gr_vector_const_void_star &input_items,
            gr_vector_void_star &output_items);

};

#endif /* INCLUDED_GR_WAVFILE_SINK_H */
/* -*- c++ -*- */
/*
 * Copyright 2004 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 <gr_wavfile_source.h>
#include <gr_io_signature.h>
#include <gri_wavfile.h>
#include <cstdio>
#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


gr_wavfile_source_sptr
gr_make_wavfile_source (const char *filename, bool repeat)
{
  return gr_wavfile_source_sptr (new gr_wavfile_source (filename, repeat));
}


gr_wavfile_source::gr_wavfile_source (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 (!gri_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)));
}


gr_wavfile_source::~gr_wavfile_source ()
{
        fclose ((FILE *) d_fp);
}


int
gr_wavfile_source::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 = gri_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;
}


float
gr_wavfile_source::convert_to_float(short int sample)
{
        float sample_out = (float) sample;
        sample_out /= d_normalize_fac;
        sample_out -= d_normalize_shift;
        return sample_out;
}
/* -*- c++ -*- */
/*
 * Copyright 2004 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 <gr_sync_block.h>

class gr_wavfile_source;
typedef boost::shared_ptr<gr_wavfile_source> gr_wavfile_source_sptr;

gr_wavfile_source_sptr
gr_make_wavfile_source (const char *filename, bool repeat = false);

/*!
 * \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
 */

class gr_wavfile_source : public gr_sync_block
{
        friend gr_wavfile_source_sptr gr_make_wavfile_source (const char 
*filename,
                                                              bool repeat);
 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;

        gr_wavfile_source(const char *filename, bool repeat);

        /*!
         * \brief Convert an integer sample value to a float value within [-1;1]
         */
        float convert_to_float(short int sample);


 public:
        ~gr_wavfile_source ();

        int work (int noutput_items,
            gr_vector_const_void_star &input_items,
            gr_vector_void_star &output_items);

        /*!
         * \brief Read the sample rate as specified in the wav file header
         */
        unsigned int get_sample_rate() { return d_sample_rate; };

        /*!
         * \brief Return the number of bits per sample as specified in the wav
         * file header. Only 8 or 16 bit are supported here.
         */
        int get_bits_per_sample() { return d_bytes_per_sample * 8; };

        /*!
         * \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.
         */
        int get_n_channels() { return d_nchans; };
};

#endif /* INCLUDED_GR_WAVFILE_SOURCE_H */

Attachment: qa_wavefile_py.DEFANGED-5190
Description: application/defanged-5190

Attachment: test_16bit_1chunk_wav.DEFANGED-5192
Description: application/defanged-5192


reply via email to

[Prev in Thread] Current Thread [Next in Thread]