Commit cf9169d0 authored by Stefan Westerfeld's avatar Stefan Westerfeld

Merge branch 'wav-pipe'

* wav-pipe:
  README.adoc: fix typo
  README.adoc: update raw streams description (wav-pipe format related)
  README.adoc: basic documentation for wav-pipe format
  Bump copyright date in wav-pipe format.
  Check wav input format type from header.
  Use more efficient float->int conversion in SFOutputStream.
  Bump samples per read/write buffer size for wav pipe I/O.
  Improve wav-pipe format input error handling.
  Use similar API for from/to raw conversion in RawConverter.
  WavPipeInputStream: avoid filling sample array with zeros before read.
  Selectively enable -O3 for gcc for some functions (use auto vectorization).
  Optimize wav-pipe format a bit more.
  Optimize native endian case for RawConverter float->int 16/32 bit.
  Support 32-bit output pipe wav.
  Optimize RawConverter native endian raw->float conversion.
  BUILD: add endianness test
  Support new format (--format wav-pipe) for wav pipes without size limit.
  Support 32 bit conversions in RawConverter.
Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parents 0afd30ad 269d26c1
......@@ -434,6 +434,53 @@ Streaming input is also supported for watermark detection.
cat in.wav | audiowmark get -
== Wav Pipe Format
In some cases, the length of the streaming input is not known by the program
that produces the stream. For instance consider a mp3 that is being decoded by
madplay.
cat in.mp3 |
madplay -o wave:- - |
audiowmark add - out.wav f0
Since madplay doesn't know the length of the output when it starts decoding the
mp3, the best it can do is to fill the wav header with a big number. And
indeed, audiowmark will watermark the stream, but also print a warning like
this:
audiowmark: warning: unexpected EOF; input frames (1073741823) != output frames (8316288)
This may sound harmless, but for very long input streams, this will also
truncate the audio input after this length. If you already know that you need
to input a wav file from a pipe (without correct length in the header) and
simply want to watermark all of it, it is better to use the `wav-pipe` format:
cat in.mp3 |
madplay -o wave:- - |
audiowmark add --input-format wav-pipe --output-format rf64 - out.wav f0
This will not print a warning, and it also works correctly for long input
streams. Note that using `rf64` as output format is necessary for huge output
files (larger than 4G).
Similar to pipe input, audiowmark can write a wav header with a huge number (in
cases where it does not know the length in advance) if the output format is set
to `wav-pipe`.
cat in.mp3 |
madplay -o wave:- - |
audiowmark add --input-format wav-pipe --output-format wav-pipe - - f0 |
lame - > out.mp3
If you need both, `wav-pipe` input and output, a shorter way to write it is
using `--format wav-pipe`, like this:
cat in.mp3 |
madplay -o wave:- - |
audiowmark add --format wav-pipe - - f0 |
lame - > out.mp3
== Raw Streams
So far, all streams described here are essentially wav streams, which means
......@@ -442,15 +489,10 @@ channels, bit depth, encoding and so forth from the stream itself, and the a
wav header is written for the program after `audiowmark`, so that this can
figure out the parameters of the stream.
There are two cases where this is problematic. The first case is if the full
length of the stream is not known at the time processing starts. Then a wav
header cannot be used, as the wav file contains the length of the stream. The
second case is that the program before or after `audiowmark` doesn't support wav
headers.
For these two cases, raw streams are available. The idea is to set all
information that is needed like sample rate, number of channels,... manually.
Then, headerless data can be processed from stdin and/or sent to stdout.
If the program before or after `audiowmark` doesn't support wav headers, raw
streams can be used instead. The idea is to set all information that is needed
like sample rate, number of channels,... manually. Then, headerless data can
be processed from stdin and/or sent to stdout.
--input-format raw::
--output-format raw::
......
......@@ -7,6 +7,8 @@ AM_INIT_AUTOMAKE([foreign])
AC_PROG_CXX
LT_INIT
AC_C_BIGENDIAN()
dnl
dnl sndfile
dnl
......
......@@ -7,7 +7,7 @@ COMMON_SRC = utils.hh utils.cc convcode.hh convcode.cc random.hh random.cc wavda
rawconverter.cc rawconverter.hh mp3inputstream.cc mp3inputstream.hh wmcommon.cc wmcommon.hh fft.cc fft.hh \
limiter.cc limiter.hh shortcode.cc shortcode.hh mpegts.cc mpegts.hh hls.cc hls.hh audiobuffer.hh \
wmget.cc wmadd.cc syncfinder.cc syncfinder.hh wmspeed.cc wmspeed.hh threadpool.cc threadpool.hh \
resample.cc resample.hh
resample.cc resample.hh wavpipeinputstream.cc wavpipeinputstream.hh
COMMON_LIBS = $(SNDFILE_LIBS) $(FFTW_LIBS) $(LIBGCRYPT_LIBS) $(LIBMPG123_LIBS) $(FFMPEG_LIBS) $(LTLIBZITA_RESAMPLER)
AM_CXXFLAGS = $(SNDFILE_CFLAGS) $(FFTW_CFLAGS) $(LIBGCRYPT_CFLAGS) $(LIBMPG123_CFLAGS) $(FFMPEG_CFLAGS)
......
......@@ -23,6 +23,7 @@
#include "rawconverter.hh"
#include "rawoutputstream.hh"
#include "stdoutwavoutputstream.hh"
#include "wavpipeinputstream.hh"
using std::string;
......@@ -52,7 +53,7 @@ AudioInputStream::create (const string& filename, Error& err)
else if (err)
return nullptr;
}
else
else if (Params::input_format == Format::RAW)
{
RawInputStream *ristream = new RawInputStream();
in_stream.reset (ristream);
......@@ -61,6 +62,20 @@ AudioInputStream::create (const string& filename, Error& err)
if (err)
return nullptr;
}
else if (Params::input_format == Format::WAV_PIPE)
{
WavPipeInputStream *wistream = new WavPipeInputStream();
in_stream.reset (wistream);
err = wistream->open (filename);
if (err)
return nullptr;
}
else
{
err = Error ("selected format is not supported as input format");
return nullptr;
}
return in_stream;
}
......@@ -79,9 +94,11 @@ AudioOutputStream::create (const string& filename, int n_channels, int sample_ra
}
else if (filename == "-")
{
bool wav_pipe = Params::output_format == Format::WAV_PIPE;
StdoutWavOutputStream *swstream = new StdoutWavOutputStream();
out_stream.reset (swstream);
err = swstream->open (n_channels, sample_rate, bit_depth, n_frames);
err = swstream->open (n_channels, sample_rate, bit_depth, n_frames, wav_pipe);
if (err)
return nullptr;
}
......
......@@ -119,6 +119,8 @@ parse_format (const string& str)
return Format::AUTO;
if (str == "rf64")
return Format::RF64;
if (str == "wav-pipe")
return Format::WAV_PIPE;
error ("audiowmark: unsupported format '%s'\n", str.c_str());
exit (1);
......
......@@ -16,10 +16,12 @@
*/
#include "rawconverter.hh"
#include "config.h"
#include <array>
#include <math.h>
#include <assert.h>
using std::vector;
......@@ -28,11 +30,11 @@ RawConverter::~RawConverter()
}
template<int BIT_DEPTH, RawFormat::Endian ENDIAN, RawFormat::Encoding ENCODING>
class RawConverterImpl : public RawConverter
class RawConverterImpl final : public RawConverter
{
public:
void to_raw (const std::vector<float>& samples, std::vector<unsigned char>& bytes);
void from_raw (const std::vector<unsigned char>& bytes, std::vector<float>& samples);
void to_raw (const float *samples, unsigned char *bytes, size_t n_samples) override AUDIOWMARK_EXTRA_OPT;
void from_raw (const unsigned char *bytes, float *samples, size_t n_samples) override AUDIOWMARK_EXTRA_OPT;
};
template<int BIT_DEPTH, RawFormat::Endian ENDIAN>
......@@ -69,85 +71,117 @@ RawConverter::create (const RawFormat& raw_format, Error& error)
{
case 16: return create_with_bits<16> (raw_format, error);
case 24: return create_with_bits<24> (raw_format, error);
case 32: return create_with_bits<32> (raw_format, error);
default: error = Error ("unsupported bit depth");
return nullptr;
}
}
template<int BIT_DEPTH, RawFormat::Endian ENDIAN>
constexpr std::array<int, 3>
constexpr std::array<int, 4>
make_endian_shift ()
{
if (BIT_DEPTH == 16)
{
if (ENDIAN == RawFormat::Endian::LITTLE)
return { 16, 24, -1 };
return { 16, 24, -1, -1 };
else
return { 24, 16, -1 };
return { 24, 16, -1, -1 };
}
if (BIT_DEPTH == 24)
{
if (ENDIAN == RawFormat::Endian::LITTLE)
return { 8, 16, 24 };
return { 8, 16, 24, -1 };
else
return { 24, 16, 8 };
return { 24, 16, 8, -1 };
}
if (BIT_DEPTH == 32)
{
if (ENDIAN == RawFormat::Endian::LITTLE)
return { 0, 8, 16, 24 };
else
return { 24, 16, 8, 0 };
}
}
template<int BIT_DEPTH, RawFormat::Endian ENDIAN, RawFormat::Encoding ENCODING>
void
RawConverterImpl<BIT_DEPTH, ENDIAN, ENCODING>::to_raw (const vector<float>& samples, vector<unsigned char>& output_bytes)
RawConverterImpl<BIT_DEPTH, ENDIAN, ENCODING>::to_raw (const float *samples, unsigned char *output_bytes, size_t n_samples)
{
constexpr int sample_width = BIT_DEPTH / 8;
constexpr auto eshift = make_endian_shift<BIT_DEPTH, ENDIAN>();
constexpr unsigned char sign_flip = ENCODING == RawFormat::SIGNED ? 0x00 : 0x80;
#ifdef WORDS_BIGENDDIAN
constexpr bool native_endian = ENDIAN == RawFormat::BIG;
#else
constexpr bool native_endian = ENDIAN == RawFormat::LITTLE;
#endif
assert ((uintptr_t (output_bytes) & 3) == 0); // ensure alignment for optimized 32 bit native endian version
output_bytes.resize (sample_width * samples.size());
unsigned char *ptr = output_bytes.data();
unsigned char *ptr = output_bytes;
for (size_t i = 0; i < samples.size(); i++)
for (size_t i = 0; i < n_samples; i++)
{
const double norm = 0x80000000LL;
const double min_value = -0x80000000LL;
const double max_value = 0x7FFFFFFF;
const int sample = lrint (bound<double> (min_value, samples[i] * norm, max_value));
if (eshift[0] >= 0)
ptr[0] = (sample >> eshift[0]) ^ sign_flip;
if (eshift[1] >= 0)
ptr[1] = sample >> eshift[1];
if (eshift[2] >= 0)
ptr[2] = sample >> eshift[2];
ptr += sample_width;
if (native_endian && ENCODING == RawFormat::SIGNED && BIT_DEPTH == 32)
((int32_t *)ptr)[i] = float_to_int_clip<32> (samples[i]);
else if (native_endian && ENCODING == RawFormat::SIGNED && BIT_DEPTH == 16)
((int16_t *)ptr)[i] = float_to_int_clip<16> (samples[i]);
else
{
int sample = float_to_int_clip<32> (samples[i]);
if (eshift[0] >= 0)
ptr[0] = (sample >> eshift[0]) ^ sign_flip;
if (eshift[1] >= 0)
ptr[1] = sample >> eshift[1];
if (eshift[2] >= 0)
ptr[2] = sample >> eshift[2];
if (eshift[3] >= 0)
ptr[3] = sample >> eshift[3];
ptr += sample_width;
}
}
}
template<int BIT_DEPTH, RawFormat::Endian ENDIAN, RawFormat::Encoding ENCODING>
void
RawConverterImpl<BIT_DEPTH, ENDIAN, ENCODING>::from_raw (const vector<unsigned char>& input_bytes, vector<float>& samples)
RawConverterImpl<BIT_DEPTH, ENDIAN, ENCODING>::from_raw (const unsigned char *input_bytes, float *samples, size_t n_samples)
{
const unsigned char *ptr = input_bytes.data();
const unsigned char *ptr = input_bytes;
constexpr int sample_width = BIT_DEPTH / 8;
constexpr auto eshift = make_endian_shift<BIT_DEPTH, ENDIAN>();
constexpr unsigned char sign_flip = ENCODING == RawFormat::SIGNED ? 0x00 : 0x80;
samples.resize (input_bytes.size() / sample_width);
const double norm = 1.0 / 0x80000000LL;
for (size_t i = 0; i < samples.size(); i++)
#ifdef WORDS_BIGENDDIAN
constexpr bool native_endian = ENDIAN == RawFormat::BIG;
#else
constexpr bool native_endian = ENDIAN == RawFormat::LITTLE;
#endif
assert ((uintptr_t (input_bytes) & 3) == 0); // ensure alignment for optimized 32 bit native endian version
const float norm = 1.0 / 0x80000000LL;
for (size_t i = 0; i < n_samples; i++)
{
int s32 = 0;
if (eshift[0] >= 0)
s32 += (ptr[0] ^ sign_flip) << eshift[0];
if (eshift[1] >= 0)
s32 += ptr[1] << eshift[1];
if (eshift[2] >= 0)
s32 += ptr[2] << eshift[2];
if (native_endian && ENCODING == RawFormat::SIGNED && BIT_DEPTH == 32)
samples[i] = ((int32_t *)ptr)[i] * norm;
else if (native_endian && ENCODING == RawFormat::SIGNED && BIT_DEPTH == 16)
samples[i] = ((int16_t *)ptr)[i] * float (1.0 / 0x8000);
else
{
int s32 = 0;
if (eshift[0] >= 0)
s32 += (ptr[0] ^ sign_flip) << eshift[0];
if (eshift[1] >= 0)
s32 += ptr[1] << eshift[1];
if (eshift[2] >= 0)
s32 += ptr[2] << eshift[2];
if (eshift[3] >= 0)
s32 += ptr[3] << eshift[3];
samples[i] = s32 * norm;
ptr += sample_width;
}
samples[i] = s32 * norm;
ptr += sample_width;
}
}
......@@ -27,8 +27,26 @@ public:
virtual ~RawConverter() = 0;
virtual void to_raw (const std::vector<float>& samples, std::vector<unsigned char>& bytes) = 0;
virtual void from_raw (const std::vector<unsigned char>& bytes, std::vector<float>& samples) = 0;
virtual void to_raw (const float *samples, unsigned char *bytes, size_t n_samples) = 0;
virtual void from_raw (const unsigned char *bytes, float *samples, size_t n_samples) = 0;
};
template<int BITS>
static inline int
float_to_int_clip (float f)
{
const int64_t inorm = (1LL << (BITS - 1));
const float min_value = -inorm;
const float max_value = inorm - 1;
const float norm = inorm;
const float snorm = f * norm;
if (snorm >= max_value)
return inorm - 1;
else if (snorm <= min_value)
return -inorm;
else
return snorm;
}
#endif /* AUDIOWMARK_RAW_CONVERTER_HH */
......@@ -144,9 +144,9 @@ RawInputStream::read_frames (vector<float>& samples, size_t count)
if (ferror (m_input_file))
return Error ("error reading sample data");
input_bytes.resize (r_count * n_channels * sample_width);
samples.resize (r_count * n_channels);
m_raw_converter->from_raw (input_bytes, samples);
m_raw_converter->from_raw (input_bytes.data(), samples.data(), r_count * n_channels);
return Error::Code::NONE;
}
......
......@@ -91,8 +91,8 @@ RawOutputStream::write_frames (const vector<float>& samples)
if (samples.empty())
return Error::Code::NONE;
vector<unsigned char> bytes;
m_raw_converter->to_raw (samples, bytes);
vector<unsigned char> bytes (samples.size() * m_format.bit_depth() / 8);
m_raw_converter->to_raw (samples.data(), bytes.data(), samples.size());
fwrite (&bytes[0], 1, bytes.size(), m_output_file);
if (ferror (m_output_file))
......
......@@ -155,7 +155,7 @@ SFInputStream::read_frames (vector<float>& samples, size_t count)
* used during read and write are identical
*/
samples.resize (r_count * m_n_channels);
const double norm = 1.0 / 0x80000000LL;
const float norm = 1.0 / 0x80000000LL;
for (size_t i = 0; i < samples.size(); i++)
samples[i] = isamples[i] * norm;
}
......
......@@ -61,7 +61,7 @@ public:
Error open (const std::string& filename);
Error open (const std::vector<unsigned char> *data);
Error read_frames (std::vector<float>& samples, size_t count) override;
Error read_frames (std::vector<float>& samples, size_t count) override AUDIOWMARK_EXTRA_OPT;
void close();
int
......
......@@ -16,6 +16,7 @@
*/
#include "sfoutputstream.hh"
#include "rawconverter.hh"
#include "utils.hh"
#include <math.h>
......@@ -103,13 +104,7 @@ SFOutputStream::write_frames (const vector<float>& samples)
{
vector<int> isamples (samples.size());
for (size_t i = 0; i < samples.size(); i++)
{
const double norm = 0x80000000LL;
const double min_value = -0x80000000LL;
const double max_value = 0x7FFFFFFF;
isamples[i] = lrint (bound<double> (min_value, samples[i] * norm, max_value));
}
isamples[i] = float_to_int_clip<32> (samples[i]);
sf_count_t frames = samples.size() / m_n_channels;
sf_count_t count = sf_writef_int (m_sndfile, isamples.data(), frames);
......
......@@ -51,7 +51,7 @@ public:
Error open (const std::string& filename, int n_channels, int sample_rate, int bit_depth, OutFormat out_format = OutFormat::WAV);
Error open (std::vector<unsigned char> *data, int n_channels, int sample_rate, int bit_depth, OutFormat out_format = OutFormat::WAV);
Error write_frames (const std::vector<float>& frames) override;
Error write_frames (const std::vector<float>& frames) override AUDIOWMARK_EXTRA_OPT;
Error close() override;
int bit_depth() const override;
int sample_rate() const override;
......
......@@ -19,10 +19,12 @@
#include "utils.hh"
#include <assert.h>
#include <string.h>
#include <math.h>
using std::string;
using std::vector;
using std::min;
StdoutWavOutputStream::~StdoutWavOutputStream()
{
......@@ -71,19 +73,23 @@ header_append_u16 (vector<unsigned char>& bytes, uint16_t u)
}
Error
StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, size_t n_frames)
StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, size_t n_frames, bool wav_pipe)
{
assert (m_state == State::NEW);
if (bit_depth != 16 && bit_depth != 24)
if (bit_depth != 16 && bit_depth != 24 && bit_depth != 32)
{
return Error ("StdoutWavOutputStream::open: unsupported bit depth");
}
if (n_frames == AudioInputStream::N_FRAMES_UNKNOWN)
if (n_frames == AudioInputStream::N_FRAMES_UNKNOWN && !wav_pipe)
{
return Error ("unable to write wav format to standard out without input length information");
}
// 32-bit output is a faster (less conversion overhead), so we use it for pipes
if (bit_depth > 16 && wav_pipe)
bit_depth = 32;
RawFormat format;
format.set_bit_depth (bit_depth);
......@@ -100,7 +106,10 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz
size_t aligned_data_size = data_size + m_close_padding;
header_append_str (header_bytes, "RIFF");
header_append_u32 (header_bytes, 36 + aligned_data_size);
if (wav_pipe)
header_append_u32 (header_bytes, -1);
else
header_append_u32 (header_bytes, 36 + aligned_data_size);
header_append_str (header_bytes, "WAVE");
// subchunk 1
......@@ -115,7 +124,10 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz
// subchunk 2
header_append_str (header_bytes, "data");
header_append_u32 (header_bytes, data_size);
if (wav_pipe)
header_append_u32 (header_bytes, -1);
else
header_append_u32 (header_bytes, data_size);
fwrite (&header_bytes[0], 1, header_bytes.size(), stdout);
if (ferror (stdout))
......@@ -135,14 +147,22 @@ StdoutWavOutputStream::write_frames (const vector<float>& samples)
if (samples.empty())
return Error::Code::NONE;
vector<unsigned char> output_bytes;
const size_t block_size = 8192 * m_n_channels;
const int sample_width = m_bit_depth / 8;
m_raw_converter->to_raw (samples, output_bytes);
m_output_bytes.resize (sample_width * block_size);
size_t pos = 0;
fwrite (&output_bytes[0], 1, output_bytes.size(), stdout);
if (ferror (stdout))
return Error ("write sample data failed");
while (size_t todo = min (block_size, samples.size() - pos))
{
m_raw_converter->to_raw (samples.data() + pos, m_output_bytes.data(), todo);
fwrite (m_output_bytes.data(), 1, todo * sample_width, stdout);
if (ferror (stdout))
return Error (string_printf ("write sample data failed (%s)", strerror (errno)));
pos += todo;
}
return Error::Code::NONE;
}
......
......@@ -37,12 +37,13 @@ class StdoutWavOutputStream : public AudioOutputStream
};
State m_state = State::NEW;
std::vector<unsigned char> m_output_bytes;
std::unique_ptr<RawConverter> m_raw_converter;
public:
~StdoutWavOutputStream();
Error open (int n_channels, int sample_rate, int bit_depth, size_t n_frames);
Error open (int n_channels, int sample_rate, int bit_depth, size_t n_frames, bool wav_pipe);
Error write_frames (const std::vector<float>& frames) override;
Error close() override;
int sample_rate() const override;
......
......@@ -41,7 +41,7 @@ main (int argc, char **argv)
fprintf (stderr, "teststream: open input failed: %s\n", err.message());
return 1;
}
err = out.open (in.n_channels(), in.sample_rate(), 16, in.n_frames());
err = out.open (in.n_channels(), in.sample_rate(), 16, in.n_frames(), false);
if (err)
{
fprintf (stderr, "teststream: open output failed: %s\n", err.message());
......
......@@ -39,8 +39,10 @@ bound (const T& min_value, const T& value, const T& max_value)
// detect compiler
#if __clang__
#define AUDIOWMARK_COMP_CLANG
#define AUDIOWMARK_EXTRA_OPT
#elif __GNUC__ > 2
#define AUDIOWMARK_COMP_GCC
#define AUDIOWMARK_EXTRA_OPT __attribute__((optimize("-O3"))) /* enable auto vectorization, some functions benefit a lot from this */
#else
#error "unsupported compiler"
#endif
......
/*
* Copyright (C) 2018-2024 Stefan Westerfeld
*
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "wavpipeinputstream.hh"
#include "rawconverter.hh"
#include <assert.h>
#include <string.h>
#include <errno.h>
using std::string;
using std::vector;
using std::min;
using std::max;
WavPipeInputStream::~WavPipeInputStream()
{
close();
}
string
header_get_4cc (unsigned char *bytes)
{
string s;
s += bytes[0];
s += bytes[1];
s += bytes[2];
s += bytes[3];
return s;
}
static uint16_t
header_get_u16 (unsigned char *bytes)
{
return bytes[0] + (bytes[1] << 8);
}
static uint32_t
header_get_u32 (unsigned char *bytes)
{
return bytes[0] + (bytes[1] << 8) + (bytes[2] << 16) + (bytes[3] << 24);
}
Error
WavPipeInputStream::read_error (const std::string& message)
{
if (ferror (m_input_file))
return Error (string_printf ("wav input read error: %s", strerror (errno)));
else
return Error (message);
}
Error
WavPipeInputStream::open (const string& filename)
{
assert (m_state == State::NEW);
Error err = Error::Code::NONE;
if (err)
return err;
if (filename == "-")
{
m_input_file = stdin;
m_close_file = false;
}
else
{
m_input_file = fopen (filename.c_str(), "r");
if (!m_input_file)
return Error (strerror (errno));
m_close_file = true;
}
RawFormat format;
unsigned char riff_buffer[12];
bool riff_ok = fread (riff_buffer, sizeof (riff_buffer), 1, m_input_file);
if (!riff_ok || (header_get_4cc (riff_buffer) != "RIFF" && header_get_4cc (riff_buffer) != "RF64") ||
header_get_4cc (riff_buffer + 8) != "WAVE")
return read_error ("input file is not a valid wav file");
bool in_data_chunk = false;
bool have_fmt_chunk = false;
do
{
unsigned char chunk[8];
if (!fread (chunk, sizeof (chunk), 1, m_input_file))
return read_error ("wav input is incomplete (no data chunk found)");
uint32_t chunk_size = header_get_u32 (chunk + 4);
if (header_get_4cc (chunk) == "fmt " && chunk_size >= 16 && chunk_size <= 64 * 1024 && !have_fmt_chunk)
{
vector<unsigned char> buffer (chunk_size);
if (!fread (buffer.data(), buffer.size(), 1, m_input_file))
return read_error ("wav input is incomplete (error reading fmt chunk)");
int format_type = header_get_u16 (&buffer[0]);
if (format_type != 1)
{
if (format_type == 0xFFFE && chunk_size >= 40) /* extended */
{
static const std::array<unsigned char, 16> pcm_fmt_guid
{
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71
};
if (!std::equal (pcm_fmt_guid.begin(), pcm_fmt_guid.end(), &buffer[24]))
return Error (string_printf ("wav input has unsupported extended format type, expected PCM"));
}
else
{
return Error (string_printf ("wav input has unsupported format type (%d), expected PCM", format_type));
}
}
format.set_channels (header_get_u16 (&buffer[2]));
format.set_sample_rate (header_get_u32 (&buffer[4]));
format.set_bit_depth (header_get_u16 (&buffer[14]));
have_fmt_chunk = true;
}
else if (header_get_4cc (chunk) == "data")
{