Fix export, which has been broken since the boost::signals2 changes. Also update Audiographer, bacause of its incomplete sndfile handling. Audiographer is equal to revision 74

git-svn-id: svn://localhost/ardour2/branches/3.0@6760 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Sakari Bergen 2010-03-15 19:11:48 +00:00
parent 44f4b84551
commit 830911f6f9
72 changed files with 2182 additions and 1090 deletions

View File

@ -31,7 +31,7 @@
#include "ardour/ardour.h"
#include "audiographer/sample_format_converter.h"
#include "audiographer/general/sample_format_converter.h"
namespace ARDOUR
{

View File

@ -26,7 +26,7 @@
#include "ardour/export_channel.h"
#include "ardour/export_format_base.h"
#include "audiographer/identity_vertex.h"
#include "audiographer/utils/identity_vertex.h"
#include <glibmm/threadpool.h>

View File

@ -1,16 +1,15 @@
#include "ardour/export_graph_builder.h"
#include "audiographer/interleaver.h"
#include "audiographer/normalizer.h"
#include "audiographer/peak_reader.h"
#include "audiographer/process_context.h"
#include "audiographer/sample_format_converter.h"
#include "audiographer/sndfile_writer.h"
#include "audiographer/sr_converter.h"
#include "audiographer/silence_trimmer.h"
#include "audiographer/threader.h"
#include "audiographer/tmp_file.h"
#include "audiographer/utils.h"
#include "audiographer/general/interleaver.h"
#include "audiographer/general/normalizer.h"
#include "audiographer/general/peak_reader.h"
#include "audiographer/general/sample_format_converter.h"
#include "audiographer/general/sr_converter.h"
#include "audiographer/general/silence_trimmer.h"
#include "audiographer/general/threader.h"
#include "audiographer/sndfile/tmp_file.h"
#include "audiographer/sndfile/sndfile_writer.h"
#include "ardour/audioengine.h"
#include "ardour/export_channel_configuration.h"
@ -30,17 +29,11 @@ ExportGraphBuilder::ExportGraphBuilder (Session const & session)
{
process_buffer_frames = session.engine().frames_per_cycle();
process_buffer = new Sample[process_buffer_frames];
// TODO move and/or use global silent buffers
AudioGrapher::Utils::init_zeros<Sample> (process_buffer_frames);
}
ExportGraphBuilder::~ExportGraphBuilder ()
{
delete [] process_buffer;
// TODO see bove
AudioGrapher::Utils::free_resources();
}
int
@ -150,7 +143,7 @@ ExportGraphBuilder::Encoder::init_writer (boost::shared_ptr<AudioGrapher::Sndfil
int format = get_real_format (config);
Glib::ustring filename = config.filename->get_path (config.format);
writer.reset (new AudioGrapher::SndfileWriter<T> (channels, config.format->sample_rate(), format, filename));
writer.reset (new AudioGrapher::SndfileWriter<T> (filename, format, channels, config.format->sample_rate()));
writer->FileWritten.connect (sigc::mem_fun (*this, &ExportGraphBuilder::Encoder::copy_files));
}
@ -236,8 +229,8 @@ ExportGraphBuilder::Normalizer::init (FileSpec const & new_config, nframes_t /*m
normalizer->add_output (threader);
int format = ExportFormatBase::F_RAW | ExportFormatBase::SF_Float;
tmp_file.reset (new TmpFile<float> (config.channel_config->get_n_chans(),
config.format->sample_rate(), format));
tmp_file.reset (new TmpFile<float> (format, config.channel_config->get_n_chans(),
config.format->sample_rate()));
tmp_file->FileWritten.connect (sigc::hide (sigc::mem_fun (*this, &Normalizer::start_post_processing)));
add_child (new_config);
@ -270,17 +263,16 @@ ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const
bool
ExportGraphBuilder::Normalizer::process()
{
ProcessContext<Sample> buffer_copy (*buffer);
tmp_file->read (buffer_copy);
normalizer->process (buffer_copy);
return buffer_copy.frames() != buffer->frames();
nframes_t frames_read = tmp_file->read (*buffer);
return frames_read != buffer->frames();
}
void
ExportGraphBuilder::Normalizer::start_post_processing()
{
normalizer->set_peak (peak_reader->get_peak());
tmp_file->seek (0, SndfileReader<Sample>::SeekBeginning);
tmp_file->seek (0, SEEK_SET);
tmp_file->add_output (normalizer);
parent.normalizers.push_back (this);
}
@ -339,12 +331,11 @@ ExportGraphBuilder::SilenceHandler::init (FileSpec const & new_config, nframes_t
max_frames_in = max_frames;
nframes_t sample_rate = parent.session.nominal_frame_rate();
silence_trimmer.reset (new SilenceTrimmer<Sample>());
silence_trimmer.reset (new SilenceTrimmer<Sample>(max_frames_in));
silence_trimmer->set_trim_beginning (config.format->trim_beginning());
silence_trimmer->set_trim_end (config.format->trim_end());
silence_trimmer->add_silence_to_beginning (config.format->silence_beginning(sample_rate));
silence_trimmer->add_silence_to_end (config.format->silence_end(sample_rate));
silence_trimmer->limit_output_size (max_frames_in);
add_child (new_config);

22
libs/audiographer/README Normal file
View File

@ -0,0 +1,22 @@
AudioGrapher is Copyright Sakari Bergen 2009-2010
AudioGrapher is best described as a signal flow management library.
It includes facilities to build graphs out of signal processing elements.
Once a graph is set up, all signal flow within the graph happens automatically.
The data flow model in Audiographer is dynamic instead of synchronous - the type
and amount of data that goes in to a graph may differ from what comes out.
AudioGrapher is aimed mostly for usage by developers, as it includes lots of
facilities that ease the development process.
The main aim of AudioGrapher is to ease development and debugging of signal flow
graphs. It makes heavy use of modern C++ techniques like templates, and uses the
boost libraries a lot.
The essential classes in AudioGrapher are Sink, Source and ProcessContext. These
three define the signal flow in a graph. In addition, the core of AudioGrapher
includes lots of utility classes.
AudioGrapher includes a bunch of ready Sink, Source and Vertex implementations.
Some are utilities used when developing more vertices, while others are general
utilities (file i/o, sample rate conversion etc).

View File

@ -1,54 +0,0 @@
#ifndef AUDIOGRAPHER_CHUNKER_H
#define AUDIOGRAPHER_CHUNKER_H
#include "listed_source.h"
#include "sink.h"
#include <cstring>
namespace AudioGrapher
{
template<typename T>
class Chunker : public ListedSource<T>, public Sink<T>
{
public:
Chunker (nframes_t chunk_size)
: chunk_size (chunk_size)
, position (0)
{
buffer = new T[chunk_size];
}
~Chunker()
{
delete [] buffer;
}
void process (ProcessContext<T> const & context)
{
if (position + context.frames() < chunk_size) {
memcpy (&buffer[position], (float const *)context.data(), context.frames() * sizeof(T));
position += context.frames();
} else {
nframes_t const frames_to_copy = chunk_size - position;
memcpy (&buffer[position], context.data(), frames_to_copy * sizeof(T));
ProcessContext<T> c_out (context, buffer, chunk_size);
ListedSource<T>::output (c_out);
memcpy (buffer, &context.data()[frames_to_copy], (context.frames() - frames_to_copy) * sizeof(T));
position = context.frames() - frames_to_copy;
}
}
using Sink<T>::process;
private:
nframes_t chunk_size;
nframes_t position;
T * buffer;
};
} // namespace
#endif // AUDIOGRAPHER_CHUNKER_H

View File

@ -1,6 +1,8 @@
#ifndef AUDIOGRAPHER_DEBUG_UTILS_H
#define AUDIOGRAPHER_DEBUG_UTILS_H
#include "flag_field.h"
#include <string>
#ifdef __GNUC__
@ -10,8 +12,10 @@
namespace AudioGrapher
{
/// Utilities for debugging
struct DebugUtils
{
/// Returns the demangled name of the object passed as the parameter
template<typename T>
static std::string demangled_name (T const & obj)
{
@ -27,6 +31,8 @@ struct DebugUtils
return typeid(obj).name();
}
/// Returns name of ProcessContext::Flag
static std::string process_context_flag_name (FlagField::Flag flag);
};
} // namespace

View File

@ -10,16 +10,31 @@
namespace AudioGrapher
{
/// Compile time defined debug level
enum DebugLevel
{
DebugNone, //< Disabled
DebugObject, //< Object level stuff, ctors, initalizers etc.
DebugProcess, //< Process cycle level stuff
DebugVerbose, //< Lots of output, not on sample level
DebugSample //< Sample level stuff
DebugNone, ///< Disabled
DebugObject, ///< Object level stuff, ctors, initalizers etc.
DebugFlags, ///< Debug ProcessContext flags only on process cycle level
DebugProcess, ///< Process cycle level stuff
DebugVerbose, ///< Lots of output, not on sample level
DebugSample ///< Sample level stuff
};
/// Class that allows optimizing out debugging code during compile time
/** Class that allows optimizing out debugging code during compile time.
* Usage: to take all advantage of this class you should wrap all
* debugging statemets like this:
* \code
* if (debug_level (SomeDebugLevel) && other_optional_conditionals) {
* debug_stream() << "Debug output" << std::endl;
* }
* \endcode
*
* The order of the conditionals in the if-clause is important.
* The checks specified in \a other_optional_conditionals are only
* optimized out if \a debug_level() is placed before it with a
* logical and (short-circuiting).
*/
template<DebugLevel L = DEFAULT_DEBUG_LEVEL>
class Debuggable
{

View File

@ -1,78 +0,0 @@
template<typename T>
DeInterleaver<T>::DeInterleaver()
: channels (0)
, max_frames (0)
, buffer (0)
{}
template<typename T>
void
DeInterleaver<T>::init (unsigned int num_channels, nframes_t max_frames_per_channel)
{
reset();
channels = num_channels;
max_frames = max_frames_per_channel;
buffer = new T[max_frames];
for (unsigned int i = 0; i < channels; ++i) {
outputs.push_back (OutputPtr (new IdentityVertex<T>));
}
}
template<typename T>
typename DeInterleaver<T>::SourcePtr
DeInterleaver<T>::output (unsigned int channel)
{
if (channel >= channels) {
throw Exception (*this, "channel out of range");
}
return boost::static_pointer_cast<Source<T> > (outputs[channel]);
}
template<typename T>
void
DeInterleaver<T>::process (ProcessContext<T> const & c)
{
nframes_t frames = c.frames();
T const * data = c.data();
if (frames == 0) { return; }
nframes_t const frames_per_channel = frames / channels;
if (c.channels() != channels) {
throw Exception (*this, "wrong amount of channels given to process()");
}
if (frames % channels != 0) {
throw Exception (*this, "wrong amount of frames given to process()");
}
if (frames_per_channel > max_frames) {
throw Exception (*this, "too many frames given to process()");
}
unsigned int channel = 0;
for (typename std::vector<OutputPtr>::iterator it = outputs.begin(); it != outputs.end(); ++it, ++channel) {
if (!*it) { continue; }
for (unsigned int i = 0; i < frames_per_channel; ++i) {
buffer[i] = data[channel + (channels * i)];
}
ProcessContext<T> c_out (c, buffer, frames_per_channel, 1);
(*it)->process (c_out);
}
}
template<typename T>
void
DeInterleaver<T>::reset ()
{
outputs.clear();
delete [] buffer;
buffer = 0;
channels = 0;
max_frames = 0;
}

View File

@ -1,46 +0,0 @@
#ifndef AUDIOGRAPHER_DEINTERLEAVER_H
#define AUDIOGRAPHER_DEINTERLEAVER_H
#include "types.h"
#include "source.h"
#include "sink.h"
#include "identity_vertex.h"
#include "exception.h"
#include <vector>
namespace AudioGrapher
{
template<typename T>
class DeInterleaver : public Sink<T>
{
private:
typedef boost::shared_ptr<IdentityVertex<T> > OutputPtr;
public:
DeInterleaver();
~DeInterleaver() { reset(); }
typedef boost::shared_ptr<Source<T> > SourcePtr;
void init (unsigned int num_channels, nframes_t max_frames_per_channel);
SourcePtr output (unsigned int channel);
void process (ProcessContext<T> const & c);
using Sink<T>::process;
private:
void reset ();
std::vector<OutputPtr> outputs;
unsigned int channels;
nframes_t max_frames;
T * buffer;
};
#include "deinterleaver-inl.h"
} // namespace
#endif // AUDIOGRAPHER_DEINTERLEAVER_H

View File

@ -11,6 +11,9 @@
namespace AudioGrapher
{
/** AudioGrapher Exception class.
* Automatically tells which class an exception was thrown from.
*/
class Exception : public std::exception
{
public:

View File

@ -0,0 +1,51 @@
#ifndef AUDIOGRAPHER_FLAG_DEBUGGABLE_H
#define AUDIOGRAPHER_FLAG_DEBUGGABLE_H
#include "debuggable.h"
#include "debug_utils.h"
#include "process_context.h"
#include "types.h"
#include <boost/format.hpp>
namespace AudioGrapher
{
/// A debugging class for nodes that support a certain set of flags.
template<DebugLevel L = DEFAULT_DEBUG_LEVEL>
class FlagDebuggable : public Debuggable<L>
{
public:
typedef FlagField::Flag Flag;
protected:
/// Adds a flag to the set of flags supported
void add_supported_flag (Flag flag)
{
flags.set (flag);
}
/// Prints debug output if \a context contains flags that are not supported by this class
template<typename SelfType, typename ContextType>
void check_flags (SelfType & self, ProcessContext<ContextType> context)
{
if (!Debuggable<L>::debug_level (DebugFlags)) { return; }
FlagField unsupported = flags.unsupported_flags_of (context.flags());
for (FlagField::iterator it = unsupported.begin(); it != unsupported.end(); ++it) {
Debuggable<L>::debug_stream() << boost::str (boost::format
("%1% does not support flag %2%")
% DebugUtils::demangled_name (self) % DebugUtils::process_context_flag_name (*it)
) << std::endl;
}
}
private:
FlagField flags;
};
} // namespace
#endif // AUDIOGRAPHER_FLAG_DEBUGGABLE_H

View File

@ -0,0 +1,106 @@
#ifndef AUDIOGRAPHER_FLAG_FIELD_H
#define AUDIOGRAPHER_FLAG_FIELD_H
#include <stdint.h>
#include <iterator>
#include <climits>
#include <boost/operators.hpp>
namespace AudioGrapher {
/** Flag field capable of holding 32 flags.
* Easily grown in size to 64 flags by changing storage_type.
*/
class FlagField
: public boost::less_than_comparable<FlagField>
, boost::equivalent<FlagField>
, boost::equality_comparable<FlagField>
{
public:
typedef uint8_t Flag;
typedef uint32_t storage_type;
/// Bi-directional iterator for flag set. Iterates over flags that are set in this field.
class iterator
: public std::iterator<std::bidirectional_iterator_tag, Flag>
, public boost::less_than_comparable<iterator>
, boost::equivalent<iterator>
, boost::equality_comparable<iterator>
{
public:
iterator (FlagField const & parent, Flag position) : parent (parent), position (position) {}
iterator (iterator const & other) : parent (other.parent), position (other.position) {}
value_type operator*() const { return position; }
value_type const * operator->() const { return &position; }
iterator & operator++()
{
do {
++position;
} while (!parent.has (position) && position != max());
return *this;
}
iterator operator++(int) { iterator copy (*this); ++(*this); return copy; }
iterator & operator--()
{
do {
--position;
} while (!parent.has (position) && position != max());
return *this;
}
iterator operator--(int) { iterator copy (*this); --(*this); return copy; }
bool operator< (iterator const & other) const { return position < other.position; }
private:
FlagField const & parent;
Flag position;
};
public:
FlagField() : _flags (0) {}
FlagField(FlagField const & other) : _flags (other._flags) {}
inline bool has (Flag flag) const { return _flags & (1 << flag); }
inline storage_type flags () const { return _flags; }
inline operator bool() const { return _flags; }
inline void set (Flag flag) { _flags |= (1 << flag); }
inline void remove (Flag flag) { _flags &= ~(1 << flag); }
inline void reset () { _flags = 0; }
/// Returns the flags in \a other that are not set in this field
inline FlagField unsupported_flags_of (FlagField const & other) const { return ~(_flags | ~other._flags); }
/// Set all flags that are set in \a other
inline FlagField & operator+= (FlagField const & other) { _flags |= other._flags; return *this; }
/** Checks whether this field has all the flags set that are set in \a other
* NOTE: Can NOT be used for strict weak ordering!
* \return \a true if \a other has flags set that this field does not
*/
inline bool operator< (FlagField const & other) const { return unsupported_flags_of (other); }
iterator begin() const
{
iterator it (*this, 0);
if (!*this) { return end(); }
if (!has (0)) { ++it; }
return it;
}
iterator end() const { iterator it (*this, max()); return it; }
private:
FlagField(storage_type flags) : _flags (flags) {}
static Flag max() { return CHAR_BIT * sizeof (storage_type) + 1; }
storage_type _flags;
};
} // namespace
#endif // AUDIOGRAPHER_FLAG_FIELD_H

View File

@ -0,0 +1,85 @@
#ifndef AUDIOGRAPHER_CHUNKER_H
#define AUDIOGRAPHER_CHUNKER_H
#include "audiographer/flag_debuggable.h"
#include "audiographer/sink.h"
#include "audiographer/type_utils.h"
#include "audiographer/utils/listed_source.h"
namespace AudioGrapher
{
/// A class that chunks process cycles into equal sized frames
template<typename T = DefaultSampleType>
class Chunker
: public ListedSource<T>
, public Sink<T>
, public FlagDebuggable<>
{
public:
/** Constructs a new Chunker with a constant chunk size.
* \n NOT RT safe
*/
Chunker (nframes_t chunk_size)
: chunk_size (chunk_size)
, position (0)
{
buffer = new T[chunk_size];
add_supported_flag (ProcessContext<T>::EndOfInput);
}
~Chunker()
{
delete [] buffer;
}
/** Outputs data in \a context in chunks with the size specified in the constructor.
* Note that some calls might not produce any output, while others may produce several.
* \n RT safe
*/
void process (ProcessContext<T> const & context)
{
check_flags (*this, context);
nframes_t frames_left = context.frames();
nframes_t input_position = 0;
while (position + frames_left >= chunk_size) {
// Copy from context to buffer
nframes_t const frames_to_copy = chunk_size - position;
TypeUtils<T>::copy (&context.data()[input_position], &buffer[position], frames_to_copy);
// Output whole buffer
ProcessContext<T> c_out (context, buffer, chunk_size);
ListedSource<T>::output (c_out);
// Update counters
position = 0;
input_position += frames_to_copy;
frames_left -= frames_to_copy;
}
if (frames_left) {
// Copy the rest of the data
TypeUtils<T>::copy (&context.data()[input_position], &buffer[position], frames_left);
position += frames_left;
}
if (context.has_flag (ProcessContext<T>::EndOfInput)) {
ProcessContext<T> c_out (context, buffer, position);
ListedSource<T>::output (c_out);
}
}
using Sink<T>::process;
private:
nframes_t chunk_size;
nframes_t position;
T * buffer;
};
} // namespace
#endif // AUDIOGRAPHER_CHUNKER_H

View File

@ -0,0 +1,109 @@
#ifndef AUDIOGRAPHER_DEINTERLEAVER_H
#define AUDIOGRAPHER_DEINTERLEAVER_H
#include "audiographer/types.h"
#include "audiographer/source.h"
#include "audiographer/sink.h"
#include "audiographer/exception.h"
#include "audiographer/utils/identity_vertex.h"
#include <vector>
namespace AudioGrapher
{
/// Converts on stream of interleaved data to many streams of uninterleaved data.
template<typename T = DefaultSampleType>
class DeInterleaver
: public Sink<T>
, public Throwing<>
{
private:
typedef boost::shared_ptr<IdentityVertex<T> > OutputPtr;
public:
/// Constructor. \n RT safe
DeInterleaver()
: channels (0)
, max_frames (0)
, buffer (0)
{}
~DeInterleaver() { reset(); }
typedef boost::shared_ptr<Source<T> > SourcePtr;
/// Inits the deinterleaver. Must be called before using. \n Not RT safe
void init (unsigned int num_channels, nframes_t max_frames_per_channel)
{
reset();
channels = num_channels;
max_frames = max_frames_per_channel;
buffer = new T[max_frames];
for (unsigned int i = 0; i < channels; ++i) {
outputs.push_back (OutputPtr (new IdentityVertex<T>));
}
}
/// Returns an output indexed by \a channel \n RT safe
SourcePtr output (unsigned int channel)
{
if (throw_level (ThrowObject) && channel >= channels) {
throw Exception (*this, "channel out of range");
}
return outputs[channel];
}
/// Deinterleaves data and outputs it to the outputs. \n RT safe
void process (ProcessContext<T> const & c)
{
nframes_t frames = c.frames();
T const * data = c.data();
nframes_t const frames_per_channel = frames / channels;
if (throw_level (ThrowProcess) && c.channels() != channels) {
throw Exception (*this, "wrong amount of channels given to process()");
}
if (throw_level (ThrowProcess) && frames_per_channel > max_frames) {
throw Exception (*this, "too many frames given to process()");
}
unsigned int channel = 0;
for (typename std::vector<OutputPtr>::iterator it = outputs.begin(); it != outputs.end(); ++it, ++channel) {
if (!*it) { continue; }
for (unsigned int i = 0; i < frames_per_channel; ++i) {
buffer[i] = data[channel + (channels * i)];
}
ProcessContext<T> c_out (c, buffer, frames_per_channel, 1);
(*it)->process (c_out);
}
}
using Sink<T>::process;
private:
void reset ()
{
outputs.clear();
delete [] buffer;
buffer = 0;
channels = 0;
max_frames = 0;
}
std::vector<OutputPtr> outputs;
unsigned int channels;
nframes_t max_frames;
T * buffer;
};
} // namespace
#endif // AUDIOGRAPHER_DEINTERLEAVER_H

View File

@ -0,0 +1,152 @@
#ifndef AUDIOGRAPHER_INTERLEAVER_H
#define AUDIOGRAPHER_INTERLEAVER_H
#include "audiographer/types.h"
#include "audiographer/sink.h"
#include "audiographer/exception.h"
#include "audiographer/throwing.h"
#include "audiographer/utils/listed_source.h"
#include <vector>
#include <cmath>
namespace AudioGrapher
{
/// Interleaves many streams of non-interleaved data into one interleaved stream
template<typename T = DefaultSampleType>
class Interleaver
: public ListedSource<T>
, public Throwing<>
{
public:
/// Constructs an interleaver \n RT safe
Interleaver()
: channels (0)
, max_frames (0)
, buffer (0)
{}
~Interleaver() { reset(); }
/// Inits the interleaver. Must be called before using. \n Not RT safe
void init (unsigned int num_channels, nframes_t max_frames_per_channel)
{
reset();
channels = num_channels;
max_frames = max_frames_per_channel;
buffer = new T[channels * max_frames];
for (unsigned int i = 0; i < channels; ++i) {
inputs.push_back (InputPtr (new Input (*this, i)));
}
}
/** Returns the input indexed by \a channel \n RT safe
* \n The \a process function of returned Sinks are also RT Safe
*/
typename Source<T>::SinkPtr input (unsigned int channel)
{
if (throw_level (ThrowObject) && channel >= channels) {
throw Exception (*this, "Channel out of range");
}
return boost::static_pointer_cast<Sink<T> > (inputs[channel]);
}
private:
class Input : public Sink<T>
{
public:
Input (Interleaver & parent, unsigned int channel)
: frames_written (0), parent (parent), channel (channel) {}
void process (ProcessContext<T> const & c)
{
if (parent.throw_level (ThrowProcess) && c.channels() > 1) {
throw Exception (*this, "Data input has more than on channel");
}
if (parent.throw_level (ThrowStrict) && frames_written) {
throw Exception (*this, "Input channels out of sync");
}
frames_written = c.frames();
parent.write_channel (c, channel);
}
using Sink<T>::process;
nframes_t frames() { return frames_written; }
void reset() { frames_written = 0; }
private:
nframes_t frames_written;
Interleaver & parent;
unsigned int channel;
};
void reset ()
{
inputs.clear();
delete [] buffer;
buffer = 0;
channels = 0;
max_frames = 0;
}
void reset_channels ()
{
for (unsigned int i = 0; i < channels; ++i) {
inputs[i]->reset();
}
}
void write_channel (ProcessContext<T> const & c, unsigned int channel)
{
if (throw_level (ThrowProcess) && c.frames() > max_frames) {
reset_channels();
throw Exception (*this, "Too many frames given to an input");
}
for (unsigned int i = 0; i < c.frames(); ++i) {
buffer[channel + (channels * i)] = c.data()[i];
}
nframes_t const ready_frames = ready_to_output();
if (ready_frames) {
ProcessContext<T> c_out (c, buffer, ready_frames, channels);
ListedSource<T>::output (c_out);
reset_channels ();
}
}
nframes_t ready_to_output()
{
nframes_t ready_frames = inputs[0]->frames();
if (!ready_frames) { return 0; }
for (unsigned int i = 1; i < channels; ++i) {
nframes_t const frames = inputs[i]->frames();
if (!frames) { return 0; }
if (throw_level (ThrowProcess) && frames != ready_frames) {
init (channels, max_frames);
throw Exception (*this, "Frames count out of sync");
}
}
return ready_frames * channels;
}
typedef boost::shared_ptr<Input> InputPtr;
std::vector<InputPtr> inputs;
unsigned int channels;
nframes_t max_frames;
T * buffer;
};
} // namespace
#endif // AUDIOGRAPHER_INTERLEAVER_H

View File

@ -1,18 +1,23 @@
#ifndef AUDIOGRAPHER_NORMALIZER_H
#define AUDIOGRAPHER_NORMALIZER_H
#include "listed_source.h"
#include "sink.h"
#include "routines.h"
#include "audiographer/sink.h"
#include "audiographer/routines.h"
#include "audiographer/utils/listed_source.h"
#include <cstring>
namespace AudioGrapher
{
class Normalizer : public ListedSource<float>, Sink<float>
/// A class for normalizing to a specified target in dB
class Normalizer
: public ListedSource<float>
, public Sink<float>
, public Throwing<>
{
public:
/// Constructs a normalizer with a specific target in dB \n RT safe
Normalizer (float target_dB)
: enabled (false)
, buffer (0)
@ -26,6 +31,7 @@ class Normalizer : public ListedSource<float>, Sink<float>
delete [] buffer;
}
/// Sets the peak found in the material to be normalized \see PeakReader \n RT safe
void set_peak (float peak)
{
if (peak == 0.0f || peak == target) {
@ -37,6 +43,11 @@ class Normalizer : public ListedSource<float>, Sink<float>
}
}
/** Allocates a buffer for using with const ProcessContexts
* This function does not need to be called if
* non-const ProcessContexts are given to \a process() .
* \n Not RT safe
*/
void alloc_buffer(nframes_t frames)
{
delete [] buffer;
@ -44,9 +55,10 @@ class Normalizer : public ListedSource<float>, Sink<float>
buffer_size = frames;
}
/// Process a const ProcessContext \see alloc_buffer() \n RT safe
void process (ProcessContext<float> const & c)
{
if (c.frames() > buffer_size) {
if (throw_level (ThrowProcess) && c.frames() > buffer_size) {
throw Exception (*this, "Too many frames given to process()");
}
@ -59,6 +71,7 @@ class Normalizer : public ListedSource<float>, Sink<float>
ListedSource<float>::output (c_out);
}
/// Process a non-const ProcsesContext in-place \n RT safe
void process (ProcessContext<float> & c)
{
if (enabled) {

View File

@ -1,32 +1,34 @@
#ifndef AUDIOGRAPHER_PEAK_READER_H
#define AUDIOGRAPHER_PEAK_READER_H
#include "listed_source.h"
#include "sink.h"
#include "routines.h"
#include "audiographer/sink.h"
#include "audiographer/routines.h"
#include "audiographer/utils/listed_source.h"
namespace AudioGrapher
{
/// A class that reads the maximum value from a stream
class PeakReader : public ListedSource<float>, public Sink<float>
{
public:
/// Constructor \n RT safe
PeakReader() : peak (0.0) {}
/// Returns the highest absolute of the values found so far. \n RT safe
float get_peak() { return peak; }
/// Resets the peak to 0 \n RT safe
void reset() { peak = 0.0; }
/// Finds peaks from the data \n RT safe
void process (ProcessContext<float> const & c)
{
peak = Routines::compute_peak (c.data(), c.frames(), peak);
ListedSource<float>::output(c);
}
void process (ProcessContext<float> & c)
{
peak = Routines::compute_peak (c.data(), c.frames(), peak);
ListedSource<float>::output(c);
}
using Sink<float>::process;
private:
float peak;

View File

@ -1,9 +1,9 @@
#ifndef AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
#define AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
#include "listed_source.h"
#include "sink.h"
#include "gdither/gdither_types.h"
#include "audiographer/sink.h"
#include "audiographer/utils/listed_source.h"
#include "private/gdither/gdither_types.h"
namespace AudioGrapher
{
@ -21,13 +21,16 @@ enum DitherType
* This class can only convert floats to either \a float, \a int32_t, \a int16_t, or \a uint8_t
*/
template <typename TOut>
class SampleFormatConverter : public Sink<float>, public ListedSource<TOut>
class SampleFormatConverter
: public Sink<float>
, public ListedSource<TOut>
, public Throwing<>
{
public:
/** Constructor
* \param channels number of channels in stream
*/
SampleFormatConverter (uint32_t channels);
SampleFormatConverter (ChannelCount channels);
~SampleFormatConverter ();
/** Initialize and allocate buffers for processing.
@ -51,9 +54,9 @@ class SampleFormatConverter : public Sink<float>, public ListedSource<TOut>
private:
void reset();
void init_common(nframes_t max_frames); // not-template-specialized part of init
void check_frame_count(nframes_t frames);
void check_frame_and_channel_count(nframes_t frames, ChannelCount channels_);
uint32_t channels;
ChannelCount channels;
GDither dither;
nframes_t data_out_size;
TOut * data_out;

View File

@ -1,27 +1,59 @@
#ifndef AUDIOGRAPHER_SILENCE_TRIMMER_H
#define AUDIOGRAPHER_SILENCE_TRIMMER_H
#include "listed_source.h"
#include "sink.h"
#include "exception.h"
#include "utils.h"
#include "audiographer/debug_utils.h"
#include "audiographer/flag_debuggable.h"
#include "audiographer/sink.h"
#include "audiographer/exception.h"
#include "audiographer/utils/listed_source.h"
#include <cstring>
namespace AudioGrapher {
template<typename T>
class SilenceTrimmer : public ListedSource<T>, public Sink<T>
/// Removes and adds silent frames to beginning and/or end of stream
template<typename T = DefaultSampleType>
class SilenceTrimmer
: public ListedSource<T>
, public Sink<T>
, public FlagDebuggable<>
, public Throwing<>
{
public:
SilenceTrimmer()
/// Constructor, \see reset() \n Not RT safe
SilenceTrimmer(nframes_t silence_buffer_size_ = 1024)
: silence_buffer_size (0)
, silence_buffer (0)
{
reset ();
reset (silence_buffer_size_);
add_supported_flag (ProcessContext<T>::EndOfInput);
}
void reset()
~SilenceTrimmer()
{
delete [] silence_buffer;
}
/** Reset state \n Not RT safe
* Allocates a buffer the size of \a silence_buffer_size_
* This also defines the maximum length of output process context
* which can be output during long intermediate silence.
*/
void reset (nframes_t silence_buffer_size_ = 1024)
{
if (throw_level (ThrowObject) && silence_buffer_size_ == 0) {
throw Exception (*this,
"Silence trimmer constructor and reset() must be called with a non-zero parameter!");
}
if (silence_buffer_size != silence_buffer_size_) {
silence_buffer_size = silence_buffer_size_;
delete [] silence_buffer;
silence_buffer = new T[silence_buffer_size];
TypeUtils<T>::zero_fill (silence_buffer, silence_buffer_size);
}
in_beginning = true;
in_end = false;
trim_beginning = false;
@ -32,46 +64,71 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
add_to_end = 0;
}
/** Tells that \a frames_per_channel frames of silence per channel should be added to beginning
* Needs to be called before starting processing.
* \n RT safe
*/
void add_silence_to_beginning (nframes_t frames_per_channel)
{
if (!in_beginning) {
if (throw_level (ThrowObject) && !in_beginning) {
throw Exception(*this, "Tried to add silence to beginning after already outputting data");
}
add_to_beginning = frames_per_channel;
}
/** Tells that \a frames_per_channel frames of silence per channel should be added to end
* Needs to be called before end is reached.
* \n RT safe
*/
void add_silence_to_end (nframes_t frames_per_channel)
{
if (in_end) {
if (throw_level (ThrowObject) && in_end) {
throw Exception(*this, "Tried to add silence to end after already reaching end");
}
add_to_end = frames_per_channel;
}
/** Tells whether ot nor silence should be trimmed from the beginning
* Has to be called before starting processing.
* \n RT safe
*/
void set_trim_beginning (bool yn)
{
if (!in_beginning) {
if (throw_level (ThrowObject) && !in_beginning) {
throw Exception(*this, "Tried to set beginning trim after already outputting data");
}
trim_beginning = yn;
}
/** Tells whether ot nor silence should be trimmed from the end
* Has to be called before the is reached.
* \n RT safe
*/
void set_trim_end (bool yn)
{
if (in_end) {
if (throw_level (ThrowObject) && in_end) {
throw Exception(*this, "Tried to set end trim after already reaching end");
}
trim_end = yn;
}
void limit_output_size (nframes_t max_frames)
{
max_output_frames = max_frames;
}
/** Process stream according to current settings.
* Note that some calls will not produce any output,
* while others may produce many. \see reset()
* \n RT safe
*/
void process (ProcessContext<T> const & c)
{
if (in_end) { throw Exception(*this, "process() after reacing end of input"); }
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
"::process()" << std::endl;
}
check_flags (*this, c);
if (throw_level (ThrowStrict) && in_end) {
throw Exception(*this, "process() after reacing end of input");
}
in_end = c.has_flag (ProcessContext<T>::EndOfInput);
nframes_t frame_index = 0;
@ -88,6 +145,12 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
// Added silence if there is silence to add
if (add_to_beginning) {
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
" adding to beginning" << std::endl;
}
ConstProcessContext<T> c_copy (c);
if (has_data) { // There will be more output, so remove flag
c_copy().remove_flag (ProcessContext<T>::EndOfInput);
@ -100,6 +163,12 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
// Then has_data = true and frame_index = 0
// Otherwise these reflect the silence state
if (has_data) {
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
" outputting whole frame to beginning" << std::endl;
}
in_beginning = false;
ConstProcessContext<T> c_out (c, &c.data()[frame_index], c.frames() - frame_index);
ListedSource<T>::output (c_out);
@ -108,20 +177,43 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
} else if (trim_end) { // Only check zero samples if trimming end
if (find_first_non_zero_sample (c, frame_index)) {
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
" flushing intermediate silence and outputting frame" << std::endl;
}
// context contains non-zero data
output_silence_frames (c, silence_frames); // flush intermediate silence
ListedSource<T>::output (c); // output rest of data
} else { // whole context is zero
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
" no, output, adding frames to silence count" << std::endl;
}
silence_frames += c.frames();
}
} else { // no need to do anything special
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
" outputting whole frame in middle" << std::endl;
}
ListedSource<T>::output (c);
}
// Finally if in end, add silence to end
// Finally, if in end, add silence to end
if (in_end && add_to_end) {
if (debug_level (DebugVerbose)) {
debug_stream () << DebugUtils::demangled_name (*this) <<
" adding to end" << std::endl;
}
add_to_end *= c.channels();
output_silence_frames (c, add_to_end, true);
}
@ -146,9 +238,6 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
void output_silence_frames (ProcessContext<T> const & c, nframes_t & total_frames, bool adding_to_end = false)
{
nframes_t silence_buffer_size = Utils::get_zero_buffer_size<T>();
if (silence_buffer_size == 0) { throw Exception (*this, "Utils::init_zeros has not been called!"); }
bool end_of_input = c.has_flag (ProcessContext<T>::EndOfInput);
c.remove_flag (ProcessContext<T>::EndOfInput);
@ -160,7 +249,7 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
frames -= frames % c.channels();
total_frames -= frames;
ConstProcessContext<T> c_out (c, Utils::get_zeros<T>(frames), frames);
ConstProcessContext<T> c_out (c, silence_buffer, frames);
// boolean commentation :)
bool const no_more_silence_will_be_added = adding_to_end || (add_to_end == 0);
@ -184,6 +273,9 @@ class SilenceTrimmer : public ListedSource<T>, public Sink<T>
nframes_t add_to_beginning;
nframes_t add_to_end;
nframes_t silence_buffer_size;
T * silence_buffer;
};
} // namespace

View File

@ -3,32 +3,39 @@
#include <samplerate.h>
#include "debuggable.h"
#include "listed_source.h"
#include "sink.h"
#include "throwing.h"
#include "types.h"
#include "audiographer/flag_debuggable.h"
#include "audiographer/sink.h"
#include "audiographer/throwing.h"
#include "audiographer/types.h"
#include "audiographer/utils/listed_source.h"
namespace AudioGrapher
{
/// Samplerate converter
class SampleRateConverter
: public ListedSource<float>
, public Sink<float>
, public Debuggable<>
, public FlagDebuggable<>
, public Throwing<>
{
public:
/// Constructor. \n RT safe
SampleRateConverter (uint32_t channels);
~SampleRateConverter ();
// not RT safe
/// Init converter \n Not RT safe
void init (nframes_t in_rate, nframes_t out_rate, int quality = 0);
// returns max amount of frames that will be output
/// Returns max amount of frames that will be output \n RT safe
nframes_t allocate_buffers (nframes_t max_frames);
// could be RT safe (check libsamplerate to be sure)
/** Does sample rate conversion.
* Note that outpt size may vary a lot.
* May or may not output several contexts of data.
* \n Should be RT safe.
* \TODO Check RT safety from libsamplerate
*/
void process (ProcessContext<float> const & c);
using Sink<float>::process;

View File

@ -9,13 +9,14 @@
#include <vector>
#include <algorithm>
#include "source.h"
#include "sink.h"
#include "exception.h"
#include "audiographer/source.h"
#include "audiographer/sink.h"
#include "audiographer/exception.h"
namespace AudioGrapher
{
/// Class that stores exceptions thrown from different threads
class ThreaderException : public Exception
{
public:
@ -28,7 +29,8 @@ class ThreaderException : public Exception
{ }
};
template <typename T>
/// Class for distributing processing across several threads
template <typename T = DefaultSampleType>
class Threader : public Source<T>, public Sink<T>
{
private:
@ -36,6 +38,11 @@ class Threader : public Source<T>, public Sink<T>
public:
/** Constructor
* \n RT safe
* \param thread_pool a thread pool from which all tasks are scheduled
* \param wait_timeout_milliseconds maximum time allowed for threads to use in processing
*/
Threader (Glib::ThreadPool & thread_pool, long wait_timeout_milliseconds = 1000)
: thread_pool (thread_pool)
, readers (0)
@ -44,14 +51,19 @@ class Threader : public Source<T>, public Sink<T>
virtual ~Threader () {}
/// Adds output \n RT safe
void add_output (typename Source<T>::SinkPtr output) { outputs.push_back (output); }
/// Clears outputs \n RT safe
void clear_outputs () { outputs.clear (); }
/// Removes a specific output \n RT safe
void remove_output (typename Source<T>::SinkPtr output) {
typename OutputVec::iterator new_end = std::remove(outputs.begin(), outputs.end(), output);
outputs.erase (new_end, outputs.end());
}
/* The context has to be const, because this is working concurrently */
/// Processes context concurrently by scheduling each output separately to the given thread pool
void process (ProcessContext<T> const & c)
{
wait_mutex.lock();

View File

@ -1,92 +0,0 @@
template<typename T>
Interleaver<T>::Interleaver()
: channels (0)
, max_frames (0)
, buffer (0)
{}
template<typename T>
void
Interleaver<T>::init (unsigned int num_channels, nframes_t max_frames_per_channel)
{
reset();
channels = num_channels;
max_frames = max_frames_per_channel;
buffer = new T[channels * max_frames];
for (unsigned int i = 0; i < channels; ++i) {
inputs.push_back (InputPtr (new Input (*this, i)));
}
}
template<typename T>
typename Source<T>::SinkPtr
Interleaver<T>::input (unsigned int channel)
{
if (channel >= channels) {
throw Exception (*this, "Channel out of range");
}
return boost::static_pointer_cast<Sink<T> > (inputs[channel]);
}
template<typename T>
void
Interleaver<T>::reset_channels ()
{
for (unsigned int i = 0; i < channels; ++i) {
inputs[i]->reset();
}
}
template<typename T>
void
Interleaver<T>::reset ()
{
inputs.clear();
delete [] buffer;
buffer = 0;
channels = 0;
max_frames = 0;
}
template<typename T>
void
Interleaver<T>::write_channel (ProcessContext<T> const & c, unsigned int channel)
{
if (c.frames() > max_frames) {
reset_channels();
throw Exception (*this, "Too many frames given to an input");
}
for (unsigned int i = 0; i < c.frames(); ++i) {
buffer[channel + (channels * i)] = c.data()[i];
}
nframes_t const ready_frames = ready_to_output();
if (ready_frames) {
ProcessContext<T> c_out (c, buffer, ready_frames, channels);
ListedSource<T>::output (c_out);
reset_channels ();
}
}
template<typename T>
nframes_t
Interleaver<T>::ready_to_output ()
{
nframes_t ready_frames = inputs[0]->frames();
if (!ready_frames) { return 0; }
for (unsigned int i = 1; i < channels; ++i) {
nframes_t const frames = inputs[i]->frames();
if (!frames) { return 0; }
if (frames != ready_frames) {
init (channels, max_frames);
throw Exception (*this, "Frames count out of sync");
}
}
return ready_frames * channels;
}

View File

@ -1,71 +0,0 @@
#ifndef AUDIOGRAPHER_INTERLEAVER_H
#define AUDIOGRAPHER_INTERLEAVER_H
#include "types.h"
#include "listed_source.h"
#include "sink.h"
#include "exception.h"
#include <vector>
#include <cmath>
namespace AudioGrapher
{
template<typename T>
class Interleaver : public ListedSource<T>
{
public:
Interleaver();
~Interleaver() { reset(); }
void init (unsigned int num_channels, nframes_t max_frames_per_channel);
typename Source<T>::SinkPtr input (unsigned int channel);
private:
class Input : public Sink<T>
{
public:
Input (Interleaver & parent, unsigned int channel)
: frames_written (0), parent (parent), channel (channel) {}
void process (ProcessContext<T> const & c)
{
if (c.channels() > 1) { throw Exception (*this, "Data input has more than on channel"); }
if (frames_written) { throw Exception (*this, "Input channels out of sync"); }
frames_written = c.frames();
parent.write_channel (c, channel);
}
using Sink<T>::process;
nframes_t frames() { return frames_written; }
void reset() { frames_written = 0; }
private:
nframes_t frames_written;
Interleaver & parent;
unsigned int channel;
};
void reset ();
void reset_channels ();
void write_channel (ProcessContext<T> const & c, unsigned int channel);
nframes_t ready_to_output();
void output();
typedef boost::shared_ptr<Input> InputPtr;
std::vector<InputPtr> inputs;
unsigned int channels;
nframes_t max_frames;
T * buffer;
};
#include "interleaver-inl.h"
} // namespace
#endif // AUDIOGRAPHER_INTERLEAVER_H

View File

@ -3,8 +3,13 @@
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
#include <boost/format.hpp>
#include "exception.h"
#include "debug_utils.h"
#include "types.h"
#include "flag_field.h"
#include "throwing.h"
#include "type_utils.h"
namespace AudioGrapher
@ -14,8 +19,10 @@ namespace AudioGrapher
* Processing context. Constness only applies to data, not flags
*/
template <typename T>
class ProcessContext {
template <typename T = DefaultSampleType>
class ProcessContext
: public Throwing<>
{
BOOST_STATIC_ASSERT (boost::has_trivial_destructor<T>::value);
@ -31,26 +38,44 @@ public:
/// Basic constructor with data, frame and channel count
ProcessContext (T * data, nframes_t frames, ChannelCount channels)
: _data (data), _frames (frames), _channels (channels) {}
: _data (data), _frames (frames), _channels (channels)
{ validate_data(); }
/// Normal copy constructor
ProcessContext (ProcessContext<T> const & other)
: _data (other._data), _frames (other._frames), _channels (other._channels), _flags (other._flags) {}
: _data (other._data), _frames (other._frames), _channels (other._channels), _flags (other._flags)
{ /* No need to validate data */ }
/// "Copy constructor" with unique data, frame and channel count, but copies flags
template<typename Y>
ProcessContext (ProcessContext<Y> const & other, T * data, nframes_t frames, ChannelCount channels)
: _data (data), _frames (frames), _channels (channels), _flags (other.flags()) {}
: _data (data), _frames (frames), _channels (channels), _flags (other.flags())
{ validate_data(); }
/// "Copy constructor" with unique data and frame count, but copies channel count and flags
template<typename Y>
ProcessContext (ProcessContext<Y> const & other, T * data, nframes_t frames)
: _data (data), _frames (frames), _channels (other.channels()), _flags (other.flags()) {}
: _data (data), _frames (frames), _channels (other.channels()), _flags (other.flags())
{ validate_data(); }
/// "Copy constructor" with unique data, but copies frame and channel count + flags
template<typename Y>
ProcessContext (ProcessContext<Y> const & other, T * data)
: _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags()) {}
: _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags())
{ /* No need to validate data */ }
/// Make new Context out of the beginning of this context
ProcessContext beginning (nframes_t frames)
{
if (throw_level (ThrowProcess) && frames > _frames) {
throw Exception (*this, boost::str (boost::format
("Trying to use too many frames of %1% for a new Context: %2% instead of %3%")
% DebugUtils::demangled_name (*this) % frames % _frames));
}
validate_data ();
return ProcessContext (*this, _data, frames);
}
virtual ~ProcessContext () {}
@ -60,13 +85,11 @@ public:
/// \a frames tells how many frames the array pointed by data contains
inline nframes_t const & frames() const { return _frames; }
inline nframes_t & frames() { return _frames; }
/** \a channels tells how many interleaved channels \a data contains
* If \a channels is greater than 1, each channel contains \a frames / \a channels frames of data
*/
inline ChannelCount const & channels() const { return _channels; }
inline ChannelCount & channels() { return _channels; }
/// Returns the amount of frames per channel
inline nframes_t frames_per_channel() const { return _frames / _channels; }
@ -84,20 +107,35 @@ protected:
ChannelCount _channels;
mutable FlagField _flags;
private:
inline void validate_data()
{
if (throw_level (ThrowProcess) && (_frames % _channels != 0)) {
throw Exception (*this, boost::str (boost::format
("Number of frames given to %1% was not a multiple of channels: %2% frames with %3% channels")
% DebugUtils::demangled_name (*this) % _frames % _channels));
}
}
};
/// A process context that allocates and owns it's data buffer
template <typename T>
template <typename T = DefaultSampleType>
struct AllocatingProcessContext : public ProcessContext<T>
{
/// Allocates uninitialized memory
AllocatingProcessContext (nframes_t frames, ChannelCount channels)
: ProcessContext<T> (new T[frames], frames, channels) {}
/// Allocates and copies data from raw buffer
AllocatingProcessContext (T const * data, nframes_t frames, ChannelCount channels)
: ProcessContext<T> (new T[frames], frames, channels)
{ TypeUtils<float>::copy (data, ProcessContext<T>::_data, frames); }
/// Copy constructor, copies data from other ProcessContext
AllocatingProcessContext (ProcessContext<T> const & other)
: ProcessContext<T> (other, new T[other._frames])
{ memcpy (ProcessContext<T>::_data, other._data, other._channels * other._frames * sizeof (T)); }
{ TypeUtils<float>::copy (ProcessContext<T>::_data, other._data, other._frames); }
/// "Copy constructor" with uninitialized data, unique frame and channel count, but copies flags
template<typename Y>
@ -118,7 +156,7 @@ struct AllocatingProcessContext : public ProcessContext<T>
};
/// A wrapper for a const ProcesContext which can be created from const data
template <typename T>
template <typename T = DefaultSampleType>
class ConstProcessContext
{
public:

View File

@ -5,43 +5,57 @@
#include <cmath>
#define routines_nframes_t uint32_t
namespace AudioGrapher
{
/// Allows overriding some routines with more efficient ones.
class Routines
{
public:
typedef float (*compute_peak_t) (float const *, routines_nframes_t, float);
typedef void (*apply_gain_to_buffer_t) (float *, routines_nframes_t, float);
typedef uint32_t uint_type;
typedef float (*compute_peak_t) (float const *, uint_type, float);
typedef void (*apply_gain_to_buffer_t) (float *, uint_type, float);
static void override_compute_peak (compute_peak_t func) { _compute_peak = func; }
static void override_apply_gain_to_buffer (apply_gain_to_buffer_t func) { _apply_gain_to_buffer = func; }
static inline float compute_peak (float const * data, routines_nframes_t frames, float current_peak)
/** Computes peak in float buffer
* \n RT safe
* \param data buffer from which the peak is computed
* \param frames length of the portion of \a buffer that is checked
* \param current_peak current peak of buffer, if calculated in several passes
* \return maximum of values in [\a data, \a data + \a frames) and \a current_peak
*/
static inline float compute_peak (float const * data, uint_type frames, float current_peak)
{
return (*_compute_peak) (data, frames, current_peak);
}
static inline void apply_gain_to_buffer (float * data, routines_nframes_t frames, float gain)
/** Applies constant gain to buffer
* \n RT safe
* \param data data to which the gain is applied
* \param frames length of data
* \param gain gain that is applied
*/
static inline void apply_gain_to_buffer (float * data, uint_type frames, float gain)
{
(*_apply_gain_to_buffer) (data, frames, gain);
}
private:
static inline float default_compute_peak (float const * data, routines_nframes_t frames, float current_peak)
static inline float default_compute_peak (float const * data, uint_type frames, float current_peak)
{
for (routines_nframes_t i = 0; i < frames; ++i) {
for (uint_type i = 0; i < frames; ++i) {
float abs = std::fabs(data[i]);
if (abs > current_peak) { current_peak = abs; }
}
return current_peak;
}
static inline void default_apply_gain_to_buffer (float * data, routines_nframes_t frames, float gain)
static inline void default_apply_gain_to_buffer (float * data, uint_type frames, float gain)
{
for (routines_nframes_t i = 0; i < frames; ++i) {
for (uint_type i = 0; i < frames; ++i) {
data[i] *= gain;
}
}
@ -52,6 +66,4 @@ class Routines
} // namespace
#undef routines_nframes_t
#endif // AUDIOGRAPHER_ROUTINES_H

View File

@ -8,6 +8,9 @@
namespace AudioGrapher
{
/** A sink for data
* This is a pure virtual interface for all data sinks in AudioGrapher
*/
template <typename T>
class Sink {
public:

View File

@ -0,0 +1,30 @@
#ifndef AUDIOGRAPHER_SNDFILE_H
#define AUDIOGRAPHER_SNDFILE_H
#include "sndfile_writer.h"
#include "sndfile_reader.h"
namespace AudioGrapher
{
/** Reader/Writer for audio files using libsndfile.
* Only short, int and float are valid template parameters
*/
template<typename T = DefaultSampleType>
class Sndfile : public SndfileWriter<T>, public SndfileReader<T>
{
public:
Sndfile (std::string const & filename, SndfileBase::Mode mode = SndfileBase::ReadWrite, int format = 0,
ChannelCount channels = 0, nframes_t samplerate = 0)
: SndfileHandle (filename, mode, format, channels, samplerate)
{}
Sndfile (Sndfile const & other) : SndfileHandle (other) {}
using SndfileHandle::operator=;
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_H

View File

@ -0,0 +1,28 @@
#ifndef AUDIOGRAPHER_SNDFILE_BASE_H
#define AUDIOGRAPHER_SNDFILE_BASE_H
// We need to use our modified version until
// the fd patch is accepted upstream
#include "private/sndfile.hh"
namespace AudioGrapher
{
/// Base class for all classes using libsndfile
class SndfileBase : public virtual AudioGrapher::SndfileHandle
{
public:
enum Mode
{
Read = SFM_READ,
Write = SFM_WRITE,
ReadWrite = SFM_RDWR
};
protected:
SndfileBase () {}
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_BASE_H

View File

@ -0,0 +1,57 @@
#ifndef AUDIOGRAPHER_SNDFILE_READER_H
#define AUDIOGRAPHER_SNDFILE_READER_H
#include "audiographer/utils/listed_source.h"
#include "audiographer/process_context.h"
#include "audiographer/sndfile/sndfile_base.h"
namespace AudioGrapher
{
/** Reader for audio files using libsndfile.
* Only short, int and float are valid template parameters
*/
template<typename T = DefaultSampleType>
class SndfileReader
: public virtual SndfileBase
, public ListedSource<T>
, public Throwing<>
{
public:
SndfileReader (std::string const & path) : SndfileHandle (path) {}
virtual ~SndfileReader () {}
SndfileReader (SndfileReader const & other) : SndfileHandle (other) {}
using SndfileHandle::operator=;
/** Read data into buffer in \a context, only the data is modified (not frame count)
* Note that the data read is output to the outputs, as well as read into the context
* \return number of frames read
*/
nframes_t read (ProcessContext<T> & context)
{
if (throw_level (ThrowStrict) && context.channels() != channels() ) {
throw Exception (*this, boost::str (boost::format
("Wrong number of channels given to process(), %1% instead of %2%")
% context.channels() % channels()));
}
nframes_t frames_read = SndfileHandle::read (context.data(), context.frames());
ProcessContext<T> c_out = context.beginning (frames_read);
if (frames_read < context.frames()) {
c_out.set_flag (ProcessContext<T>::EndOfInput);
}
output (c_out);
return frames_read;
}
protected:
/// SndfileHandle has to be constructed directly by deriving classes
SndfileReader () {}
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_READER_H

View File

@ -0,0 +1,80 @@
#ifndef AUDIOGRAPHER_SNDFILE_WRITER_H
#define AUDIOGRAPHER_SNDFILE_WRITER_H
#include <boost/signals2.hpp>
#include <boost/format.hpp>
#include <string>
#include "audiographer/flag_debuggable.h"
#include "audiographer/sink.h"
#include "audiographer/types.h"
#include "audiographer/sndfile/sndfile_base.h"
namespace AudioGrapher
{
/** Writer for audio files using libsndfile.
* Only short, int and float are valid template parameters
*/
template <typename T = DefaultSampleType>
class SndfileWriter
: public virtual SndfileBase
, public Sink<T>
, public Throwing<>
, public FlagDebuggable<>
{
public:
SndfileWriter (std::string const & path, int format, ChannelCount channels, nframes_t samplerate)
: SndfileHandle (path, Write, format, channels, samplerate)
, path (path)
{
add_supported_flag (ProcessContext<T>::EndOfInput);
}
virtual ~SndfileWriter () {}
SndfileWriter (SndfileWriter const & other) : SndfileHandle (other) {}
using SndfileHandle::operator=;
/// Writes data to file
void process (ProcessContext<T> const & c)
{
check_flags (*this, c);
if (throw_level (ThrowStrict) && c.channels() != channels()) {
throw Exception (*this, boost::str (boost::format
("Wrong number of channels given to process(), %1% instead of %2%")
% c.channels() % channels()));
}
nframes_t written = write (c.data(), c.frames());
if (throw_level (ThrowProcess) && written != c.frames()) {
throw Exception (*this, boost::str (boost::format
("Could not write data to output file (%1%)")
% strError()));
}
if (c.has_flag(ProcessContext<T>::EndOfInput)) {
writeSync();
FileWritten (path);
}
}
using Sink<T>::process;
boost::signals2::signal<void (std::string)> FileWritten;
protected:
/// SndfileHandle has to be constructed directly by deriving classes
SndfileWriter ()
{
add_supported_flag (ProcessContext<T>::EndOfInput);
}
protected:
std::string path;
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_WRITER_H

View File

@ -0,0 +1,27 @@
#ifndef AUDIOGRAPHER_TMP_FILE_H
#define AUDIOGRAPHER_TMP_FILE_H
#include "sndfile_writer.h"
#include "sndfile_reader.h"
namespace AudioGrapher
{
/// A temporary file deleted after this class is destructed
template<typename T = DefaultSampleType>
class TmpFile : public SndfileWriter<T>, public SndfileReader<T>
{
public:
TmpFile (int format, ChannelCount channels, nframes_t samplerate)
: SndfileHandle (fileno (tmpfile()), true, SndfileBase::ReadWrite, format, channels, samplerate)
{}
TmpFile (TmpFile const & other) : SndfileHandle (other) {}
using SndfileHandle::operator=;
};
} // namespace
#endif // AUDIOGRAPHER_TMP_FILE_H

View File

@ -1,31 +0,0 @@
#ifndef AUDIOGRAPHER_SNDFILE_BASE_H
#define AUDIOGRAPHER_SNDFILE_BASE_H
#include <string>
#include <sndfile.h>
#include <sigc++/signal.h>
#include "types.h"
#include "debuggable.h"
namespace AudioGrapher {
/// Common interface for templated libsndfile readers/writers
class SndfileBase : public Debuggable<>
{
public:
sigc::signal<void, std::string> FileWritten;
protected:
SndfileBase (ChannelCount channels, nframes_t samplerate, int format, std::string const & path);
virtual ~SndfileBase ();
std::string path;
SF_INFO sf_info;
SNDFILE * sndfile;
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_BASE_H

View File

@ -1,40 +0,0 @@
#ifndef AUDIOGRAPHER_SNDFILE_READER_H
#define AUDIOGRAPHER_SNDFILE_READER_H
#include "sndfile_base.h"
#include "listed_source.h"
#include "process_context.h"
namespace AudioGrapher
{
/** Reader for audio files using libsndfile.
* Once again only short, int and float are valid template parameters
*/
template<typename T>
class SndfileReader : public virtual SndfileBase, public ListedSource<T>
{
public:
enum SeekType {
SeekBeginning = SEEK_SET, //< Seek from beginning of file
SeekCurrent = SEEK_CUR, //< Seek from current position
SeekEnd = SEEK_END //< Seek from end
};
public:
SndfileReader (ChannelCount channels, nframes_t samplerate, int format, std::string path);
nframes_t seek (nframes_t frames, SeekType whence);
nframes_t read (ProcessContext<T> & context);
private:
void init(); // init read function
sf_count_t (*read_func)(SNDFILE *, T *, sf_count_t);
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_READER_H

View File

@ -1,29 +0,0 @@
#ifndef AUDIOGRAPHER_SNDFILE_WRITER_H
#define AUDIOGRAPHER_SNDFILE_WRITER_H
#include "sndfile_base.h"
#include "types.h"
#include "sink.h"
namespace AudioGrapher
{
/// Template parameter specific parts of sndfile writer
template <typename T>
class SndfileWriter : public virtual SndfileBase, public Sink<T>
{
public:
SndfileWriter (ChannelCount channels, nframes_t samplerate, int format, std::string const & path);
void process (ProcessContext<T> const & c);
using Sink<T>::process;
private:
void init (); // Inits write function
sf_count_t (*write_func)(SNDFILE *, const T *, sf_count_t);
};
} // namespace
#endif // AUDIOGRAPHER_SNDFILE_WRITER_H

View File

@ -9,6 +9,9 @@
namespace AudioGrapher
{
/** A source for data
* This is a pure virtual interface for all data sources in AudioGrapher
*/
template<typename T>
class Source
{
@ -17,8 +20,13 @@ class Source
typedef boost::shared_ptr<Sink<T> > SinkPtr;
/// Adds an output to this source. All data generated is forwarded to \a output
virtual void add_output (SinkPtr output) = 0;
/// Removes all outputs added
virtual void clear_outputs () = 0;
/// Removes a specific output from this source
virtual void remove_output (SinkPtr output) = 0;
};

View File

@ -8,16 +8,35 @@
namespace AudioGrapher
{
/** Compile time defined throw level.
* Throw levels less than ThrowStrict should be used with caution.
* Not throwing could mean getting a segfault.
* However, if you want ultra-optimized code and/or don't care about handling
* error situations, feel free to use whatever you want.
*/
enum ThrowLevel
{
ThrowNone, //< Not allowed to throw
ThrowObject, //< Object level stuff, ctors, initalizers etc.
ThrowProcess, //< Process cycle level stuff
ThrowStrict, //< Stricter checks than ThrowProcess, less than ThrowSample
ThrowSample //< Sample level stuff
ThrowNone, ///< Not allowed to throw
ThrowObject, ///< Object level stuff, ctors, initalizers etc.
ThrowProcess, ///< Process cycle level stuff
ThrowStrict, ///< Stricter checks than ThrowProcess, less than ThrowSample
ThrowSample ///< Sample level stuff
};
/// Class that allows optimizing out error checking during compile time
/** Class that allows optimizing out error checking during compile time.
* Usage: to take all advantage of this class you should wrap all
* throwing statemets like this:
* \code
* if (throw_level (SomeThrowLevel) && other_optional_conditionals) {
* throw Exception (...);
* }
* \endcode
*
* The order of the conditionals in the if-clause is important.
* The checks specified in \a other_optional_conditionals are only
* optimized out if \a throw_level() is placed before it with a
* logical and (short-circuiting).
*/
template<ThrowLevel L = DEFAULT_THROW_LEVEL>
class Throwing
{

View File

@ -1,25 +0,0 @@
#ifndef AUDIOGRAPHER_TMP_FILE_H
#define AUDIOGRAPHER_TMP_FILE_H
#include "sndfile_writer.h"
#include "sndfile_reader.h"
namespace AudioGrapher
{
template<typename T>
class TmpFile : public SndfileWriter<T>, public SndfileReader<T>
{
public:
TmpFile (ChannelCount channels, nframes_t samplerate, int format)
: SndfileBase (channels, samplerate, format, "temp")
, SndfileWriter<T> (channels, samplerate, format, "temp")
, SndfileReader<T> (channels, samplerate, format, "temp")
{}
};
} // namespace
#endif // AUDIOGRAPHER_TMP_FILE_H

View File

@ -1,7 +1,7 @@
#ifndef AUDIOGRAPHER_TYPE_UTILS_H
#define AUDIOGRAPHER_TYPE_UTILS_H
#include "types.h"
#include "audiographer/types.h"
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
#include <memory>
@ -10,22 +10,22 @@
namespace AudioGrapher
{
/// Non-template base class for TypeUtils
class TypeUtilsBase
{
protected:
template<typename T, bool b>
static void do_fill(T * buffer, nframes_t frames, const boost::integral_constant<bool, b>&)
static void do_zero_fill(T * buffer, nframes_t frames, const boost::integral_constant<bool, b>&)
{ std::uninitialized_fill_n (buffer, frames, T()); }
template<typename T>
static void do_fill(T * buffer, nframes_t frames, const boost::true_type&)
{ memset (buffer, frames * sizeof(T), 0); }
private:
static void do_zero_fill(T * buffer, nframes_t frames, const boost::true_type&)
{ memset (buffer, 0, frames * sizeof(T)); }
};
template<typename T>
/// Utilities for initializing, copying, moving, etc. data
template<typename T = DefaultSampleType>
class TypeUtils : private TypeUtilsBase
{
BOOST_STATIC_ASSERT (boost::has_trivial_destructor<T>::value);
@ -34,23 +34,33 @@ class TypeUtils : private TypeUtilsBase
boost::is_floating_point<T>::value ||
boost::is_signed<T>::value> zero_fillable;
public:
/** Fill buffer with a zero value
* The value used for filling is either 0 or the value of T()
* if T is not a floating point or signed integer type
* \n RT safe
*/
inline static void zero_fill (T * buffer, nframes_t frames)
{ do_zero_fill(buffer, frames, zero_fillable()); }
inline static void copy (T* source, T* destination, nframes_t frames)
/** Copies \a frames frames of data from \a source to \a destination
* The source and destination may NOT overlap.
* \n RT safe
*/
inline static void copy (T const * source, T * destination, nframes_t frames)
{ std::uninitialized_copy (source, &source[frames], destination); }
inline static void move (T* source, T* destination, nframes_t frames)
/** Moves \a frames frames of data from \a source to \a destination
* The source and destination may overlap in any way.
* \n RT safe
*/
inline static void move (T const * source, T * destination, nframes_t frames)
{
if (destination < source) {
std::copy (source, &source[frames], destination);
} else {
std::copy_backward (source, &source[frames], destination);
} else if (destination > source) {
std::copy_backward (source, &source[frames], destination + frames);
}
}
private:
};

View File

@ -8,30 +8,8 @@ namespace AudioGrapher {
typedef int64_t nframes_t;
typedef uint8_t ChannelCount;
/** Flag field capable of holding 32 flags.
Easily grown in size to 64 flags by changing storage_type */
class FlagField {
public:
typedef uint8_t Flag;
typedef uint32_t storage_type;
FlagField() : _flags (0) {}
FlagField(FlagField const & other) : _flags (other._flags) {}
inline bool has (Flag flag) const { return _flags & (1 << flag); }
inline storage_type flags () const { return _flags; }
inline operator bool() const { return _flags; }
inline void set (Flag flag) { _flags |= (1 << flag); }
inline void remove (Flag flag) { _flags &= ~(1 << flag); }
inline void reset () { _flags = 0; }
inline FlagField & operator+= (FlagField const & other) { _flags |= other._flags; return *this; }
inline bool operator== (FlagField const & other) const { return _flags == other._flags; }
private:
storage_type _flags;
};
typedef float DefaultSampleType;
} // namespace
#endif // __audiographer_types_h__
#endif // AUDIOGRAPHER_TYPES_H

View File

@ -1,59 +0,0 @@
#ifndef AUDIOGRAPHER_UTILS_H
#define AUDIOGRAPHER_UTILS_H
#include "types.h"
#include "exception.h"
#include <cstring>
namespace AudioGrapher
{
class Utils
{
public:
static void free_resources();
/// Initialize zero buffer, if buffer is != 0, it will be used as the zero buffer
template <typename T>
static void init_zeros (nframes_t frames, T const * buffer = 0)
{
if (frames == 0) {
throw Exception (Utils(), "init_zeros must be called with an argument greater than zero.");
}
unsigned long n_zeros = frames * sizeof (T);
if (n_zeros <= num_zeros) { return; }
delete [] zeros;
if (buffer) {
zeros = reinterpret_cast<char const *>(buffer);
} else {
zeros = new char[n_zeros];
memset (const_cast<char *>(zeros), 0, n_zeros);
}
num_zeros = n_zeros;
}
template <typename T>
static T const * get_zeros (nframes_t frames)
{
if (frames * sizeof (T) > num_zeros) {
throw Exception (Utils(), "init_zeros has not been called with a large enough frame count");
}
return reinterpret_cast<T const *> (zeros);
}
template <typename T>
static nframes_t get_zero_buffer_size ()
{
return num_zeros / sizeof (T);
}
private:
static char const * zeros;
static unsigned long num_zeros;
};
} // namespace
#endif // AUDIOGRAPHER_ROUTINES_H

View File

@ -1,13 +1,14 @@
#ifndef AUDIOGRAPHER_IDENTITY_VERTEX_H
#define AUDIOGRAPHER_IDENTITY_VERTEX_H
#include "listed_source.h"
#include "sink.h"
#include "audiographer/utils/listed_source.h"
#include "audiographer/sink.h"
namespace AudioGrapher
{
template<typename T>
/// Outputs its input directly to a number of Sinks
template<typename T = DefaultSampleType>
class IdentityVertex : public ListedSource<T>, Sink<T>
{
public:

View File

@ -1,15 +1,16 @@
#ifndef AUDIOGRAPHER_LISTED_SOURCE_H
#define AUDIOGRAPHER_LISTED_SOURCE_H
#include "types.h"
#include "source.h"
#include "audiographer/types.h"
#include "audiographer/source.h"
#include <list>
namespace AudioGrapher
{
template<typename T>
/// An generic \a Source that uses a \a std::list for managing outputs
template<typename T = DefaultSampleType>
class ListedSource : public Source<T>
{
public:

View File

@ -0,0 +1,284 @@
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = AudioGrapher
PROJECT_NUMBER =
OUTPUT_DIRECTORY = ./doc
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
STRIP_FROM_PATH = ./
STRIP_FROM_INC_PATH = ./
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
BUILTIN_STL_SUPPORT = YES
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
TYPEDEF_HIDES_STRUCT = NO
SYMBOL_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = NO
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_DIRECTORIES = NO
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = audiographer \
src \
doc/mainpage.dox
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.cc
RECURSIVE = YES
EXCLUDE = src/gdither
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
HTML_DYNAMIC_SECTIONS = NO
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE =
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
USE_INLINE_TREES = NO
TREEVIEW_WIDTH = 250
FORMULA_FONTSIZE = 10
SEARCHENGINE = YES
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
DOT_FONTNAME = FreeSans
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = YES
CALLER_GRAPH = YES
GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES

View File

@ -0,0 +1,26 @@
/** @mainpage
@section Overview
AudioGrapher is best described as a signal flow management library.
It includes facilities to build graphs out of signal processing elements.
Once a graph is set up, all signal flow within the graph happens automatically.
The data flow model in Audiographer is dynamic instead of synchronous -
the type and amount of data that goes in to a graph may differ from what comes out.
AudioGrapher is aimed mostly for usage by developers,
as it includes lots of facilities that ease the development process.
The main aim of AudioGrapher is to ease development and debugging of signal flow graphs.
It makes heavy use of modern C++ techniques like templates,
and uses the boost libraries a lot.
The essential classes in AudioGrapher are Sink, Source and ProcessContext.
These three define the signal flow in a graph.
In addition, the core of AudioGrapher includes lots of utility classes.
AudioGrapher includes a bunch of ready Sink, Source and Vertex implementations.
Some are utilities used when developing more vertices,
while others are general utilities (file i/o, sample rate conversion etc.).
*/

View File

@ -0,0 +1,399 @@
/*
** Copyright (C) 2005-2007 Erik de Castro Lopo <erikd@mega-nerd.com>
**
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the author nor the names of any contributors may be used
** to endorse or promote products derived from this software without
** specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
** The above modified BSD style license (GPL and LGPL compatible) applies to
** this file. It does not apply to libsndfile itself which is released under
** the GNU LGPL or the libsndfile test suite which is released under the GNU
** GPL.
** This means that this header file can be used under this modified BSD style
** license, but the LGPL still holds for the libsndfile library itself.
*/
/*
** sndfile.hh -- A lightweight C++ wrapper for the libsndfile API.
**
** All the methods are inlines and all functionality is contained in this
** file. There is no separate implementation file.
**
** API documentation is in the doc/ directory of the source code tarball
** and at http://www.mega-nerd.com/libsndfile/api.html.
*/
#ifndef SNDFILE_HH
#define SNDFILE_HH
#include <sndfile.h>
#include <string>
#include <new> // for std::nothrow
// Prevent conflicts
namespace AudioGrapher {
class SndfileHandle
{ private :
struct SNDFILE_ref
{ SNDFILE_ref (void) ;
~SNDFILE_ref (void) ;
SNDFILE *sf ;
SF_INFO sfinfo ;
int ref ;
} ;
SNDFILE_ref *p ;
public :
/* Default constructor */
SndfileHandle (void) : p (NULL) {} ;
SndfileHandle (const char *path, int mode = SFM_READ,
int format = 0, int channels = 0, int samplerate = 0) ;
SndfileHandle (std::string const & path, int mode = SFM_READ,
int format = 0, int channels = 0, int samplerate = 0) ;
SndfileHandle (int fd, bool close_desc, int mode = SFM_READ,
int format = 0, int channels = 0, int samplerate = 0) ;
~SndfileHandle (void) ;
SndfileHandle (const SndfileHandle &orig) ;
SndfileHandle & operator = (const SndfileHandle &rhs) ;
/* Mainly for debugging/testing. */
int refCount (void) const { return (p == NULL) ? 0 : p->ref ; }
operator bool () const { return (p != NULL) ; }
bool operator == (const SndfileHandle &rhs) const { return (p == rhs.p) ; }
sf_count_t frames (void) const { return p ? p->sfinfo.frames : 0 ; }
int format (void) const { return p ? p->sfinfo.format : 0 ; }
int channels (void) const { return p ? p->sfinfo.channels : 0 ; }
int samplerate (void) const { return p ? p->sfinfo.samplerate : 0 ; }
int error (void) const ;
const char * strError (void) const ;
int command (int cmd, void *data, int datasize) ;
sf_count_t seek (sf_count_t frames, int whence) ;
void writeSync (void) ;
int setString (int str_type, const char* str) ;
const char* getString (int str_type) const ;
static int formatCheck (int format, int channels, int samplerate) ;
sf_count_t read (short *ptr, sf_count_t items) ;
sf_count_t read (int *ptr, sf_count_t items) ;
sf_count_t read (float *ptr, sf_count_t items) ;
sf_count_t read (double *ptr, sf_count_t items) ;
sf_count_t write (const short *ptr, sf_count_t items) ;
sf_count_t write (const int *ptr, sf_count_t items) ;
sf_count_t write (const float *ptr, sf_count_t items) ;
sf_count_t write (const double *ptr, sf_count_t items) ;
sf_count_t readf (short *ptr, sf_count_t frames) ;
sf_count_t readf (int *ptr, sf_count_t frames) ;
sf_count_t readf (float *ptr, sf_count_t frames) ;
sf_count_t readf (double *ptr, sf_count_t frames) ;
sf_count_t writef (const short *ptr, sf_count_t frames) ;
sf_count_t writef (const int *ptr, sf_count_t frames) ;
sf_count_t writef (const float *ptr, sf_count_t frames) ;
sf_count_t writef (const double *ptr, sf_count_t frames) ;
sf_count_t readRaw (void *ptr, sf_count_t bytes) ;
sf_count_t writeRaw (const void *ptr, sf_count_t bytes) ;
} ;
/*==============================================================================
** Nothing but implementation below.
*/
inline
SndfileHandle::SNDFILE_ref::SNDFILE_ref (void)
: ref (1)
{}
inline
SndfileHandle::SNDFILE_ref::~SNDFILE_ref (void)
{ if (sf != NULL) sf_close (sf) ; }
inline
SndfileHandle::SndfileHandle (const char *path, int mode, int fmt, int chans, int srate)
: p (NULL)
{
p = new (std::nothrow) SNDFILE_ref () ;
if (p != NULL)
{ p->ref = 1 ;
p->sfinfo.frames = 0 ;
p->sfinfo.channels = chans ;
p->sfinfo.format = fmt ;
p->sfinfo.samplerate = srate ;
p->sfinfo.sections = 0 ;
p->sfinfo.seekable = 0 ;
p->sf = sf_open (path, mode, &p->sfinfo) ;
} ;
return ;
} /* SndfileHandle const char * constructor */
inline
SndfileHandle::SndfileHandle (std::string const & path, int mode, int fmt, int chans, int srate)
: p (NULL)
{
p = new (std::nothrow) SNDFILE_ref () ;
if (p != NULL)
{ p->ref = 1 ;
p->sfinfo.frames = 0 ;
p->sfinfo.channels = chans ;
p->sfinfo.format = fmt ;
p->sfinfo.samplerate = srate ;
p->sfinfo.sections = 0 ;
p->sfinfo.seekable = 0 ;
p->sf = sf_open (path.c_str (), mode, &p->sfinfo) ;
} ;
return ;
} /* SndfileHandle std::string constructor */
SndfileHandle::SndfileHandle (int fd, bool close_desc, int mode, int fmt, int chans, int srate)
: p (NULL)
{
if (fd < 0)
return;
p = new (std::nothrow) SNDFILE_ref () ;
if (p != NULL)
{ p->ref = 1 ;
p->sfinfo.frames = 0 ;
p->sfinfo.channels = chans ;
p->sfinfo.format = fmt ;
p->sfinfo.samplerate = srate ;
p->sfinfo.sections = 0 ;
p->sfinfo.seekable = 0 ;
p->sf = sf_open_fd (fd, mode, &p->sfinfo, close_desc) ;
} ;
return ;
} /* SndfileHandle fd constructor */
inline
SndfileHandle::~SndfileHandle (void)
{ if (p != NULL && --p->ref == 0)
delete p ;
} /* SndfileHandle destructor */
inline
SndfileHandle::SndfileHandle (const SndfileHandle &orig)
: p (orig.p)
{ if (p != NULL)
++p->ref ;
} /* SndfileHandle copy constructor */
inline SndfileHandle &
SndfileHandle::operator = (const SndfileHandle &rhs)
{
if (&rhs == this)
return *this ;
if (p != NULL && --p->ref == 0)
delete p ;
p = rhs.p ;
if (p != NULL)
++p->ref ;
return *this ;
} /* SndfileHandle assignment operator */
inline int
SndfileHandle::error (void) const
{ return sf_error (p->sf) ; }
inline const char *
SndfileHandle::strError (void) const
{ return sf_strerror (p->sf) ; }
inline int
SndfileHandle::command (int cmd, void *data, int datasize)
{ return sf_command (p->sf, cmd, data, datasize) ; }
inline sf_count_t
SndfileHandle::seek (sf_count_t frame_count, int whence)
{ return sf_seek (p->sf, frame_count, whence) ; }
inline void
SndfileHandle::writeSync (void)
{ sf_write_sync (p->sf) ; }
inline int
SndfileHandle::setString (int str_type, const char* str)
{ return sf_set_string (p->sf, str_type, str) ; }
inline const char*
SndfileHandle::getString (int str_type) const
{ return sf_get_string (p->sf, str_type) ; }
inline int
SndfileHandle::formatCheck(int fmt, int chans, int srate)
{
SF_INFO sfinfo ;
sfinfo.frames = 0 ;
sfinfo.channels = chans ;
sfinfo.format = fmt ;
sfinfo.samplerate = srate ;
sfinfo.sections = 0 ;
sfinfo.seekable = 0 ;
return sf_format_check (&sfinfo) ;
}
/*---------------------------------------------------------------------*/
inline sf_count_t
SndfileHandle::read (short *ptr, sf_count_t items)
{ return sf_read_short (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::read (int *ptr, sf_count_t items)
{ return sf_read_int (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::read (float *ptr, sf_count_t items)
{ return sf_read_float (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::read (double *ptr, sf_count_t items)
{ return sf_read_double (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::write (const short *ptr, sf_count_t items)
{ return sf_write_short (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::write (const int *ptr, sf_count_t items)
{ return sf_write_int (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::write (const float *ptr, sf_count_t items)
{ return sf_write_float (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::write (const double *ptr, sf_count_t items)
{ return sf_write_double (p->sf, ptr, items) ; }
inline sf_count_t
SndfileHandle::readf (short *ptr, sf_count_t frame_count)
{ return sf_readf_short (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::readf (int *ptr, sf_count_t frame_count)
{ return sf_readf_int (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::readf (float *ptr, sf_count_t frame_count)
{ return sf_readf_float (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::readf (double *ptr, sf_count_t frame_count)
{ return sf_readf_double (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::writef (const short *ptr, sf_count_t frame_count)
{ return sf_writef_short (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::writef (const int *ptr, sf_count_t frame_count)
{ return sf_writef_int (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::writef (const float *ptr, sf_count_t frame_count)
{ return sf_writef_float (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::writef (const double *ptr, sf_count_t frame_count)
{ return sf_writef_double (p->sf, ptr, frame_count) ; }
inline sf_count_t
SndfileHandle::readRaw (void *ptr, sf_count_t bytes)
{ return sf_read_raw (p->sf, ptr, bytes) ; }
inline sf_count_t
SndfileHandle::writeRaw (const void *ptr, sf_count_t bytes)
{ return sf_write_raw (p->sf, ptr, bytes) ; }
#ifdef ENABLE_SNDFILE_WINDOWS_PROTOTYPES
inline
SndfileHandle::SndfileHandle (LPCWSTR wpath, int mode, int fmt, int chans, int srate)
: p (NULL)
{
p = new (std::nothrow) SNDFILE_ref () ;
if (p != NULL)
{ p->ref = 1 ;
p->sfinfo.frames = 0 ;
p->sfinfo.channels = chans ;
p->sfinfo.format = fmt ;
p->sfinfo.samplerate = srate ;
p->sfinfo.sections = 0 ;
p->sfinfo.seekable = 0 ;
p->sf = sf_wchar_open (wpath, mode, &p->sfinfo) ;
} ;
return ;
} /* SndfileHandle const wchar_t * constructor */
#endif
} // namespace AudioGrapher
#endif /* SNDFILE_HH */

View File

@ -0,0 +1,26 @@
#include "audiographer/debug_utils.h"
#include "audiographer/process_context.h"
#include <sstream>
namespace AudioGrapher {
std::string
DebugUtils::process_context_flag_name (FlagField::Flag flag)
{
std::ostringstream ret;
switch (flag) {
case ProcessContext<>::EndOfInput:
ret << "EndOfInput";
break;
default:
ret << flag;
break;
}
return ret.str();
}
} // namespace

View File

@ -1,17 +1,16 @@
#include "audiographer/sample_format_converter.h"
#include "audiographer/general/sample_format_converter.h"
#include "gdither/gdither.h"
#include "audiographer/exception.h"
#include "audiographer/type_utils.h"
#include "private/gdither/gdither.h"
#include <boost/format.hpp>
#include <cstring>
namespace AudioGrapher
{
template <typename TOut>
SampleFormatConverter<TOut>::SampleFormatConverter (uint32_t channels) :
SampleFormatConverter<TOut>::SampleFormatConverter (ChannelCount channels) :
channels (channels),
dither (0),
data_out_size (0),
@ -24,7 +23,9 @@ template <>
void
SampleFormatConverter<float>::init (nframes_t max_frames, int type, int data_width)
{
if (data_width != 32) { throw Exception (*this, "Unsupported data width"); }
if (throw_level (ThrowObject) && data_width != 32) {
throw Exception (*this, "Unsupported data width");
}
init_common (max_frames);
dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
}
@ -33,7 +34,9 @@ template <>
void
SampleFormatConverter<int32_t>::init (nframes_t max_frames, int type, int data_width)
{
if(data_width < 24) { throw Exception (*this, "Use SampleFormatConverter<int16_t> for data widths < 24"); }
if(throw_level (ThrowObject) && data_width < 24) {
throw Exception (*this, "Trying to use SampleFormatConverter<int32_t> for data widths < 24");
}
init_common (max_frames);
@ -41,7 +44,7 @@ SampleFormatConverter<int32_t>::init (nframes_t max_frames, int type, int data_w
dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
} else if (data_width == 32) {
dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
} else {
} else if (throw_level (ThrowObject)) {
throw Exception (*this, "Unsupported data width");
}
}
@ -50,7 +53,9 @@ template <>
void
SampleFormatConverter<int16_t>::init (nframes_t max_frames, int type, int data_width)
{
if (data_width != 16) { throw Exception (*this, "Unsupported data width"); }
if (throw_level (ThrowObject) && data_width != 16) {
throw Exception (*this, "Unsupported data width");
}
init_common (max_frames);
dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width);
}
@ -59,7 +64,9 @@ template <>
void
SampleFormatConverter<uint8_t>::init (nframes_t max_frames, int type, int data_width)
{
if (data_width != 8) { throw Exception (*this, "Unsupported data width"); }
if (throw_level (ThrowObject) && data_width != 8) {
throw Exception (*this, "Unsupported data width");
}
init_common (max_frames);
dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width);
}
@ -106,14 +113,13 @@ void
SampleFormatConverter<TOut>::process (ProcessContext<float> const & c_in)
{
float const * const data = c_in.data();
nframes_t const frames = c_in.frames();
check_frame_count (frames);
check_frame_and_channel_count (c_in.frames (), c_in.channels ());
/* Do conversion */
for (uint32_t chn = 0; chn < channels; ++chn) {
gdither_runf (dither, chn, frames / channels, data, data_out);
for (uint32_t chn = 0; chn < c_in.channels(); ++chn) {
gdither_runf (dither, chn, c_in.frames_per_channel (), data, data_out);
}
/* Write forward */
@ -157,9 +163,8 @@ void
SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
{
// Make copy of data and pass it to non-const version
nframes_t frames = c_in.frames();
check_frame_count (frames);
memcpy (data_out, c_in.data(), frames * sizeof(float));
check_frame_and_channel_count (c_in.frames(), c_in.channels());
TypeUtils<float>::copy (c_in.data(), data_out, c_in.frames());
ProcessContext<float> c (c_in, data_out);
process (c);
@ -167,17 +172,17 @@ SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
template<typename TOut>
void
SampleFormatConverter<TOut>::check_frame_count(nframes_t frames)
SampleFormatConverter<TOut>::check_frame_and_channel_count(nframes_t frames, ChannelCount channels_)
{
if (frames % channels != 0) {
throw Exception (*this, boost::str (boost::format (
"Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels")
% frames % channels));
if (throw_level (ThrowStrict) && channels_ != channels) {
throw Exception (*this, boost::str (boost::format
("Wrong channel count given to process(), %1% instead of %2%")
% channels_ % channels));
}
if (frames > data_out_size) {
throw Exception (*this, boost::str (boost::format (
"Too many frames given to process(), %1% instad of %2%")
if (throw_level (ThrowProcess) && frames > data_out_size) {
throw Exception (*this, boost::str (boost::format
("Too many frames given to process(), %1% instad of %2%")
% frames % data_out_size));
}
}

View File

@ -1,4 +1,5 @@
#include "audiographer/sr_converter.h"
#include "audiographer/general/sr_converter.h"
#include "audiographer/exception.h"
#include "audiographer/type_utils.h"
@ -21,6 +22,7 @@ SampleRateConverter::SampleRateConverter (uint32_t channels)
, data_out_size (0)
, src_state (0)
{
add_supported_flag (ProcessContext<>::EndOfInput);
}
void
@ -78,6 +80,8 @@ SampleRateConverter::allocate_buffers (nframes_t max_frames)
void
SampleRateConverter::process (ProcessContext<float> const & c)
{
check_flags (*this, c);
if (!active) {
output (c);
return;
@ -86,17 +90,11 @@ SampleRateConverter::process (ProcessContext<float> const & c)
nframes_t frames = c.frames();
float * in = const_cast<float *> (c.data()); // TODO check if this is safe!
if (throw_level (ThrowStrict) && frames > max_frames_in) {
if (throw_level (ThrowProcess) && frames > max_frames_in) {
throw Exception (*this, str (format (
"process() called with too many frames, %1% instead of %2%")
% frames % max_frames_in));
}
if (throw_level (ThrowStrict) && frames % channels != 0) {
throw Exception (*this, boost::str (boost::format (
"Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels")
% frames % channels));
}
int err;
bool first_time = true;

View File

@ -1,56 +0,0 @@
#include "audiographer/sndfile_base.h"
#include "audiographer/exception.h"
#include <boost/format.hpp>
namespace AudioGrapher
{
using std::string;
using boost::str;
using boost::format;
/* SndfileWriterBase */
SndfileBase::SndfileBase (ChannelCount channels, nframes_t samplerate, int format, string const & path)
: path (path)
{
char errbuf[256];
sf_info.channels = channels;
sf_info.samplerate = samplerate;
sf_info.format = format;
if (!sf_format_check (&sf_info)) {
throw Exception (*this, "Invalid format in constructor");
}
if (path.length() == 0) {
throw Exception (*this, "No output file specified");
}
/* TODO add checks that the directory path exists, and also
check if we are overwriting an existing file...
*/
// Open file
if (path.compare ("temp")) {
if ((sndfile = sf_open (path.c_str(), SFM_WRITE, &sf_info)) == 0) {
sf_error_str (0, errbuf, sizeof (errbuf) - 1);
throw Exception (*this, str (boost::format ("Cannot open output file \"%1%\" (%2%)") % path % errbuf));
}
} else {
FILE * file;
if (!(file = tmpfile ())) {
throw Exception (*this, "Cannot open tempfile");
}
sndfile = sf_open_fd (fileno(file), SFM_RDWR, &sf_info, true);
}
}
SndfileBase::~SndfileBase ()
{
sf_close (sndfile);
}
} // namespace

View File

@ -1,68 +0,0 @@
#include "audiographer/sndfile_reader.h"
#include <boost/format.hpp>
#include "audiographer/exception.h"
namespace AudioGrapher
{
template<typename T>
SndfileReader<T>::SndfileReader (ChannelCount channels, nframes_t samplerate, int format, std::string path)
: SndfileBase (channels, samplerate, format, path)
{
init ();
}
template<typename T>
nframes_t
SndfileReader<T>::seek (nframes_t frames, SeekType whence)
{
return sf_seek (sndfile, frames, whence);
}
template<typename T>
nframes_t
SndfileReader<T>::read (ProcessContext<T> & context)
{
if (context.channels() != sf_info.channels) {
throw Exception (*this, boost::str (boost::format (
"ProcessContext given to read() has a wrong amount of channels: %1% instead of %2%")
% context.channels() % sf_info.channels));
}
nframes_t frames_read = (*read_func) (sndfile, context.data(), context.frames());
if (frames_read < context.frames()) {
context.frames() = frames_read;
context.set_flag (ProcessContext<T>::EndOfInput);
}
output (context);
return frames_read;
}
template<>
void
SndfileReader<short>::init()
{
read_func = &sf_read_short;
}
template<>
void
SndfileReader<int>::init()
{
read_func = &sf_read_int;
}
template<>
void
SndfileReader<float>::init()
{
read_func = &sf_read_float;
}
template class SndfileReader<short>;
template class SndfileReader<int>;
template class SndfileReader<float>;
} // namespace

View File

@ -1,74 +0,0 @@
#include "audiographer/sndfile_writer.h"
#include "audiographer/exception.h"
#include <cstring>
#include <boost/format.hpp>
namespace AudioGrapher
{
using std::string;
using boost::str;
using boost::format;
template <typename T>
SndfileWriter<T>::SndfileWriter (ChannelCount channels, nframes_t samplerate, int format, string const & path) :
SndfileBase (channels, samplerate, format, path)
{
// init write function
init ();
}
template <>
void
SndfileWriter<float>::init ()
{
write_func = &sf_write_float;
}
template <>
void
SndfileWriter<int>::init ()
{
write_func = &sf_write_int;
}
template <>
void
SndfileWriter<short>::init ()
{
write_func = &sf_write_short;
}
template <typename T>
void
SndfileWriter<T>::process (ProcessContext<T> const & c)
{
if (c.channels() != sf_info.channels) {
throw Exception (*this, str (boost::format(
"Wrong number of channels given to process(), %1% instead of %2%")
% c.channels() % sf_info.channels));
}
char errbuf[256];
nframes_t written = (*write_func) (sndfile, c.data(), c.frames());
if (written != c.frames()) {
sf_error_str (sndfile, errbuf, sizeof (errbuf) - 1);
throw Exception (*this, str ( format("Could not write data to output file (%1%)") % errbuf));
}
if (c.has_flag(ProcessContext<T>::EndOfInput)) {
sf_write_sync (sndfile);
FileWritten (path);
if (debug_level (DebugProcess)) {
debug_stream() << str ( format("Finished writing file %1%") % path) << std::endl;
}
}
}
template class SndfileWriter<short>;
template class SndfileWriter<int>;
template class SndfileWriter<float>;
} // namespace

View File

@ -1,14 +0,0 @@
#include "audiographer/utils.h"
using namespace AudioGrapher;
char const * Utils::zeros = 0;
unsigned long Utils::num_zeros = 0;
void
Utils::free_resources()
{
num_zeros = 0;
delete [] zeros;
zeros = 0;
}

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/chunker.h"
#include "tests/utils.h"
#include "audiographer/general/chunker.h"
#include <cassert>
@ -7,9 +8,12 @@ using namespace AudioGrapher;
class ChunkerTest : public CppUnit::TestFixture
{
// TODO: Test EndOfInput handling
CPPUNIT_TEST_SUITE (ChunkerTest);
CPPUNIT_TEST (testSynchronousProcess);
CPPUNIT_TEST (testAsynchronousProcess);
CPPUNIT_TEST (testChoppingProcess);
CPPUNIT_TEST_SUITE_END ();
public:
@ -99,6 +103,38 @@ class ChunkerTest : public CppUnit::TestFixture
CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames / 2], frames));
CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[ 3 * frames / 2], frames / 2));
}
void testChoppingProcess()
{
sink.reset (new AppendingVectorSink<float>());
assert (frames % 2 == 0);
chunker.reset (new Chunker<float>(frames / 4));
chunker->add_output (sink);
nframes_t frames_output = 0;
ProcessContext<float> const half_context (random_data, frames / 2, 1);
ProcessContext<float> const context (random_data, frames, 1);
// 0.5
chunker->process (half_context);
frames_output = sink->get_data().size();
CPPUNIT_ASSERT_EQUAL ((nframes_t) frames / 2, frames_output);
// 1.5
chunker->process (context);
frames_output = sink->get_data().size();
CPPUNIT_ASSERT_EQUAL ((nframes_t) frames / 2 * 3, frames_output);
// 2.5
chunker->process (context);
frames_output = sink->get_data().size();
CPPUNIT_ASSERT_EQUAL (frames / 2 * 5, frames_output);
CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames / 2));
CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames / 2], frames));
CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[ 3 * frames / 2], frames / 2));
}
private:
boost::shared_ptr<Chunker<float> > chunker;

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/deinterleaver.h"
#include "tests/utils.h"
#include "audiographer/general/deinterleaver.h"
using namespace AudioGrapher;
@ -48,19 +49,16 @@ class DeInterleaverTest : public CppUnit::TestFixture
{
deinterleaver->init (channels, frames_per_channel);
ProcessContext<float> c (random_data, 0, channels);
ProcessContext<float> c (random_data, 2 * total_frames, channels);
// Too many, frames % channels == 0
c.frames() = total_frames + channels;
CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames + channels)), Exception);
// Too many, frames % channels != 0
c.frames() = total_frames + 1;
CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames + 1)), Exception);
// Too few, frames % channels != 0
c.frames() = total_frames - 1;
CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames - 1)), Exception);
}
void assert_outputs (nframes_t expected_frames)
@ -92,8 +90,7 @@ class DeInterleaverTest : public CppUnit::TestFixture
// Now with less frames
nframes_t const less_frames = frames_per_channel / 4;
c.frames() = less_frames * channels;
deinterleaver->process (c);
deinterleaver->process (c.beginning (less_frames * channels));
assert_outputs (less_frames);
}
@ -106,11 +103,10 @@ class DeInterleaverTest : public CppUnit::TestFixture
deinterleaver->output (2)->add_output (sink_c);
// Input zero frames
ProcessContext<float> c (random_data, 0, channels);
deinterleaver->process (c);
ProcessContext<float> c (random_data, total_frames, channels);
deinterleaver->process (c.beginning (0));
// ...and now test regular input
c.frames() = total_frames;
deinterleaver->process (c);
assert_outputs (frames_per_channel);
}

View File

@ -1,6 +1,7 @@
#include "utils.h"
#include "audiographer/interleaver.h"
#include "audiographer/deinterleaver.h"
#include "tests/utils.h"
#include "audiographer/general/interleaver.h"
#include "audiographer/general/deinterleaver.h"
using namespace AudioGrapher;
@ -55,8 +56,7 @@ class InterleaverDeInterleaverTest : public CppUnit::TestFixture
// And a second round...
nframes_t less_frames = (frames_per_channel / 10) * channels;
c.frames() = less_frames;
deinterleaver->process (c);
deinterleaver->process (c.beginning (less_frames));
CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames));
}
@ -86,12 +86,9 @@ class InterleaverDeInterleaverTest : public CppUnit::TestFixture
// And a second round...
nframes_t less_frames = frames_per_channel / 5;
c_a.frames() = less_frames;
c_b.frames() = less_frames;
c_c.frames() = less_frames;
interleaver->input (0)->process (c_a);
interleaver->input (1)->process (c_b);
interleaver->input (2)->process (c_c);
interleaver->input (0)->process (c_a.beginning (less_frames));
interleaver->input (1)->process (c_b.beginning (less_frames));
interleaver->input (2)->process (c_c.beginning (less_frames));
CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames));
CPPUNIT_ASSERT (TestUtils::array_equals (random_data_b, sink_b->get_array(), less_frames));

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/interleaver.h"
#include "tests/utils.h"
#include "audiographer/general/interleaver.h"
using namespace AudioGrapher;
@ -50,16 +51,13 @@ class InterleaverTest : public CppUnit::TestFixture
ProcessContext<float> c (random_data, frames + 1, 1);
CPPUNIT_ASSERT_THROW (interleaver->input (0)->process (c), Exception);
c.frames() = frames;
interleaver->input (0)->process (c);
interleaver->input (1)->process (c);
c.frames() = frames -1;
CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception);
interleaver->input (0)->process (c.beginning (frames));
interleaver->input (1)->process (c.beginning (frames));
CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c.beginning (frames - 1)), Exception);
interleaver->input (0)->process (c);
interleaver->input (1)->process (c);
c.frames() = frames;
CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception);
interleaver->input (0)->process (c.beginning (frames - 1));
interleaver->input (1)->process (c.beginning (frames - 1));
CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c.beginning (frames)), Exception);
}
void testOutputSize()
@ -76,10 +74,9 @@ class InterleaverTest : public CppUnit::TestFixture
CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
nframes_t less_frames = frames / 2;
c.frames() = less_frames;
interleaver->input (0)->process (c);
interleaver->input (1)->process (c);
interleaver->input (2)->process (c);
interleaver->input (0)->process (c.beginning (less_frames));
interleaver->input (1)->process (c.beginning (less_frames));
interleaver->input (2)->process (c.beginning (less_frames));
expected_frames = less_frames * channels;
generated_frames = sink->get_data().size();
@ -91,15 +88,14 @@ class InterleaverTest : public CppUnit::TestFixture
interleaver->add_output (sink);
// input zero frames to all inputs
ProcessContext<float> c (random_data, 0, 1);
interleaver->input (0)->process (c);
interleaver->input (1)->process (c);
interleaver->input (2)->process (c);
ProcessContext<float> c (random_data, frames, 1);
interleaver->input (0)->process (c.beginning (0));
interleaver->input (1)->process (c.beginning (0));
interleaver->input (2)->process (c.beginning (0));
// NOTE zero input is allowed to be a NOP
// ...now test regular input
c.frames() = frames;
interleaver->input (0)->process (c);
interleaver->input (1)->process (c);
interleaver->input (2)->process (c);

View File

@ -1,7 +1,7 @@
#include "utils.h"
#include "tests/utils.h"
#include "audiographer/normalizer.h"
#include "audiographer/peak_reader.h"
#include "audiographer/general/normalizer.h"
#include "audiographer/general/peak_reader.h"
using namespace AudioGrapher;

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/peak_reader.h"
#include "tests/utils.h"
#include "audiographer/general/peak_reader.h"
using namespace AudioGrapher;

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/sample_format_converter.h"
#include "tests/utils.h"
#include "audiographer/general/sample_format_converter.h"
using namespace AudioGrapher;
@ -192,10 +193,10 @@ class SampleFormatConverterTest : public CppUnit::TestFixture
ProcessContext<float> pc(random_data, 4, 1);
CPPUNIT_ASSERT_THROW (converter->process (pc), Exception);
pc.frames() = frames - (frames % 3);
converter->process (pc);
nframes_t new_frame_count = frames - (frames % 3);
converter->process (ProcessContext<float> (pc.data(), new_frame_count, 3));
frames_output = sink->get_data().size();
CPPUNIT_ASSERT_EQUAL (pc.frames(), frames_output);
CPPUNIT_ASSERT_EQUAL (new_frame_count, frames_output);
CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), pc.frames()));
}

View File

@ -1,6 +1,6 @@
#include "utils.h"
#include "tests/utils.h"
#include "audiographer/silence_trimmer.h"
#include "audiographer/general/silence_trimmer.h"
using namespace AudioGrapher;
@ -29,7 +29,7 @@ class SilenceTrimmerTest : public CppUnit::TestFixture
half_random_data = TestUtils::init_random_data(frames);
memset(half_random_data, 0, (frames / 2) * sizeof(float));
trimmer.reset (new SilenceTrimmer<float>());
trimmer.reset (new SilenceTrimmer<float> (frames / 2));
sink.reset (new AppendingVectorSink<float>());
trimmer->set_trim_beginning (true);
@ -41,14 +41,11 @@ class SilenceTrimmerTest : public CppUnit::TestFixture
delete [] random_data;
delete [] zero_data;
delete [] half_random_data;
AudioGrapher::Utils::free_resources();
}
void testFullBuffers()
{
trimmer->add_output (sink);
AudioGrapher::Utils::init_zeros<float>(frames / 2);
{
ProcessContext<float> c (zero_data, frames, 1);
@ -93,7 +90,9 @@ class SilenceTrimmerTest : public CppUnit::TestFixture
void testPartialBuffers()
{
trimmer->add_output (sink);
AudioGrapher::Utils::init_zeros<float>(frames / 4);
trimmer->reset (frames / 4);
trimmer->set_trim_beginning (true);
trimmer->set_trim_end (true);
{
ProcessContext<float> c (half_random_data, frames, 1);
@ -121,31 +120,14 @@ class SilenceTrimmerTest : public CppUnit::TestFixture
void testExceptions()
{
// TODO more tests here
trimmer->add_output (sink);
{
ProcessContext<float> c (random_data, frames, 1);
trimmer->process (c);
}
{
ProcessContext<float> c (zero_data, frames, 1);
trimmer->process (c);
}
{
// Zeros not inited, so this should throw
ProcessContext<float> c (random_data, frames, 1);
CPPUNIT_ASSERT_THROW (trimmer->process (c), Exception);
CPPUNIT_ASSERT_THROW (trimmer->reset (0), Exception);
}
}
void testAddSilenceBeginning()
{
trimmer->add_output (sink);
AudioGrapher::Utils::init_zeros<float>(frames / 2);
nframes_t silence = frames / 2;
trimmer->add_silence_to_beginning (silence);
@ -162,7 +144,6 @@ class SilenceTrimmerTest : public CppUnit::TestFixture
void testAddSilenceEnd()
{
trimmer->add_output (sink);
AudioGrapher::Utils::init_zeros<float>(frames / 2);
nframes_t silence = frames / 3;
trimmer->add_silence_to_end (silence);
@ -178,6 +159,9 @@ class SilenceTrimmerTest : public CppUnit::TestFixture
trimmer->process (c);
}
nframes_t frames_processed = sink->get_data().size();
nframes_t total_frames = 2 * frames + silence;
CPPUNIT_ASSERT_EQUAL (total_frames, frames_processed);
CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames], random_data, frames));
CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames * 2], zero_data, silence));

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/sr_converter.h"
#include "tests/utils.h"
#include "audiographer/general/sr_converter.h"
using namespace AudioGrapher;

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/threader.h"
#include "tests/utils.h"
#include "audiographer/general/threader.h"
using namespace AudioGrapher;

View File

@ -0,0 +1,47 @@
#include "tests/utils.h"
#include "audiographer/sndfile/tmp_file.h"
using namespace AudioGrapher;
class TmpFileTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE (TmpFileTest);
CPPUNIT_TEST (testProcess);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp()
{
frames = 128;
random_data = TestUtils::init_random_data(frames);
}
void tearDown()
{
delete [] random_data;
}
void testProcess()
{
uint channels = 2;
file.reset (new TmpFile<float>(SF_FORMAT_WAV | SF_FORMAT_FLOAT, channels, 44100));
AllocatingProcessContext<float> c (random_data, frames, channels);
c.set_flag (ProcessContext<float>::EndOfInput);
file->process (c);
TypeUtils<float>::zero_fill (c.data (), c.frames());
file->seek (0, SEEK_SET);
file->read (c);
CPPUNIT_ASSERT (TestUtils::array_equals (random_data, c.data(), c.frames()));
}
private:
boost::shared_ptr<TmpFile<float> > file;
float * random_data;
nframes_t frames;
};
CPPUNIT_TEST_SUITE_REGISTRATION (TmpFileTest);

View File

@ -1,42 +0,0 @@
#include "utils.h"
#include "audiographer/sndfile_writer.h"
using namespace AudioGrapher;
class SndfileWriterTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE (SndfileWriterTest);
CPPUNIT_TEST (testProcess);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp()
{
frames = 128;
random_data = TestUtils::init_random_data(frames);
}
void tearDown()
{
delete [] random_data;
}
void testProcess()
{
uint channels = 2;
std::string filename ("test.wav");
writer.reset (new SndfileWriter<float>(channels, 44100, SF_FORMAT_WAV | SF_FORMAT_FLOAT, filename));
ProcessContext<float> c (random_data, frames, channels);
c.set_flag (ProcessContext<float>::EndOfInput);
writer->process (c);
}
private:
boost::shared_ptr<SndfileWriter<float> > writer;
float * random_data;
nframes_t frames;
};
CPPUNIT_TEST_SUITE_REGISTRATION (SndfileWriterTest);

View File

@ -0,0 +1,112 @@
#include "tests/utils.h"
#include "audiographer/type_utils.h"
using namespace AudioGrapher;
class TypeUtilsTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE (TypeUtilsTest);
CPPUNIT_TEST (testZeroFillPod);
CPPUNIT_TEST (testZeroFillNonPod);
CPPUNIT_TEST (testCopy);
CPPUNIT_TEST (testMoveBackward);
CPPUNIT_TEST (testMoveForward);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp()
{
}
void tearDown()
{
}
void testZeroFillPod()
{
unsigned frames = 10;
float buf[frames];
TypeUtils<float>::zero_fill (buf, frames);
float zero = 0.0;
for (unsigned i = 0; i < frames; ++i) {
CPPUNIT_ASSERT_EQUAL (zero, buf[i]);
}
}
void testZeroFillNonPod()
{
unsigned frames = 10;
NonPodType buf[frames];
TypeUtils<NonPodType>::zero_fill (buf, frames);
NonPodType zero;
for (unsigned i = 0; i < frames; ++i) {
CPPUNIT_ASSERT (zero == buf[i]);
}
}
void testMoveBackward()
{
int seq[8] = { 0, 1, 2, 3,
4, 5, 6, 7 };
TypeUtils<int>::move (&seq[4], &seq[2], 4);
for (int i = 2; i < 2 + 4; ++i) {
CPPUNIT_ASSERT_EQUAL (i + 2, seq[i]);
}
}
void testMoveForward()
{
int seq[8] = { 0, 1, 2, 3,
4, 5, 6, 7 };
TypeUtils<int>::move (&seq[2], &seq[4], 4);
for (int i = 4; i < 4 + 4; ++i) {
CPPUNIT_ASSERT_EQUAL (i - 2, seq[i]);
}
}
void testCopy()
{
int const seq1[4] = { 1, 2, 3, 4 };
int const seq2[4] = { 5, 6, 7, 8 };
int seq3[8] = { 0, 0, 0, 0,
0, 0, 0, 0 };
TypeUtils<int>::copy (seq1, seq3, 4);
for (int i = 0; i < 4; ++i) {
CPPUNIT_ASSERT_EQUAL (seq1[i], seq3[i]);
}
for (int i = 4; i < 8; ++i) {
CPPUNIT_ASSERT_EQUAL (0, seq3[i]);
}
TypeUtils<int>::copy (seq2, &seq3[4], 4);
for (int i = 0; i < 4; ++i) {
CPPUNIT_ASSERT_EQUAL (seq1[i], seq3[i]);
}
for (int i = 0; i < 4; ++i) {
CPPUNIT_ASSERT_EQUAL (seq2[i], seq3[4 + i]);
}
}
private:
struct NonPodType {
NonPodType() : data (42) {}
bool operator== (NonPodType const & other) const
{ return data == other.data; }
int data;
};
};
CPPUNIT_TEST_SUITE_REGISTRATION (TypeUtilsTest);

View File

@ -1,5 +1,6 @@
#include "utils.h"
#include "audiographer/identity_vertex.h"
#include "tests/utils.h"
#include "audiographer/utils/identity_vertex.h"
using namespace AudioGrapher;

View File

@ -46,27 +46,24 @@ def build(bld):
# Headers
#bld.install_files('${INCLUDEDIR}/audiographer', 'audiographer/*.h')
#bld.install_files('${INCLUDEDIR}/audiographer/general', 'audiographer/general/*.h')
#bld.install_files('${INCLUDEDIR}/audiographer/sndfile', 'audiographer/sndfile/*.h')
#bld.install_files('${INCLUDEDIR}/audiographer/utils', 'audiographer/utils/*.h')
#bld.env['BUILD_TESTS'] = True
bld.env['HAVE_ALL_GTHREAD'] = bld.env['HAVE_GLIB'] and bld.env['HAVE_GLIBMM'] and bld.env['HAVE_GTHREAD']
audiographer = bld.new_task_gen('cxx', 'shlib')
audiographer.source = '''
src/gdither/gdither.cc
src/sample_format_converter.cc
private/gdither/gdither.cc
src/general/sample_format_converter.cc
src/routines.cc
src/utils.cc
src/debug_utils.cc
'''
if bld.env['HAVE_SNDFILE']:
audiographer.source += '''
src/sndfile_base.cc
src/sndfile_writer.cc
src/sndfile_reader.cc
'''
if bld.env['HAVE_SAMPLERATE']:
audiographer.source += '''
src/sr_converter.cc
src/general/sr_converter.cc
'''
audiographer.name = 'libaudiographer'
@ -76,36 +73,38 @@ def build(bld):
audiographer.uselib = 'GLIB GLIBMM GTHREAD SAMPLERATE SNDFILE'
audiographer.vnum = AUDIOGRAPHER_LIB_VERSION
audiographer.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3')
if bld.env['BUILD_TESTS'] and bld.env['HAVE_CPPUNIT']:
# Unit tests
obj = bld.new_task_gen('cxx', 'program')
obj.source = '''
tests/identity_vertex_test.cc
tests/interleaver_test.cc
tests/deinterleaver_test.cc
tests/interleaver_deinterleaver_test.cc
tests/chunker_test.cc
tests/sample_format_converter_test.cc
tests/test_runner.cc
tests/peak_reader_test.cc
tests/normalizer_test.cc
tests/silence_trimmer_test.cc
tests/type_utils_test.cc
tests/utils/identity_vertex_test.cc
tests/general/interleaver_test.cc
tests/general/deinterleaver_test.cc
tests/general/interleaver_deinterleaver_test.cc
tests/general/chunker_test.cc
tests/general/sample_format_converter_test.cc
tests/general/peak_reader_test.cc
tests/general/normalizer_test.cc
tests/general/silence_trimmer_test.cc
'''
if bld.env['HAVE_ALL_GTHREAD']:
obj.source += '''
tests/threader_test.cc
tests/general/threader_test.cc
'''
if bld.env['HAVE_SNDFILE']:
obj.source += '''
tests/sndfile_writer_test.cc
tests/sndfile/tmp_file_test.cc
'''
if bld.env['HAVE_SAMPLERATE']:
obj.source += '''
tests/sr_converter_test.cc
tests/general/sr_converter_test.cc
'''
obj.uselib_local = 'libaudiographer'