Commit fff6bf48 authored by Stefan Westerfeld's avatar Stefan Westerfeld

Support new format (--format wav-pipe) for wav pipes without size limit.

Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parent 0dd38668
...@@ -7,7 +7,7 @@ COMMON_SRC = utils.hh utils.cc convcode.hh convcode.cc random.hh random.cc wavda ...@@ -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 \ 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 \ 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 \ 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) 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) AM_CXXFLAGS = $(SNDFILE_CFLAGS) $(FFTW_CFLAGS) $(LIBGCRYPT_CFLAGS) $(LIBMPG123_CFLAGS) $(FFMPEG_CFLAGS)
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "rawconverter.hh" #include "rawconverter.hh"
#include "rawoutputstream.hh" #include "rawoutputstream.hh"
#include "stdoutwavoutputstream.hh" #include "stdoutwavoutputstream.hh"
#include "wavpipeinputstream.hh"
using std::string; using std::string;
...@@ -52,7 +53,7 @@ AudioInputStream::create (const string& filename, Error& err) ...@@ -52,7 +53,7 @@ AudioInputStream::create (const string& filename, Error& err)
else if (err) else if (err)
return nullptr; return nullptr;
} }
else else if (Params::input_format == Format::RAW)
{ {
RawInputStream *ristream = new RawInputStream(); RawInputStream *ristream = new RawInputStream();
in_stream.reset (ristream); in_stream.reset (ristream);
...@@ -61,6 +62,20 @@ AudioInputStream::create (const string& filename, Error& err) ...@@ -61,6 +62,20 @@ AudioInputStream::create (const string& filename, Error& err)
if (err) if (err)
return nullptr; 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; return in_stream;
} }
...@@ -79,9 +94,11 @@ AudioOutputStream::create (const string& filename, int n_channels, int sample_ra ...@@ -79,9 +94,11 @@ AudioOutputStream::create (const string& filename, int n_channels, int sample_ra
} }
else if (filename == "-") else if (filename == "-")
{ {
bool wav_pipe = Params::output_format == Format::WAV_PIPE;
StdoutWavOutputStream *swstream = new StdoutWavOutputStream(); StdoutWavOutputStream *swstream = new StdoutWavOutputStream();
out_stream.reset (swstream); 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) if (err)
return nullptr; return nullptr;
} }
......
...@@ -119,6 +119,8 @@ parse_format (const string& str) ...@@ -119,6 +119,8 @@ parse_format (const string& str)
return Format::AUTO; return Format::AUTO;
if (str == "rf64") if (str == "rf64")
return Format::RF64; return Format::RF64;
if (str == "wav-pipe")
return Format::WAV_PIPE;
error ("audiowmark: unsupported format '%s'\n", str.c_str()); error ("audiowmark: unsupported format '%s'\n", str.c_str());
exit (1); exit (1);
......
...@@ -71,7 +71,7 @@ header_append_u16 (vector<unsigned char>& bytes, uint16_t u) ...@@ -71,7 +71,7 @@ header_append_u16 (vector<unsigned char>& bytes, uint16_t u)
} }
Error 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); assert (m_state == State::NEW);
...@@ -79,7 +79,7 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz ...@@ -79,7 +79,7 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz
{ {
return Error ("StdoutWavOutputStream::open: unsupported bit depth"); 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"); return Error ("unable to write wav format to standard out without input length information");
} }
...@@ -100,6 +100,9 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz ...@@ -100,6 +100,9 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz
size_t aligned_data_size = data_size + m_close_padding; size_t aligned_data_size = data_size + m_close_padding;
header_append_str (header_bytes, "RIFF"); header_append_str (header_bytes, "RIFF");
if (wav_pipe)
header_append_u32 (header_bytes, -1);
else
header_append_u32 (header_bytes, 36 + aligned_data_size); header_append_u32 (header_bytes, 36 + aligned_data_size);
header_append_str (header_bytes, "WAVE"); header_append_str (header_bytes, "WAVE");
...@@ -115,6 +118,9 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz ...@@ -115,6 +118,9 @@ StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, siz
// subchunk 2 // subchunk 2
header_append_str (header_bytes, "data"); header_append_str (header_bytes, "data");
if (wav_pipe)
header_append_u32 (header_bytes, -1);
else
header_append_u32 (header_bytes, data_size); header_append_u32 (header_bytes, data_size);
fwrite (&header_bytes[0], 1, header_bytes.size(), stdout); fwrite (&header_bytes[0], 1, header_bytes.size(), stdout);
......
...@@ -42,7 +42,7 @@ class StdoutWavOutputStream : public AudioOutputStream ...@@ -42,7 +42,7 @@ class StdoutWavOutputStream : public AudioOutputStream
public: public:
~StdoutWavOutputStream(); ~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 write_frames (const std::vector<float>& frames) override;
Error close() override; Error close() override;
int sample_rate() const override; int sample_rate() const override;
......
...@@ -41,7 +41,7 @@ main (int argc, char **argv) ...@@ -41,7 +41,7 @@ main (int argc, char **argv)
fprintf (stderr, "teststream: open input failed: %s\n", err.message()); fprintf (stderr, "teststream: open input failed: %s\n", err.message());
return 1; 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) if (err)
{ {
fprintf (stderr, "teststream: open output failed: %s\n", err.message()); fprintf (stderr, "teststream: open output failed: %s\n", err.message());
......
/*
* Copyright (C) 2018-2020 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;
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::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];
size_t 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 Error ("input file is not a valid wav file");
bool in_data_chunk = false;
do
{
unsigned char chunk[8];
size_t rc = fread (chunk, sizeof (chunk), 1, m_input_file);
if (rc == 0)
return 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)
{
vector<unsigned char> buffer (chunk_size);
fread (buffer.data(), buffer.size(), 1, m_input_file);
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]));
}
else if (header_get_4cc (chunk) == "data")
{
in_data_chunk = true;
}
else // skip unknown chunk
{
char junk[1024];
while (chunk_size)
{
uint32_t todo = min<uint32_t> (chunk_size, sizeof (junk));
fread (junk, todo, 1, m_input_file);
chunk_size -= todo;
}
}
} while (!in_data_chunk);
m_raw_converter.reset (RawConverter::create (format, err));
if (err)
return err;
m_format = format;
m_state = State::OPEN;
return Error::Code::NONE;
}
int
WavPipeInputStream::sample_rate() const
{
return m_format.sample_rate();
}
int
WavPipeInputStream::bit_depth() const
{
return m_format.bit_depth();
}
size_t
WavPipeInputStream::n_frames() const
{
return N_FRAMES_UNKNOWN;
}
int
WavPipeInputStream::n_channels() const
{
return m_format.n_channels();
}
Error
WavPipeInputStream::read_frames (vector<float>& samples, size_t count)
{
assert (m_state == State::OPEN);
const int n_channels = m_format.n_channels();
const int sample_width = m_format.bit_depth() / 8;
vector<unsigned char> input_bytes (count * n_channels * sample_width);
size_t r_count = fread (input_bytes.data(), n_channels * sample_width, count, m_input_file);
if (ferror (m_input_file))
return Error ("error reading sample data");
input_bytes.resize (r_count * n_channels * sample_width);
m_raw_converter->from_raw (input_bytes, samples);
return Error::Code::NONE;
}
void
WavPipeInputStream::close()
{
if (m_state == State::OPEN)
{
if (m_close_file && m_input_file)
{
fclose (m_input_file);
m_input_file = nullptr;
m_close_file = false;
}
m_state = State::CLOSED;
}
}
/*
* Copyright (C) 2018-2020 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/>.
*/
#ifndef AUDIOWMARK_WAV_PIPE_INPUT_STREAM_HH
#define AUDIOWMARK_WAV_PIPE_INPUT_STREAM_HH
#include <string>
#include <memory>
#include <sndfile.h>
#include "audiostream.hh"
#include "rawinputstream.hh"
class WavPipeInputStream : public AudioInputStream
{
enum class State {
NEW,
OPEN,
CLOSED
};
State m_state = State::NEW;
RawFormat m_format;
FILE *m_input_file = nullptr;
bool m_close_file = false;
std::unique_ptr<RawConverter> m_raw_converter;
public:
~WavPipeInputStream();
Error open (const std::string& filename);
Error read_frames (std::vector<float>& samples, size_t count) override;
void close();
int bit_depth() const override;
int sample_rate() const override;
size_t n_frames() const override;
int n_channels() const override;
};
#endif /* AUDIOWMARK_WAV_PIPE_INPUT_STREAM_HH */
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include <assert.h> #include <assert.h>
enum class Format { AUTO = 1, RAW = 2, RF64 = 3 }; enum class Format { AUTO = 1, RAW, RF64, WAV_PIPE };
class Params class Params
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment