Commit 650c6a9a authored by Stefan Westerfeld's avatar Stefan Westerfeld

Move stdout wave output stream to extra cc/hh file.

Signed-off-by: Stefan Westerfeld's avatarStefan Westerfeld <stefan@space.twc.de>
parent 570e2923
bin_PROGRAMS = audiowmark
COMMON_SRC = utils.hh utils.cc convcode.hh convcode.cc random.hh random.cc mp3.cc mp3.hh wavdata.cc wavdata.hh \
audiostream.cc audiostream.hh sfinputstream.cc sfinputstream.hh
audiostream.cc audiostream.hh sfinputstream.cc sfinputstream.hh stdoutwavoutputstream.cc stdoutwavoutputstream.hh
COMMON_LIBS = $(SNDFILE_LIBS) $(FFTW_LIBS) $(LIBGCRYPT_LIBS) $(LIBMPG123_LIBS)
audiowmark_SOURCES = audiowmark.cc fft.cc fft.hh $(COMMON_SRC)
......
possible improvements:
- dynamic bit strength
- mp3 support with libmad
streaming:
- need explicit Error objects
- logging
#include "audiostream.hh"
AudioStream::~AudioStream()
{
}
......@@ -6,6 +6,7 @@
class AudioStream
{
public:
virtual int bit_depth() const = 0;
virtual int sample_rate() const = 0;
virtual size_t n_frames() const = 0;
......
......@@ -12,6 +12,7 @@
#include "convcode.hh"
#include "random.hh"
#include "sfinputstream.hh"
#include "stdoutwavoutputstream.hh"
#include <zita-resampler/resampler.h>
#include <zita-resampler/vresampler.h>
......@@ -682,6 +683,15 @@ add_watermark (const string& infile, const string& outfile, const string& bits)
printf ("Sample Rate: %d\n", in_stream->sample_rate());
printf ("Channels: %d\n", in_stream->n_channels());
bool open (int n_channels, int sample_rate, int bit_depth, size_t n_frames);
const int out_bit_depth = in_stream->bit_depth() > 16 ? 24 : 16;
auto out_stream = std::make_unique <StdoutWavOutputStream>();
if (!out_stream->open (in_stream->n_channels(), in_stream->sample_rate(), out_bit_depth, in_stream->n_frames()))
{
fprintf (stderr, "audiowmark: error writing to -\n"); //%s: %s\n", outfile.c_str(), out_wav_data.error_blurb()); FIXME
return 1;
}
#if 0
vector<float> in_signal;
if (orig_wav_data.sample_rate() != Params::mark_sample_rate)
......
......@@ -34,6 +34,35 @@ SFInputStream::open (const string& filename)
m_n_channels = sfinfo.channels;
m_n_values = sfinfo.frames * sfinfo.channels;
m_sample_rate = sfinfo.samplerate;
switch (sfinfo.format & SF_FORMAT_SUBMASK)
{
case SF_FORMAT_PCM_U8:
case SF_FORMAT_PCM_S8:
m_bit_depth = 8;
break;
case SF_FORMAT_PCM_16:
m_bit_depth = 16;
break;
case SF_FORMAT_PCM_24:
m_bit_depth = 24;
break;
case SF_FORMAT_FLOAT:
case SF_FORMAT_PCM_32:
m_bit_depth = 32;
break;
case SF_FORMAT_DOUBLE:
m_bit_depth = 64;
break;
default:
m_bit_depth = 32; /* unknown */
}
m_state = State::OPEN;
return true;
}
......@@ -44,6 +73,12 @@ SFInputStream::sample_rate() const
return m_sample_rate;
}
int
SFInputStream::bit_depth() const
{
return m_bit_depth;
}
vector<float>
SFInputStream::read_frames (size_t count)
{
......
......@@ -13,6 +13,7 @@ class SFInputStream : public AudioInputStream
std::string m_error_blurb;
int m_n_channels = 0;
int m_n_values = 0;
int m_bit_depth = 0;
int m_sample_rate = 0;
enum class State {
......@@ -34,7 +35,8 @@ public:
{
return m_n_channels;
}
int sample_rate() const;
int sample_rate() const override;
int bit_depth() const override;
size_t
n_values() const
{
......
#include "stdoutwavoutputstream.hh"
#include "utils.hh"
#include <assert.h>
#include <math.h>
using std::string;
using std::vector;
StdoutWavOutputStream::~StdoutWavOutputStream()
{
close();
}
int
StdoutWavOutputStream::sample_rate() const
{
return m_sample_rate;
}
int
StdoutWavOutputStream::bit_depth() const
{
return m_bit_depth;
}
static void
header_append_str (vector<unsigned char>& bytes, const string& str)
{
for (auto ch : str)
bytes.push_back (ch);
}
static void
header_append_u32 (vector<unsigned char>& bytes, uint32_t u)
{
bytes.push_back (u);
bytes.push_back (u >> 8);
bytes.push_back (u >> 16);
bytes.push_back (u >> 24);
}
static void
header_append_u16 (vector<unsigned char>& bytes, uint16_t u)
{
bytes.push_back (u);
bytes.push_back (u >> 8);
}
bool
StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, size_t n_frames)
{
assert (m_state == State::NEW);
if (bit_depth != 16 && bit_depth != 24)
{
m_error_blurb = "StdoutWavOutputStream::open: unsupported bit depth";
return false;
}
vector<unsigned char> header_bytes;
size_t data_size = n_frames * n_channels * ((bit_depth + 7) / 8);
m_close_padding = data_size & 1; // padding to ensure even data size
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);
header_append_str (header_bytes, "WAVE");
// subchunk 1
header_append_str (header_bytes, "fmt ");
header_append_u32 (header_bytes, 16); // subchunk size
header_append_u16 (header_bytes, 1); // uncompressed audio
header_append_u16 (header_bytes, n_channels);
header_append_u32 (header_bytes, sample_rate);
header_append_u32 (header_bytes, sample_rate * n_channels * bit_depth / 8); // byte rate
header_append_u16 (header_bytes, n_channels * bit_depth / 8); // block align
header_append_u16 (header_bytes, bit_depth); // bits per sample
// subchunk 2
header_append_str (header_bytes, "data");
header_append_u32 (header_bytes, data_size);
fwrite (&header_bytes[0], 1, header_bytes.size(), stdout);
m_bit_depth = bit_depth;
m_n_frames = n_frames;
m_state = State::OPEN;
return true;
}
template<int BIT_DEPTH> void
convert_frames (const vector<float>& samples, vector<unsigned char>& output_bytes)
{
for (size_t i = 0; i < samples.size(); 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 (BIT_DEPTH == 16)
{
// write 16-bit little endian value
output_bytes[i * 2] = sample >> 16;
output_bytes[i * 2 + 1] = sample >> 24;
}
else if (BIT_DEPTH == 24)
{
// write 24-bit little endian value
output_bytes[i * 3] = sample >> 8;
output_bytes[i * 3 + 1] = sample >> 16;
output_bytes[i * 3 + 2] = sample >> 24;
}
}
}
bool
StdoutWavOutputStream::write_frames (const vector<float>& samples)
{
vector<unsigned char> output_bytes (samples.size() * (m_bit_depth / 8));
if (m_bit_depth == 16)
{
convert_frames<16> (samples, output_bytes);
}
else if (m_bit_depth == 24)
{
convert_frames<24> (samples, output_bytes);
}
else
{
assert (false);
}
fwrite (&output_bytes[0], 1, output_bytes.size(), stdout);
return true;
}
void
StdoutWavOutputStream::close()
{
if (m_state == State::OPEN)
{
for (size_t i = 0; i < m_close_padding; i++)
fputc (0, stdout);
m_state = State::CLOSED;
}
}
#ifndef AUDIOWMARK_STDOUT_WAV_STREAM_HH
#define AUDIOWMARK_STDOUT_WAV_STREAM_HH
#include "audiostream.hh"
#include <string>
class StdoutWavOutputStream : public AudioOutputStream
{
std::string m_error_blurb;
int m_bit_depth = 0;
int m_sample_rate = 0;
size_t m_n_frames = 0;
size_t m_close_padding = 0;
enum class State {
NEW,
OPEN,
CLOSED
};
State m_state = State::NEW;
public:
~StdoutWavOutputStream();
bool open (int n_channels, int sample_rate, int bit_depth, size_t n_frames);
bool write_frames (const std::vector<float>& frames);
void close();
int sample_rate() const override;
int bit_depth() const override;
size_t n_frames() const override
{
return m_n_frames;
}
const char *error_blurb() const
{
return m_error_blurb.c_str();
}
};
#endif
......@@ -5,183 +5,12 @@
#include <math.h>
#include "sfinputstream.hh"
#include "stdoutwavoutputstream.hh"
#include "utils.hh"
class StdoutWavOutputStream : public AudioOutputStream
{
std::string m_error_blurb;
int m_bit_depth = 0;
int m_sample_rate = 0;
size_t m_n_frames = 0;
size_t m_close_padding = 0;
enum class State {
NEW,
OPEN,
CLOSED
};
State m_state = State::NEW;
public:
~StdoutWavOutputStream();
bool open (int n_channels, int sample_rate, int bit_depth, size_t n_frames);
bool write_frames (const std::vector<float>& frames);
void close();
int sample_rate() const override;
size_t n_frames() const override
{
return m_n_frames;
}
const char *error_blurb() const
{
return m_error_blurb.c_str();
}
};
using std::string;
using std::vector;
StdoutWavOutputStream::~StdoutWavOutputStream()
{
close();
}
int
StdoutWavOutputStream::sample_rate() const
{
return m_sample_rate;
}
static void
header_append_str (vector<unsigned char>& bytes, const string& str)
{
for (auto ch : str)
bytes.push_back (ch);
}
static void
header_append_u32 (vector<unsigned char>& bytes, uint32_t u)
{
bytes.push_back (u);
bytes.push_back (u >> 8);
bytes.push_back (u >> 16);
bytes.push_back (u >> 24);
}
static void
header_append_u16 (vector<unsigned char>& bytes, uint16_t u)
{
bytes.push_back (u);
bytes.push_back (u >> 8);
}
bool
StdoutWavOutputStream::open (int n_channels, int sample_rate, int bit_depth, size_t n_frames)
{
assert (m_state == State::NEW);
if (bit_depth != 16 && bit_depth != 24)
{
m_error_blurb = "StdoutWavOutputStream::open: unsupported bit depth";
return false;
}
vector<unsigned char> header_bytes;
size_t data_size = n_frames * n_channels * ((bit_depth + 7) / 8);
m_close_padding = data_size & 1; // padding to ensure even data size
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);
header_append_str (header_bytes, "WAVE");
// subchunk 1
header_append_str (header_bytes, "fmt ");
header_append_u32 (header_bytes, 16); // subchunk size
header_append_u16 (header_bytes, 1); // uncompressed audio
header_append_u16 (header_bytes, n_channels);
header_append_u32 (header_bytes, sample_rate);
header_append_u32 (header_bytes, sample_rate * n_channels * bit_depth / 8); // byte rate
header_append_u16 (header_bytes, n_channels * bit_depth / 8); // block align
header_append_u16 (header_bytes, bit_depth); // bits per sample
// subchunk 2
header_append_str (header_bytes, "data");
header_append_u32 (header_bytes, data_size);
fwrite (&header_bytes[0], 1, header_bytes.size(), stdout);
m_bit_depth = bit_depth;
m_n_frames = n_frames;
m_state = State::OPEN;
return true;
}
template<int BIT_DEPTH> void
convert_frames (const vector<float>& samples, vector<unsigned char>& output_bytes)
{
for (size_t i = 0; i < samples.size(); 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 (BIT_DEPTH == 16)
{
// write 16-bit little endian value
output_bytes[i * 2] = sample >> 16;
output_bytes[i * 2 + 1] = sample >> 24;
}
else if (BIT_DEPTH == 24)
{
// write 24-bit little endian value
output_bytes[i * 3] = sample >> 8;
output_bytes[i * 3 + 1] = sample >> 16;
output_bytes[i * 3 + 2] = sample >> 24;
}
}
}
bool
StdoutWavOutputStream::write_frames (const vector<float>& samples)
{
vector<unsigned char> output_bytes (samples.size() * (m_bit_depth / 8));
if (m_bit_depth == 16)
{
convert_frames<16> (samples, output_bytes);
}
else if (m_bit_depth == 24)
{
convert_frames<24> (samples, output_bytes);
}
else
{
assert (false);
}
fwrite (&output_bytes[0], 1, output_bytes.size(), stdout);
return true;
}
void
StdoutWavOutputStream::close()
{
if (m_state == State::OPEN)
{
for (size_t i = 0; i < m_close_padding; i++)
fputc (0, stdout);
m_state = State::CLOSED;
}
}
int
main (int argc, char **argv)
{
......
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