#include "ardour/ardour.h"
-#include "audiographer/sample_format_converter.h"
+#include "audiographer/general/sample_format_converter.h"
namespace ARDOUR
{
#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>
#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"
{
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
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));
}
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);
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);
}
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);
--- /dev/null
+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).
\ No newline at end of file
+++ /dev/null
-#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
-
#ifndef AUDIOGRAPHER_DEBUG_UTILS_H
#define AUDIOGRAPHER_DEBUG_UTILS_H
+#include "flag_field.h"
+
#include <string>
#ifdef __GNUC__
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)
{
return typeid(obj).name();
}
+ /// Returns name of ProcessContext::Flag
+ static std::string process_context_flag_name (FlagField::Flag flag);
};
} // namespace
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
{
+++ /dev/null
-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;
-}
+++ /dev/null
-#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
namespace AudioGrapher
{
+/** AudioGrapher Exception class.
+ * Automatically tells which class an exception was thrown from.
+ */
class Exception : public std::exception
{
public:
--- /dev/null
+#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
--- /dev/null
+#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
\ No newline at end of file
--- /dev/null
+#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
+
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#ifndef AUDIOGRAPHER_NORMALIZER_H
+#define AUDIOGRAPHER_NORMALIZER_H
+
+#include "audiographer/sink.h"
+#include "audiographer/routines.h"
+#include "audiographer/utils/listed_source.h"
+
+#include <cstring>
+
+namespace AudioGrapher
+{
+
+/// 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)
+ , buffer_size (0)
+ {
+ target = pow (10.0f, target_dB * 0.05f);
+ }
+
+ ~Normalizer()
+ {
+ 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) {
+ /* don't even try */
+ enabled = false;
+ } else {
+ enabled = true;
+ gain = target / peak;
+ }
+ }
+
+ /** 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;
+ buffer = new float[frames];
+ buffer_size = frames;
+ }
+
+ /// Process a const ProcessContext \see alloc_buffer() \n RT safe
+ void process (ProcessContext<float> const & c)
+ {
+ if (throw_level (ThrowProcess) && c.frames() > buffer_size) {
+ throw Exception (*this, "Too many frames given to process()");
+ }
+
+ if (enabled) {
+ memcpy (buffer, c.data(), c.frames() * sizeof(float));
+ Routines::apply_gain_to_buffer (buffer, c.frames(), gain);
+ }
+
+ ProcessContext<float> c_out (c, buffer);
+ ListedSource<float>::output (c_out);
+ }
+
+ /// Process a non-const ProcsesContext in-place \n RT safe
+ void process (ProcessContext<float> & c)
+ {
+ if (enabled) {
+ Routines::apply_gain_to_buffer (c.data(), c.frames(), gain);
+ }
+ ListedSource<float>::output(c);
+ }
+
+ private:
+ bool enabled;
+ float target;
+ float gain;
+
+ float * buffer;
+ nframes_t buffer_size;
+};
+
+
+} // namespace
+
+#endif // AUDIOGRAPHER_NORMALIZER_H
--- /dev/null
+#ifndef AUDIOGRAPHER_PEAK_READER_H
+#define AUDIOGRAPHER_PEAK_READER_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);
+ }
+
+ using Sink<float>::process;
+
+ private:
+ float peak;
+};
+
+
+} // namespace
+
+#endif // AUDIOGRAPHER_PEAK_READER_H
--- /dev/null
+#ifndef AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
+#define AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
+
+#include "audiographer/sink.h"
+#include "audiographer/utils/listed_source.h"
+#include "private/gdither/gdither_types.h"
+
+namespace AudioGrapher
+{
+
+/// Dither types from the gdither library
+enum DitherType
+{
+ D_None = GDitherNone, ///< No didtering
+ D_Rect = GDitherRect, ///< Rectangular dithering, i.e. white noise
+ D_Tri = GDitherTri, ///< Triangular dithering
+ D_Shaped = GDitherShaped ///< Actually noise shaping, only works for 46kHzish signals
+};
+
+/** Sample format converter that does dithering.
+ * 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>
+ , public Throwing<>
+{
+ public:
+ /** Constructor
+ * \param channels number of channels in stream
+ */
+ SampleFormatConverter (ChannelCount channels);
+ ~SampleFormatConverter ();
+
+ /** Initialize and allocate buffers for processing.
+ * \param max_frames maximum number of frames that is allowed to be used in calls to \a process()
+ * \param type dither type from \a DitherType
+ * \param data_width data with in bits
+ * \note If the non-const version of process() is used with floats,
+ * there is no need to call this function.
+ */
+ void init (nframes_t max_frames, int type, int data_width);
+
+ /// Set whether or not clipping to [-1.0, 1.0] should occur when TOut = float. Clipping is off by default
+ void set_clip_floats (bool yn) { clip_floats = yn; }
+
+ /// Processes data without modifying it
+ void process (ProcessContext<float> const & c_in);
+
+ /// This version is only different in the case when \a TOut = float, and float clipping is on.
+ void process (ProcessContext<float> & c_in);
+
+ private:
+ void reset();
+ void init_common(nframes_t max_frames); // not-template-specialized part of init
+ void check_frame_and_channel_count(nframes_t frames, ChannelCount channels_);
+
+ ChannelCount channels;
+ GDither dither;
+ nframes_t data_out_size;
+ TOut * data_out;
+
+ bool clip_floats;
+
+};
+
+} // namespace
+
+#endif // AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
--- /dev/null
+#ifndef AUDIOGRAPHER_SILENCE_TRIMMER_H
+#define AUDIOGRAPHER_SILENCE_TRIMMER_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 {
+
+/// 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:
+
+ /// Constructor, \see reset() \n Not RT safe
+ SilenceTrimmer(nframes_t silence_buffer_size_ = 1024)
+ : silence_buffer_size (0)
+ , silence_buffer (0)
+ {
+ reset (silence_buffer_size_);
+ add_supported_flag (ProcessContext<T>::EndOfInput);
+ }
+
+ ~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;
+ trim_end = false;
+ silence_frames = 0;
+ max_output_frames = 0;
+ add_to_beginning = 0;
+ 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 (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 (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 (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 (throw_level (ThrowObject) && in_end) {
+ throw Exception(*this, "Tried to set end trim after already reaching end");
+ }
+ trim_end = yn;
+ }
+
+ /** 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 (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;
+
+ if (in_beginning) {
+
+ bool has_data = true;
+
+ // only check silence if doing either of these
+ // This will set both has_data and frame_index
+ if (add_to_beginning || trim_beginning) {
+ has_data = find_first_non_zero_sample (c, frame_index);
+ }
+
+ // 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);
+ }
+ add_to_beginning *= c.channels();
+ output_silence_frames (c_copy, add_to_beginning);
+ }
+
+ // If we are not trimming the beginning, output everything
+ // 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);
+ }
+
+ } 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
+ 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);
+ }
+ }
+
+ using Sink<T>::process;
+
+ private:
+
+ bool find_first_non_zero_sample (ProcessContext<T> const & c, nframes_t & result_frame)
+ {
+ for (nframes_t i = 0; i < c.frames(); ++i) {
+ if (c.data()[i] != static_cast<T>(0.0)) {
+ result_frame = i;
+ // Round down to nearest interleaved "frame" beginning
+ result_frame -= result_frame % c.channels();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void output_silence_frames (ProcessContext<T> const & c, nframes_t & total_frames, bool adding_to_end = false)
+ {
+ bool end_of_input = c.has_flag (ProcessContext<T>::EndOfInput);
+ c.remove_flag (ProcessContext<T>::EndOfInput);
+
+ while (total_frames > 0) {
+ nframes_t frames = std::min (silence_buffer_size, total_frames);
+ if (max_output_frames) {
+ frames = std::min (frames, max_output_frames);
+ }
+ frames -= frames % c.channels();
+
+ total_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);
+ bool const is_last_frame_output_in_this_function = (total_frames == 0);
+ if (end_of_input && no_more_silence_will_be_added && is_last_frame_output_in_this_function) {
+ c_out().set_flag (ProcessContext<T>::EndOfInput);
+ }
+ ListedSource<T>::output (c_out);
+ }
+ }
+
+
+ bool in_beginning;
+ bool in_end;
+
+ bool trim_beginning;
+ bool trim_end;
+
+ nframes_t silence_frames;
+ nframes_t max_output_frames;
+
+ nframes_t add_to_beginning;
+ nframes_t add_to_end;
+
+ nframes_t silence_buffer_size;
+ T * silence_buffer;
+};
+
+} // namespace
+
+#endif // AUDIOGRAPHER_SILENCE_TRIMMER_H
--- /dev/null
+#ifndef AUDIOGRAPHER_SR_CONVERTER_H
+#define AUDIOGRAPHER_SR_CONVERTER_H
+
+#include <samplerate.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 FlagDebuggable<>
+ , public Throwing<>
+{
+ public:
+ /// Constructor. \n RT safe
+ SampleRateConverter (uint32_t channels);
+ ~SampleRateConverter ();
+
+ /// 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 \n RT safe
+ nframes_t allocate_buffers (nframes_t max_frames);
+
+ /** 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;
+
+ private:
+
+ void set_end_of_input (ProcessContext<float> const & c);
+ void reset ();
+
+ bool active;
+ uint32_t channels;
+ nframes_t max_frames_in;
+
+ float * leftover_data;
+ nframes_t leftover_frames;
+ nframes_t max_leftover_frames;
+
+ float * data_out;
+ nframes_t data_out_size;
+
+ SRC_DATA src_data;
+ SRC_STATE* src_state;
+};
+
+} // namespace
+
+#endif // AUDIOGRAPHER_SR_CONVERTER_H
--- /dev/null
+#ifndef AUDIOGRAPHER_THREADER_H
+#define AUDIOGRAPHER_THREADER_H
+
+#include <glibmm/threadpool.h>
+#include <sigc++/slot.h>
+#include <boost/format.hpp>
+
+#include <glib.h>
+#include <vector>
+#include <algorithm>
+
+#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:
+ template<typename T>
+ ThreaderException (T const & thrower, std::exception const & e)
+ : Exception (thrower,
+ boost::str ( boost::format
+ ("\n\t- Dynamic type: %1%\n\t- what(): %2%")
+ % DebugUtils::demangled_name (e) % e.what() ))
+ { }
+};
+
+/// Class for distributing processing across several threads
+template <typename T = DefaultSampleType>
+class Threader : public Source<T>, public Sink<T>
+{
+ private:
+ typedef std::vector<typename Source<T>::SinkPtr> OutputVec;
+
+ 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)
+ , wait_timeout (wait_timeout_milliseconds)
+ { }
+
+ 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());
+ }
+
+ /// Processes context concurrently by scheduling each output separately to the given thread pool
+ void process (ProcessContext<T> const & c)
+ {
+ wait_mutex.lock();
+
+ exception.reset();
+
+ unsigned int outs = outputs.size();
+ g_atomic_int_add (&readers, outs);
+ for (unsigned int i = 0; i < outs; ++i) {
+ thread_pool.push (sigc::bind (sigc::mem_fun (this, &Threader::process_output), c, i));
+ }
+
+ wait();
+ }
+
+ using Sink<T>::process;
+
+ private:
+
+ void wait()
+ {
+ Glib::TimeVal wait_time;
+ wait_time.assign_current_time();
+ wait_time.add_milliseconds(wait_timeout);
+
+ wait_cond.timed_wait(wait_mutex, wait_time);
+ bool timed_out = (g_atomic_int_get (&readers) != 0);
+ wait_mutex.unlock();
+ if (timed_out) { throw Exception (*this, "wait timed out"); }
+
+ if (exception) {
+ throw *exception;
+ }
+ }
+
+ void process_output(ProcessContext<T> const & c, unsigned int output)
+ {
+ try {
+ outputs[output]->process (c);
+ } catch (std::exception const & e) {
+ // Only first exception will be passed on
+ exception_mutex.lock();
+ if(!exception) { exception.reset (new ThreaderException (*this, e)); }
+ exception_mutex.unlock();
+ }
+
+ if (g_atomic_int_dec_and_test (&readers)) {
+ wait_cond.signal();
+ }
+ }
+
+ OutputVec outputs;
+
+ Glib::ThreadPool & thread_pool;
+ Glib::Mutex wait_mutex;
+ Glib::Cond wait_cond;
+ gint readers;
+ long wait_timeout;
+
+ Glib::Mutex exception_mutex;
+ boost::shared_ptr<ThreaderException> exception;
+
+};
+
+} // namespace
+
+#endif //AUDIOGRAPHER_THREADER_H
\ No newline at end of file
+++ /dev/null
-#ifndef AUDIOGRAPHER_IDENTITY_VERTEX_H
-#define AUDIOGRAPHER_IDENTITY_VERTEX_H
-
-#include "listed_source.h"
-#include "sink.h"
-
-namespace AudioGrapher
-{
-
-template<typename T>
-class IdentityVertex : public ListedSource<T>, Sink<T>
-{
- public:
- void process (ProcessContext<T> const & c) { ListedSource<T>::output(c); }
- void process (ProcessContext<T> & c) { ListedSource<T>::output(c); }
-};
-
-
-} // namespace
-
-#endif // AUDIOGRAPHER_IDENTITY_VERTEX_H
+++ /dev/null
-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;
-}
+++ /dev/null
-#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
+++ /dev/null
-#ifndef AUDIOGRAPHER_LISTED_SOURCE_H
-#define AUDIOGRAPHER_LISTED_SOURCE_H
-
-#include "types.h"
-#include "source.h"
-
-#include <list>
-
-namespace AudioGrapher
-{
-
-template<typename T>
-class ListedSource : public Source<T>
-{
- public:
- void add_output (typename Source<T>::SinkPtr output) { outputs.push_back(output); }
- void clear_outputs () { outputs.clear(); }
- void remove_output (typename Source<T>::SinkPtr output) { outputs.remove(output); }
-
- protected:
-
- typedef std::list<typename Source<T>::SinkPtr> SinkList;
-
- /// Helper for derived classes
- void output (ProcessContext<T> const & c)
- {
- for (typename SinkList::iterator i = outputs.begin(); i != outputs.end(); ++i) {
- (*i)->process (c);
- }
- }
-
- void output (ProcessContext<T> & c)
- {
- if (output_size_is_one()) {
- // only one output, so we can keep this non-const
- outputs.front()->process (c);
- } else {
- output (const_cast<ProcessContext<T> const &> (c));
- }
- }
-
- inline bool output_size_is_one () { return (!outputs.empty() && ++outputs.begin() == outputs.end()); }
-
- SinkList outputs;
-};
-
-} // namespace
-
-#endif //AUDIOGRAPHER_LISTED_SOURCE_H
-
+++ /dev/null
-#ifndef AUDIOGRAPHER_NORMALIZER_H
-#define AUDIOGRAPHER_NORMALIZER_H
-
-#include "listed_source.h"
-#include "sink.h"
-#include "routines.h"
-
-#include <cstring>
-
-namespace AudioGrapher
-{
-
-class Normalizer : public ListedSource<float>, Sink<float>
-{
- public:
- Normalizer (float target_dB)
- : enabled (false)
- , buffer (0)
- , buffer_size (0)
- {
- target = pow (10.0f, target_dB * 0.05f);
- }
-
- ~Normalizer()
- {
- delete [] buffer;
- }
-
- void set_peak (float peak)
- {
- if (peak == 0.0f || peak == target) {
- /* don't even try */
- enabled = false;
- } else {
- enabled = true;
- gain = target / peak;
- }
- }
-
- void alloc_buffer(nframes_t frames)
- {
- delete [] buffer;
- buffer = new float[frames];
- buffer_size = frames;
- }
-
- void process (ProcessContext<float> const & c)
- {
- if (c.frames() > buffer_size) {
- throw Exception (*this, "Too many frames given to process()");
- }
-
- if (enabled) {
- memcpy (buffer, c.data(), c.frames() * sizeof(float));
- Routines::apply_gain_to_buffer (buffer, c.frames(), gain);
- }
-
- ProcessContext<float> c_out (c, buffer);
- ListedSource<float>::output (c_out);
- }
-
- void process (ProcessContext<float> & c)
- {
- if (enabled) {
- Routines::apply_gain_to_buffer (c.data(), c.frames(), gain);
- }
- ListedSource<float>::output(c);
- }
-
- private:
- bool enabled;
- float target;
- float gain;
-
- float * buffer;
- nframes_t buffer_size;
-};
-
-
-} // namespace
-
-#endif // AUDIOGRAPHER_NORMALIZER_H
+++ /dev/null
-#ifndef AUDIOGRAPHER_PEAK_READER_H
-#define AUDIOGRAPHER_PEAK_READER_H
-
-#include "listed_source.h"
-#include "sink.h"
-#include "routines.h"
-
-namespace AudioGrapher
-{
-
-class PeakReader : public ListedSource<float>, public Sink<float>
-{
- public:
- PeakReader() : peak (0.0) {}
-
- float get_peak() { return peak; }
- void reset() { peak = 0.0; }
-
- 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);
- }
-
- private:
- float peak;
-};
-
-
-} // namespace
-
-#endif // AUDIOGRAPHER_PEAK_READER_H
#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
* 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);
/// 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 () {}
/// \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; }
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>
};
/// A wrapper for a const ProcesContext which can be created from const data
-template <typename T>
+template <typename T = DefaultSampleType>
class ConstProcessContext
{
public:
#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;
}
}
} // namespace
-#undef routines_nframes_t
-
#endif // AUDIOGRAPHER_ROUTINES_H
+++ /dev/null
-#ifndef AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
-#define AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
-
-#include "listed_source.h"
-#include "sink.h"
-#include "gdither/gdither_types.h"
-
-namespace AudioGrapher
-{
-
-/// Dither types from the gdither library
-enum DitherType
-{
- D_None = GDitherNone, ///< No didtering
- D_Rect = GDitherRect, ///< Rectangular dithering, i.e. white noise
- D_Tri = GDitherTri, ///< Triangular dithering
- D_Shaped = GDitherShaped ///< Actually noise shaping, only works for 46kHzish signals
-};
-
-/** Sample format converter that does dithering.
- * 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>
-{
- public:
- /** Constructor
- * \param channels number of channels in stream
- */
- SampleFormatConverter (uint32_t channels);
- ~SampleFormatConverter ();
-
- /** Initialize and allocate buffers for processing.
- * \param max_frames maximum number of frames that is allowed to be used in calls to \a process()
- * \param type dither type from \a DitherType
- * \param data_width data with in bits
- * \note If the non-const version of process() is used with floats,
- * there is no need to call this function.
- */
- void init (nframes_t max_frames, int type, int data_width);
-
- /// Set whether or not clipping to [-1.0, 1.0] should occur when TOut = float. Clipping is off by default
- void set_clip_floats (bool yn) { clip_floats = yn; }
-
- /// Processes data without modifying it
- void process (ProcessContext<float> const & c_in);
-
- /// This version is only different in the case when \a TOut = float, and float clipping is on.
- void process (ProcessContext<float> & c_in);
-
- private:
- void reset();
- void init_common(nframes_t max_frames); // not-template-specialized part of init
- void check_frame_count(nframes_t frames);
-
- uint32_t channels;
- GDither dither;
- nframes_t data_out_size;
- TOut * data_out;
-
- bool clip_floats;
-
-};
-
-} // namespace
-
-#endif // AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H
+++ /dev/null
-#ifndef AUDIOGRAPHER_SILENCE_TRIMMER_H
-#define AUDIOGRAPHER_SILENCE_TRIMMER_H
-
-#include "listed_source.h"
-#include "sink.h"
-#include "exception.h"
-#include "utils.h"
-
-#include <cstring>
-
-namespace AudioGrapher {
-
-template<typename T>
-class SilenceTrimmer : public ListedSource<T>, public Sink<T>
-{
- public:
-
- SilenceTrimmer()
- {
- reset ();
- }
-
- void reset()
- {
- in_beginning = true;
- in_end = false;
- trim_beginning = false;
- trim_end = false;
- silence_frames = 0;
- max_output_frames = 0;
- add_to_beginning = 0;
- add_to_end = 0;
- }
-
- void add_silence_to_beginning (nframes_t frames_per_channel)
- {
- if (!in_beginning) {
- throw Exception(*this, "Tried to add silence to beginning after already outputting data");
- }
- add_to_beginning = frames_per_channel;
- }
-
- void add_silence_to_end (nframes_t frames_per_channel)
- {
- if (in_end) {
- throw Exception(*this, "Tried to add silence to end after already reaching end");
- }
- add_to_end = frames_per_channel;
- }
-
- void set_trim_beginning (bool yn)
- {
- if (!in_beginning) {
- throw Exception(*this, "Tried to set beginning trim after already outputting data");
- }
- trim_beginning = yn;
- }
-
- void set_trim_end (bool yn)
- {
- if (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;
- }
-
- void process (ProcessContext<T> const & c)
- {
- if (in_end) { throw Exception(*this, "process() after reacing end of input"); }
- in_end = c.has_flag (ProcessContext<T>::EndOfInput);
-
- nframes_t frame_index = 0;
-
- if (in_beginning) {
-
- bool has_data = true;
-
- // only check silence if doing either of these
- // This will set both has_data and frame_index
- if (add_to_beginning || trim_beginning) {
- has_data = find_first_non_zero_sample (c, frame_index);
- }
-
- // Added silence if there is silence to add
- if (add_to_beginning) {
- ConstProcessContext<T> c_copy (c);
- if (has_data) { // There will be more output, so remove flag
- c_copy().remove_flag (ProcessContext<T>::EndOfInput);
- }
- add_to_beginning *= c.channels();
- output_silence_frames (c_copy, add_to_beginning);
- }
-
- // If we are not trimming the beginning, output everything
- // Then has_data = true and frame_index = 0
- // Otherwise these reflect the silence state
- if (has_data) {
- in_beginning = false;
- ConstProcessContext<T> c_out (c, &c.data()[frame_index], c.frames() - frame_index);
- ListedSource<T>::output (c_out);
- }
-
- } else if (trim_end) { // Only check zero samples if trimming end
-
- if (find_first_non_zero_sample (c, frame_index)) {
- // 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
- silence_frames += c.frames();
- }
-
- } else { // no need to do anything special
-
- ListedSource<T>::output (c);
- }
-
- // Finally if in end, add silence to end
- if (in_end && add_to_end) {
- add_to_end *= c.channels();
- output_silence_frames (c, add_to_end, true);
- }
- }
-
- using Sink<T>::process;
-
- private:
-
- bool find_first_non_zero_sample (ProcessContext<T> const & c, nframes_t & result_frame)
- {
- for (nframes_t i = 0; i < c.frames(); ++i) {
- if (c.data()[i] != static_cast<T>(0.0)) {
- result_frame = i;
- // Round down to nearest interleaved "frame" beginning
- result_frame -= result_frame % c.channels();
- return true;
- }
- }
- return false;
- }
-
- 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);
-
- while (total_frames > 0) {
- nframes_t frames = std::min (silence_buffer_size, total_frames);
- if (max_output_frames) {
- frames = std::min (frames, max_output_frames);
- }
- frames -= frames % c.channels();
-
- total_frames -= frames;
- ConstProcessContext<T> c_out (c, Utils::get_zeros<T>(frames), frames);
-
- // boolean commentation :)
- bool const no_more_silence_will_be_added = adding_to_end || (add_to_end == 0);
- bool const is_last_frame_output_in_this_function = (total_frames == 0);
- if (end_of_input && no_more_silence_will_be_added && is_last_frame_output_in_this_function) {
- c_out().set_flag (ProcessContext<T>::EndOfInput);
- }
- ListedSource<T>::output (c_out);
- }
- }
-
-
- bool in_beginning;
- bool in_end;
-
- bool trim_beginning;
- bool trim_end;
-
- nframes_t silence_frames;
- nframes_t max_output_frames;
-
- nframes_t add_to_beginning;
- nframes_t add_to_end;
-};
-
-} // namespace
-
-#endif // AUDIOGRAPHER_SILENCE_TRIMMER_H
namespace AudioGrapher
{
+/** A sink for data
+ * This is a pure virtual interface for all data sinks in AudioGrapher
+ */
template <typename T>
class Sink {
public:
--- /dev/null
+#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
\ No newline at end of file
--- /dev/null
+#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
\ No newline at end of file
--- /dev/null
+#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
\ No newline at end of file
--- /dev/null
+#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
\ No newline at end of file
--- /dev/null
+#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
\ No newline at end of file
+++ /dev/null
-#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
\ No newline at end of file
+++ /dev/null
-#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
\ No newline at end of file
+++ /dev/null
-#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
\ No newline at end of file
namespace AudioGrapher
{
+/** A source for data
+ * This is a pure virtual interface for all data sources in AudioGrapher
+ */
template<typename T>
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;
};
+++ /dev/null
-#ifndef AUDIOGRAPHER_SR_CONVERTER_H
-#define AUDIOGRAPHER_SR_CONVERTER_H
-
-#include <samplerate.h>
-
-#include "debuggable.h"
-#include "listed_source.h"
-#include "sink.h"
-#include "throwing.h"
-#include "types.h"
-
-namespace AudioGrapher
-{
-
-class SampleRateConverter
- : public ListedSource<float>
- , public Sink<float>
- , public Debuggable<>
- , public Throwing<>
-{
- public:
- SampleRateConverter (uint32_t channels);
- ~SampleRateConverter ();
-
- // 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
- nframes_t allocate_buffers (nframes_t max_frames);
-
- // could be RT safe (check libsamplerate to be sure)
- void process (ProcessContext<float> const & c);
- using Sink<float>::process;
-
- private:
-
- void set_end_of_input (ProcessContext<float> const & c);
- void reset ();
-
- bool active;
- uint32_t channels;
- nframes_t max_frames_in;
-
- float * leftover_data;
- nframes_t leftover_frames;
- nframes_t max_leftover_frames;
-
- float * data_out;
- nframes_t data_out_size;
-
- SRC_DATA src_data;
- SRC_STATE* src_state;
-};
-
-} // namespace
-
-#endif // AUDIOGRAPHER_SR_CONVERTER_H
+++ /dev/null
-#ifndef AUDIOGRAPHER_THREADER_H
-#define AUDIOGRAPHER_THREADER_H
-
-#include <glibmm/threadpool.h>
-#include <sigc++/slot.h>
-#include <boost/format.hpp>
-
-#include <glib.h>
-#include <vector>
-#include <algorithm>
-
-#include "source.h"
-#include "sink.h"
-#include "exception.h"
-
-namespace AudioGrapher
-{
-
-class ThreaderException : public Exception
-{
- public:
- template<typename T>
- ThreaderException (T const & thrower, std::exception const & e)
- : Exception (thrower,
- boost::str ( boost::format
- ("\n\t- Dynamic type: %1%\n\t- what(): %2%")
- % DebugUtils::demangled_name (e) % e.what() ))
- { }
-};
-
-template <typename T>
-class Threader : public Source<T>, public Sink<T>
-{
- private:
- typedef std::vector<typename Source<T>::SinkPtr> OutputVec;
-
- public:
-
- Threader (Glib::ThreadPool & thread_pool, long wait_timeout_milliseconds = 1000)
- : thread_pool (thread_pool)
- , readers (0)
- , wait_timeout (wait_timeout_milliseconds)
- { }
-
- virtual ~Threader () {}
-
- void add_output (typename Source<T>::SinkPtr output) { outputs.push_back (output); }
- void clear_outputs () { outputs.clear (); }
- 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 */
- void process (ProcessContext<T> const & c)
- {
- wait_mutex.lock();
-
- exception.reset();
-
- unsigned int outs = outputs.size();
- g_atomic_int_add (&readers, outs);
- for (unsigned int i = 0; i < outs; ++i) {
- thread_pool.push (sigc::bind (sigc::mem_fun (this, &Threader::process_output), c, i));
- }
-
- wait();
- }
-
- using Sink<T>::process;
-
- private:
-
- void wait()
- {
- Glib::TimeVal wait_time;
- wait_time.assign_current_time();
- wait_time.add_milliseconds(wait_timeout);
-
- wait_cond.timed_wait(wait_mutex, wait_time);
- bool timed_out = (g_atomic_int_get (&readers) != 0);
- wait_mutex.unlock();
- if (timed_out) { throw Exception (*this, "wait timed out"); }
-
- if (exception) {
- throw *exception;
- }
- }
-
- void process_output(ProcessContext<T> const & c, unsigned int output)
- {
- try {
- outputs[output]->process (c);
- } catch (std::exception const & e) {
- // Only first exception will be passed on
- exception_mutex.lock();
- if(!exception) { exception.reset (new ThreaderException (*this, e)); }
- exception_mutex.unlock();
- }
-
- if (g_atomic_int_dec_and_test (&readers)) {
- wait_cond.signal();
- }
- }
-
- OutputVec outputs;
-
- Glib::ThreadPool & thread_pool;
- Glib::Mutex wait_mutex;
- Glib::Cond wait_cond;
- gint readers;
- long wait_timeout;
-
- Glib::Mutex exception_mutex;
- boost::shared_ptr<ThreaderException> exception;
-
-};
-
-} // namespace
-
-#endif //AUDIOGRAPHER_THREADER_H
\ No newline at end of file
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
{
+++ /dev/null
-#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
\ No newline at end of file
#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>
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);
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:
-
};
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__
\ No newline at end of file
+#endif // AUDIOGRAPHER_TYPES_H
\ No newline at end of file
+++ /dev/null
-#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
--- /dev/null
+#ifndef AUDIOGRAPHER_IDENTITY_VERTEX_H
+#define AUDIOGRAPHER_IDENTITY_VERTEX_H
+
+#include "audiographer/utils/listed_source.h"
+#include "audiographer/sink.h"
+
+namespace AudioGrapher
+{
+
+/// Outputs its input directly to a number of Sinks
+template<typename T = DefaultSampleType>
+class IdentityVertex : public ListedSource<T>, Sink<T>
+{
+ public:
+ void process (ProcessContext<T> const & c) { ListedSource<T>::output(c); }
+ void process (ProcessContext<T> & c) { ListedSource<T>::output(c); }
+};
+
+
+} // namespace
+
+#endif // AUDIOGRAPHER_IDENTITY_VERTEX_H
--- /dev/null
+#ifndef AUDIOGRAPHER_LISTED_SOURCE_H
+#define AUDIOGRAPHER_LISTED_SOURCE_H
+
+#include "audiographer/types.h"
+#include "audiographer/source.h"
+
+#include <list>
+
+namespace AudioGrapher
+{
+
+/// An generic \a Source that uses a \a std::list for managing outputs
+template<typename T = DefaultSampleType>
+class ListedSource : public Source<T>
+{
+ public:
+ void add_output (typename Source<T>::SinkPtr output) { outputs.push_back(output); }
+ void clear_outputs () { outputs.clear(); }
+ void remove_output (typename Source<T>::SinkPtr output) { outputs.remove(output); }
+
+ protected:
+
+ typedef std::list<typename Source<T>::SinkPtr> SinkList;
+
+ /// Helper for derived classes
+ void output (ProcessContext<T> const & c)
+ {
+ for (typename SinkList::iterator i = outputs.begin(); i != outputs.end(); ++i) {
+ (*i)->process (c);
+ }
+ }
+
+ void output (ProcessContext<T> & c)
+ {
+ if (output_size_is_one()) {
+ // only one output, so we can keep this non-const
+ outputs.front()->process (c);
+ } else {
+ output (const_cast<ProcessContext<T> const &> (c));
+ }
+ }
+
+ inline bool output_size_is_one () { return (!outputs.empty() && ++outputs.begin() == outputs.end()); }
+
+ SinkList outputs;
+};
+
+} // namespace
+
+#endif //AUDIOGRAPHER_LISTED_SOURCE_H
+
--- /dev/null
+
+#---------------------------------------------------------------------------
+# 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
--- /dev/null
+/** @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.).
+
+*/
--- /dev/null
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "gdither_types_internal.h"
+#include "gdither.h"
+#include "noise.h"
+
+/* this monstrosity is necessary to get access to lrintf() and random().
+ whoever is writing the glibc headers <cmath> and <cstdlib> should be
+ hauled off to a programmer re-education camp. for the rest of
+ their natural lives. or longer. <paul@linuxaudiosystems.com>
+*/
+
+#define _ISOC9X_SOURCE 1
+#define _ISOC99_SOURCE 1
+#ifdef __cplusplus
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+#undef __USE_SVID
+#define __USE_SVID 1
+#ifdef __cplusplus
+#include <cstdlib>
+#else
+#include <stdlib.h>
+#endif
+
+#include <sys/types.h>
+
+/* Lipshitz's minimally audible FIR, only really works for 46kHz-ish signals */
+static const float shaped_bs[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f };
+
+/* Some useful constants */
+#define MAX_U8 255
+#define MIN_U8 0
+#define SCALE_U8 128.0f
+
+#define MAX_S16 32767
+#define MIN_S16 -32768
+#define SCALE_S16 32768.0f
+
+#define MAX_S24 8388607
+#define MIN_S24 -8388608
+#define SCALE_S24 8388608.0f
+
+GDither gdither_new(GDitherType type, uint32_t channels,
+
+ GDitherSize bit_depth, int dither_depth)
+{
+ GDither s;
+
+ s = (GDither)calloc(1, sizeof(struct GDither_s));
+ s->type = type;
+ s->channels = channels;
+ s->bit_depth = (int)bit_depth;
+
+ if (dither_depth <= 0 || dither_depth > (int)bit_depth) {
+ dither_depth = (int)bit_depth;
+ }
+ s->dither_depth = dither_depth;
+
+ s->scale = (float)(1LL << (dither_depth - 1));
+ if (bit_depth == GDitherFloat || bit_depth == GDitherDouble) {
+ s->post_scale_fp = 1.0f / s->scale;
+ s->post_scale = 0;
+ } else {
+ s->post_scale_fp = 0.0f;
+ s->post_scale = 1 << ((int)bit_depth - dither_depth);
+ }
+
+ switch (bit_depth) {
+ case GDither8bit:
+ /* Unsigned 8 bit */
+ s->bias = 1.0f;
+ s->clamp_u = 255;
+ s->clamp_l = 0;
+ break;
+ case GDither16bit:
+ /* Signed 16 bit */
+ s->bias = 0.0f;
+ s->clamp_u = 32767;
+ s->clamp_l = -32768;
+ break;
+ case GDither32bit:
+ /* Signed 24 bit, in upper 24 bits of 32 bit word */
+ s->bias = 0.0f;
+ s->clamp_u = 8388607;
+ s->clamp_l = -8388608;
+ break;
+ case GDitherFloat:
+ /* normalised float */
+ s->bias = 0.0f;
+ s->clamp_u = lrintf(s->scale);
+ s->clamp_l = lrintf(-s->scale);
+ break;
+ case GDitherDouble:
+ /* normalised float */
+ s->bias = 0.0f;
+ s->clamp_u = lrintf(s->scale);
+ s->clamp_l = lrintf(-s->scale);
+ break;
+ case 23:
+ /* special performance test case */
+ s->scale = SCALE_S24;
+ s->post_scale = 256;
+ s->bias = 0.0f;
+ s->clamp_u = 8388607;
+ s->clamp_l = -8388608;
+ break;
+ default:
+ /* Not a bit depth we can handle */
+ free(s);
+
+ return NULL;
+ break;
+ }
+
+ switch (type) {
+ case GDitherNone:
+ case GDitherRect:
+ /* No state */
+ break;
+
+ case GDitherTri:
+ /* The last whitenoise sample */
+ s->tri_state = (float *) calloc(channels, sizeof(float));
+ break;
+
+ case GDitherShaped:
+ /* The error from the last few samples encoded */
+ s->shaped_state = (GDitherShapedState*)
+ calloc(channels, sizeof(GDitherShapedState));
+ break;
+ }
+
+ return s;
+}
+
+void gdither_free(GDither s)
+{
+ if (s) {
+ free(s->tri_state);
+ free(s->shaped_state);
+ free(s);
+ }
+}
+
+inline static void gdither_innner_loop(const GDitherType dt,
+ const uint32_t stride, const float bias, const float scale,
+
+ const uint32_t post_scale, const int bit_depth,
+ const uint32_t channel, const uint32_t length, float *ts,
+
+ GDitherShapedState *ss, float const *x, void *y, const int clamp_u,
+
+ const int clamp_l)
+{
+ uint32_t pos, i;
+ uint8_t *o8 = (uint8_t*) y;
+ int16_t *o16 = (int16_t*) y;
+ int32_t *o32 = (int32_t*) y;
+ float tmp, r, ideal;
+ int64_t clamped;
+
+ i = channel;
+ for (pos = 0; pos < length; pos++, i += stride) {
+ tmp = x[i] * scale + bias;
+
+ switch (dt) {
+ case GDitherNone:
+ break;
+ case GDitherRect:
+ tmp -= GDITHER_NOISE;
+ break;
+ case GDitherTri:
+ r = GDITHER_NOISE - 0.5f;
+ tmp -= r - ts[channel];
+ ts[channel] = r;
+ break;
+ case GDitherShaped:
+ /* Save raw value for error calculations */
+ ideal = tmp;
+
+ /* Run FIR and add white noise */
+ ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
+ tmp += ss->buffer[ss->phase] * shaped_bs[0]
+ + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[1]
+ + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[2]
+ + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[3]
+ + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[4];
+
+ /* Roll buffer and store last error */
+ ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
+ ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
+ break;
+ }
+
+ clamped = lrintf(tmp);
+ if (clamped > clamp_u) {
+ clamped = clamp_u;
+ } else if (clamped < clamp_l) {
+ clamped = clamp_l;
+ }
+
+ switch (bit_depth) {
+ case GDither8bit:
+ o8[i] = (u_int8_t) (clamped * post_scale);
+ break;
+ case GDither16bit:
+ o16[i] = (int16_t) (clamped * post_scale);
+ break;
+ case GDither32bit:
+ o32[i] = (int32_t) (clamped * post_scale);
+ break;
+ }
+ }
+}
+
+/* floating pint version of the inner loop function */
+inline static void gdither_innner_loop_fp(const GDitherType dt,
+ const uint32_t stride, const float bias, const float scale,
+
+ const float post_scale, const int bit_depth,
+ const uint32_t channel, const uint32_t length, float *ts,
+
+ GDitherShapedState *ss, float const *x, void *y, const int clamp_u,
+
+ const int clamp_l)
+{
+ uint32_t pos, i;
+ float *oflt = (float*) y;
+ double *odbl = (double*) y;
+ float tmp, r, ideal;
+ double clamped;
+
+ i = channel;
+ for (pos = 0; pos < length; pos++, i += stride) {
+ tmp = x[i] * scale + bias;
+
+ switch (dt) {
+ case GDitherNone:
+ break;
+ case GDitherRect:
+ tmp -= GDITHER_NOISE;
+ break;
+ case GDitherTri:
+ r = GDITHER_NOISE - 0.5f;
+ tmp -= r - ts[channel];
+ ts[channel] = r;
+ break;
+ case GDitherShaped:
+ /* Save raw value for error calculations */
+ ideal = tmp;
+
+ /* Run FIR and add white noise */
+ ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
+ tmp += ss->buffer[ss->phase] * shaped_bs[0]
+ + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[1]
+ + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[2]
+ + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[3]
+ + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[4];
+
+ /* Roll buffer and store last error */
+ ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
+ ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
+ break;
+ }
+
+ clamped = rintf(tmp);
+ if (clamped > clamp_u) {
+ clamped = clamp_u;
+ } else if (clamped < clamp_l) {
+ clamped = clamp_l;
+ }
+
+ switch (bit_depth) {
+ case GDitherFloat:
+ oflt[i] = (float) (clamped * post_scale);
+ break;
+ case GDitherDouble:
+ odbl[i] = (double) (clamped * post_scale);
+ break;
+ }
+ }
+}
+
+#define GDITHER_CONV_BLOCK 512
+
+void gdither_run(GDither s, uint32_t channel, uint32_t length,
+ double const *x, void *y)
+{
+ float conv[GDITHER_CONV_BLOCK];
+ uint32_t i, pos;
+ char *ycast = (char *)y;
+
+ int step;
+
+ switch (s->bit_depth) {
+ case GDither8bit:
+ step = 1;
+ break;
+ case GDither16bit:
+ step = 2;
+ break;
+ case GDither32bit:
+ case GDitherFloat:
+ step = 4;
+ break;
+ case GDitherDouble:
+ step = 8;
+ break;
+ default:
+ step = 0;
+ break;
+ }
+
+ pos = 0;
+ while (pos < length) {
+ for (i=0; (i + pos) < length && i < GDITHER_CONV_BLOCK; i++) {
+ conv[i] = x[pos + i];
+ }
+ gdither_runf(s, channel, i, conv, ycast + s->channels * step);
+ pos += i;
+ }
+}
+
+void gdither_runf(GDither s, uint32_t channel, uint32_t length,
+ float const *x, void *y)
+{
+ uint32_t pos, i;
+ float tmp;
+ int64_t clamped;
+ GDitherShapedState *ss = NULL;
+
+ if (!s || channel >= s->channels) {
+ return;
+ }
+
+ if (s->shaped_state) {
+ ss = s->shaped_state + channel;
+ }
+
+ if (s->type == GDitherNone && s->bit_depth == 23) {
+ int32_t *o32 = (int32_t*) y;
+
+ for (pos = 0; pos < length; pos++) {
+ i = channel + (pos * s->channels);
+ tmp = x[i] * 8388608.0f;
+
+ clamped = lrintf(tmp);
+ if (clamped > 8388607) {
+ clamped = 8388607;
+ } else if (clamped < -8388608) {
+ clamped = -8388608;
+ }
+
+ o32[i] = (int32_t) (clamped * 256);
+ }
+
+ return;
+ }
+
+ /* some common case handling code - looks a bit wierd, but it allows
+ * the compiler to optimise out the branches in the inner loop */
+ if (s->bit_depth == 8 && s->dither_depth == 8) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL, NULL, x, y,
+ MAX_U8, MIN_U8);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL, NULL, x, y,
+ MAX_U8, MIN_U8);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, s->tri_state,
+ NULL, x, y, MAX_U8, MIN_U8);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL,
+ ss, x, y, MAX_U8, MIN_U8);
+ break;
+ }
+ } else if (s->bit_depth == 16 && s->dither_depth == 16) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, NULL, NULL, x, y,
+ MAX_S16, MIN_S16);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, NULL, NULL, x, y,
+ MAX_S16, MIN_S16);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, s->tri_state,
+ NULL, x, y, MAX_S16, MIN_S16);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 0.0f,
+ SCALE_S16, 1, 16, channel, length, NULL,
+ ss, x, y, MAX_S16, MIN_S16);
+ break;
+ }
+ } else if (s->bit_depth == 32 && s->dither_depth == 24) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, NULL, NULL, x,
+ y, MAX_S24, MIN_S24);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, NULL, NULL, x,
+ y, MAX_S24, MIN_S24);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, s->tri_state,
+ NULL, x, y, MAX_S24, MIN_S24);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length,
+ NULL, ss, x, y, MAX_S24, MIN_S24);
+ break;
+ }
+ } else if (s->bit_depth == GDitherFloat || s->bit_depth == GDitherDouble) {
+ gdither_innner_loop_fp(s->type, s->channels, s->bias, s->scale,
+ s->post_scale_fp, s->bit_depth, channel, length,
+ s->tri_state, ss, x, y, s->clamp_u, s->clamp_l);
+ } else {
+ /* no special case handling, just process it from the struct */
+
+ gdither_innner_loop(s->type, s->channels, s->bias, s->scale,
+ s->post_scale, s->bit_depth, channel,
+ length, s->tri_state, ss, x, y, s->clamp_u,
+ s->clamp_l);
+ }
+}
+
+/* vi:set ts=8 sts=4 sw=4: */
--- /dev/null
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GDITHER_H
+#define GDITHER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gdither_types.h"
+
+/* Create and initialise a state structure, takes a dither type, a number of
+ * channels and a bit depth as input
+ *
+ * The Dither type is one of
+ *
+ * GDitherNone - straight nearest neighbour rounding. Theres no pressing
+ * reason to do this at 8 or 16 bit, but you might want to at 24, for some
+ * reason. At the lest it will save you writing int->float conversion code,
+ * which is arder than it sounds.
+ *
+ * GDitherRect - mathematically most accurate, lowest noise floor, but not
+ * that good for audio. It is the fastest though.
+ *
+ * GDitherTri - a happy medium between Rectangular and Shaped, reasonable
+ * noise floor, not too obvious, quite fast.
+ *
+ * GDitherShaped - should have the least audible impact, but has the highest
+ * noise floor, fairly CPU intensive. Not advisible if your going to apply
+ * any frequency manipulation afterwards.
+ *
+ * channels, sets the number of channels in the output data, output data will
+ * be written interleaved into the area given to gdither_run(). Set to 1
+ * if you are not working with interleaved buffers.
+ *
+ * bit depth, sets the bit width of the output sample data, it can be one of:
+ *
+ * GDither8bit - 8 bit unsiged
+ * GDither16bit - 16 bit signed
+ * GDither32bit - 24+bits in upper bits of a 32 bit word
+ * GDitherFloat - IEEE floating point (32bits)
+ * GDitherDouble - Double precision IEEE floating point (64bits)
+ *
+ * dither_depth, set the number of bits before the signal will be truncated to,
+ * eg. 16 will produce an output stream with 16bits-worth of signal. Setting to
+ * zero or greater than the width of the output format will dither to the
+ * maximum precision allowed by the output format.
+ */
+GDither gdither_new(GDitherType type, uint32_t channels,
+
+ GDitherSize bit_depth, int dither_depth);
+
+/* Frees memory used by gdither_new.
+ */
+void gdither_free(GDither s);
+
+/* Applies dithering to the supplied signal.
+ *
+ * channel is the channel number you are processing (0 - channles-1), length is
+ * the length of the input, in samples, x is the input samples (float), y is
+ * where the output samples will be written, it should have the approaprate
+ * type for the chosen bit depth
+ */
+void gdither_runf(GDither s, uint32_t channel, uint32_t length,
+ float const *x, void *y);
+
+/* see gdither_runf, vut input argument is double format */
+void gdither_run(GDither s, uint32_t channel, uint32_t length,
+ double const *x, void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GDITHER_TYPES_H
+#define GDITHER_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ GDitherNone = 0,
+ GDitherRect,
+ GDitherTri,
+ GDitherShaped
+} GDitherType;
+
+typedef enum {
+ GDither8bit = 8,
+ GDither16bit = 16,
+ GDither32bit = 32,
+ GDitherFloat = 25,
+ GDitherDouble = 54
+} GDitherSize;
+
+typedef void *GDither;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef GDITHER_TYPES_H
+#define GDITHER_TYPES_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GDITHER_SH_BUF_SIZE 8
+#define GDITHER_SH_BUF_MASK 7
+
+/* this must agree with whats in gdither_types.h */
+typedef enum {
+ GDitherNone = 0,
+ GDitherRect,
+ GDitherTri,
+ GDitherShaped
+} GDitherType;
+
+typedef enum {
+ GDither8bit = 8,
+ GDither16bit = 16,
+ GDither32bit = 32,
+ GDitherFloat = 25,
+ GDitherDouble = 54
+} GDitherSize;
+
+typedef struct {
+ uint32_t phase;
+ float buffer[GDITHER_SH_BUF_SIZE];
+} GDitherShapedState;
+
+typedef struct GDither_s {
+ GDitherType type;
+ uint32_t channels;
+ uint32_t bit_depth;
+ uint32_t dither_depth;
+ float scale;
+ uint32_t post_scale;
+ float post_scale_fp;
+ float bias;
+
+ int clamp_u;
+
+ int clamp_l;
+ float *tri_state;
+ GDitherShapedState *shaped_state;
+} *GDither;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef NOISE_H
+#define NOISE_H
+
+/* Can be overrriden with any code that produces whitenoise between 0.0f and
+ * 1.0f, eg (random() / (float)RAND_MAX) should be a good source of noise, but
+ * its expensive */
+#ifndef GDITHER_NOISE
+#define GDITHER_NOISE gdither_noise()
+#endif
+
+inline static float gdither_noise()
+{
+ static uint32_t rnd = 23232323;
+ rnd = (rnd * 196314165) + 907633515;
+
+ return rnd * 2.3283064365387e-10f;
+}
+
+#endif
--- /dev/null
+/*
+** 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 */
+
--- /dev/null
+#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
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "gdither_types_internal.h"
-#include "gdither.h"
-#include "noise.h"
-
-/* this monstrosity is necessary to get access to lrintf() and random().
- whoever is writing the glibc headers <cmath> and <cstdlib> should be
- hauled off to a programmer re-education camp. for the rest of
- their natural lives. or longer. <paul@linuxaudiosystems.com>
-*/
-
-#define _ISOC9X_SOURCE 1
-#define _ISOC99_SOURCE 1
-#ifdef __cplusplus
-#include <cmath>
-#else
-#include <math.h>
-#endif
-
-#undef __USE_SVID
-#define __USE_SVID 1
-#ifdef __cplusplus
-#include <cstdlib>
-#else
-#include <stdlib.h>
-#endif
-
-#include <sys/types.h>
-
-/* Lipshitz's minimally audible FIR, only really works for 46kHz-ish signals */
-static const float shaped_bs[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f };
-
-/* Some useful constants */
-#define MAX_U8 255
-#define MIN_U8 0
-#define SCALE_U8 128.0f
-
-#define MAX_S16 32767
-#define MIN_S16 -32768
-#define SCALE_S16 32768.0f
-
-#define MAX_S24 8388607
-#define MIN_S24 -8388608
-#define SCALE_S24 8388608.0f
-
-GDither gdither_new(GDitherType type, uint32_t channels,
-
- GDitherSize bit_depth, int dither_depth)
-{
- GDither s;
-
- s = (GDither)calloc(1, sizeof(struct GDither_s));
- s->type = type;
- s->channels = channels;
- s->bit_depth = (int)bit_depth;
-
- if (dither_depth <= 0 || dither_depth > (int)bit_depth) {
- dither_depth = (int)bit_depth;
- }
- s->dither_depth = dither_depth;
-
- s->scale = (float)(1LL << (dither_depth - 1));
- if (bit_depth == GDitherFloat || bit_depth == GDitherDouble) {
- s->post_scale_fp = 1.0f / s->scale;
- s->post_scale = 0;
- } else {
- s->post_scale_fp = 0.0f;
- s->post_scale = 1 << ((int)bit_depth - dither_depth);
- }
-
- switch (bit_depth) {
- case GDither8bit:
- /* Unsigned 8 bit */
- s->bias = 1.0f;
- s->clamp_u = 255;
- s->clamp_l = 0;
- break;
- case GDither16bit:
- /* Signed 16 bit */
- s->bias = 0.0f;
- s->clamp_u = 32767;
- s->clamp_l = -32768;
- break;
- case GDither32bit:
- /* Signed 24 bit, in upper 24 bits of 32 bit word */
- s->bias = 0.0f;
- s->clamp_u = 8388607;
- s->clamp_l = -8388608;
- break;
- case GDitherFloat:
- /* normalised float */
- s->bias = 0.0f;
- s->clamp_u = lrintf(s->scale);
- s->clamp_l = lrintf(-s->scale);
- break;
- case GDitherDouble:
- /* normalised float */
- s->bias = 0.0f;
- s->clamp_u = lrintf(s->scale);
- s->clamp_l = lrintf(-s->scale);
- break;
- case 23:
- /* special performance test case */
- s->scale = SCALE_S24;
- s->post_scale = 256;
- s->bias = 0.0f;
- s->clamp_u = 8388607;
- s->clamp_l = -8388608;
- break;
- default:
- /* Not a bit depth we can handle */
- free(s);
-
- return NULL;
- break;
- }
-
- switch (type) {
- case GDitherNone:
- case GDitherRect:
- /* No state */
- break;
-
- case GDitherTri:
- /* The last whitenoise sample */
- s->tri_state = (float *) calloc(channels, sizeof(float));
- break;
-
- case GDitherShaped:
- /* The error from the last few samples encoded */
- s->shaped_state = (GDitherShapedState*)
- calloc(channels, sizeof(GDitherShapedState));
- break;
- }
-
- return s;
-}
-
-void gdither_free(GDither s)
-{
- if (s) {
- free(s->tri_state);
- free(s->shaped_state);
- free(s);
- }
-}
-
-inline static void gdither_innner_loop(const GDitherType dt,
- const uint32_t stride, const float bias, const float scale,
-
- const uint32_t post_scale, const int bit_depth,
- const uint32_t channel, const uint32_t length, float *ts,
-
- GDitherShapedState *ss, float const *x, void *y, const int clamp_u,
-
- const int clamp_l)
-{
- uint32_t pos, i;
- uint8_t *o8 = (uint8_t*) y;
- int16_t *o16 = (int16_t*) y;
- int32_t *o32 = (int32_t*) y;
- float tmp, r, ideal;
- int64_t clamped;
-
- i = channel;
- for (pos = 0; pos < length; pos++, i += stride) {
- tmp = x[i] * scale + bias;
-
- switch (dt) {
- case GDitherNone:
- break;
- case GDitherRect:
- tmp -= GDITHER_NOISE;
- break;
- case GDitherTri:
- r = GDITHER_NOISE - 0.5f;
- tmp -= r - ts[channel];
- ts[channel] = r;
- break;
- case GDitherShaped:
- /* Save raw value for error calculations */
- ideal = tmp;
-
- /* Run FIR and add white noise */
- ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
- tmp += ss->buffer[ss->phase] * shaped_bs[0]
- + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
- * shaped_bs[1]
- + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
- * shaped_bs[2]
- + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
- * shaped_bs[3]
- + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
- * shaped_bs[4];
-
- /* Roll buffer and store last error */
- ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
- ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
- break;
- }
-
- clamped = lrintf(tmp);
- if (clamped > clamp_u) {
- clamped = clamp_u;
- } else if (clamped < clamp_l) {
- clamped = clamp_l;
- }
-
- switch (bit_depth) {
- case GDither8bit:
- o8[i] = (u_int8_t) (clamped * post_scale);
- break;
- case GDither16bit:
- o16[i] = (int16_t) (clamped * post_scale);
- break;
- case GDither32bit:
- o32[i] = (int32_t) (clamped * post_scale);
- break;
- }
- }
-}
-
-/* floating pint version of the inner loop function */
-inline static void gdither_innner_loop_fp(const GDitherType dt,
- const uint32_t stride, const float bias, const float scale,
-
- const float post_scale, const int bit_depth,
- const uint32_t channel, const uint32_t length, float *ts,
-
- GDitherShapedState *ss, float const *x, void *y, const int clamp_u,
-
- const int clamp_l)
-{
- uint32_t pos, i;
- float *oflt = (float*) y;
- double *odbl = (double*) y;
- float tmp, r, ideal;
- double clamped;
-
- i = channel;
- for (pos = 0; pos < length; pos++, i += stride) {
- tmp = x[i] * scale + bias;
-
- switch (dt) {
- case GDitherNone:
- break;
- case GDitherRect:
- tmp -= GDITHER_NOISE;
- break;
- case GDitherTri:
- r = GDITHER_NOISE - 0.5f;
- tmp -= r - ts[channel];
- ts[channel] = r;
- break;
- case GDitherShaped:
- /* Save raw value for error calculations */
- ideal = tmp;
-
- /* Run FIR and add white noise */
- ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
- tmp += ss->buffer[ss->phase] * shaped_bs[0]
- + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
- * shaped_bs[1]
- + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
- * shaped_bs[2]
- + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
- * shaped_bs[3]
- + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
- * shaped_bs[4];
-
- /* Roll buffer and store last error */
- ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
- ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
- break;
- }
-
- clamped = rintf(tmp);
- if (clamped > clamp_u) {
- clamped = clamp_u;
- } else if (clamped < clamp_l) {
- clamped = clamp_l;
- }
-
- switch (bit_depth) {
- case GDitherFloat:
- oflt[i] = (float) (clamped * post_scale);
- break;
- case GDitherDouble:
- odbl[i] = (double) (clamped * post_scale);
- break;
- }
- }
-}
-
-#define GDITHER_CONV_BLOCK 512
-
-void gdither_run(GDither s, uint32_t channel, uint32_t length,
- double const *x, void *y)
-{
- float conv[GDITHER_CONV_BLOCK];
- uint32_t i, pos;
- char *ycast = (char *)y;
-
- int step;
-
- switch (s->bit_depth) {
- case GDither8bit:
- step = 1;
- break;
- case GDither16bit:
- step = 2;
- break;
- case GDither32bit:
- case GDitherFloat:
- step = 4;
- break;
- case GDitherDouble:
- step = 8;
- break;
- default:
- step = 0;
- break;
- }
-
- pos = 0;
- while (pos < length) {
- for (i=0; (i + pos) < length && i < GDITHER_CONV_BLOCK; i++) {
- conv[i] = x[pos + i];
- }
- gdither_runf(s, channel, i, conv, ycast + s->channels * step);
- pos += i;
- }
-}
-
-void gdither_runf(GDither s, uint32_t channel, uint32_t length,
- float const *x, void *y)
-{
- uint32_t pos, i;
- float tmp;
- int64_t clamped;
- GDitherShapedState *ss = NULL;
-
- if (!s || channel >= s->channels) {
- return;
- }
-
- if (s->shaped_state) {
- ss = s->shaped_state + channel;
- }
-
- if (s->type == GDitherNone && s->bit_depth == 23) {
- int32_t *o32 = (int32_t*) y;
-
- for (pos = 0; pos < length; pos++) {
- i = channel + (pos * s->channels);
- tmp = x[i] * 8388608.0f;
-
- clamped = lrintf(tmp);
- if (clamped > 8388607) {
- clamped = 8388607;
- } else if (clamped < -8388608) {
- clamped = -8388608;
- }
-
- o32[i] = (int32_t) (clamped * 256);
- }
-
- return;
- }
-
- /* some common case handling code - looks a bit wierd, but it allows
- * the compiler to optimise out the branches in the inner loop */
- if (s->bit_depth == 8 && s->dither_depth == 8) {
- switch (s->type) {
- case GDitherNone:
- gdither_innner_loop(GDitherNone, s->channels, 128.0f, SCALE_U8,
- 1, 8, channel, length, NULL, NULL, x, y,
- MAX_U8, MIN_U8);
- break;
- case GDitherRect:
- gdither_innner_loop(GDitherRect, s->channels, 128.0f, SCALE_U8,
- 1, 8, channel, length, NULL, NULL, x, y,
- MAX_U8, MIN_U8);
- break;
- case GDitherTri:
- gdither_innner_loop(GDitherTri, s->channels, 128.0f, SCALE_U8,
- 1, 8, channel, length, s->tri_state,
- NULL, x, y, MAX_U8, MIN_U8);
- break;
- case GDitherShaped:
- gdither_innner_loop(GDitherShaped, s->channels, 128.0f, SCALE_U8,
- 1, 8, channel, length, NULL,
- ss, x, y, MAX_U8, MIN_U8);
- break;
- }
- } else if (s->bit_depth == 16 && s->dither_depth == 16) {
- switch (s->type) {
- case GDitherNone:
- gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S16,
- 1, 16, channel, length, NULL, NULL, x, y,
- MAX_S16, MIN_S16);
- break;
- case GDitherRect:
- gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S16,
- 1, 16, channel, length, NULL, NULL, x, y,
- MAX_S16, MIN_S16);
- break;
- case GDitherTri:
- gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S16,
- 1, 16, channel, length, s->tri_state,
- NULL, x, y, MAX_S16, MIN_S16);
- break;
- case GDitherShaped:
- gdither_innner_loop(GDitherShaped, s->channels, 0.0f,
- SCALE_S16, 1, 16, channel, length, NULL,
- ss, x, y, MAX_S16, MIN_S16);
- break;
- }
- } else if (s->bit_depth == 32 && s->dither_depth == 24) {
- switch (s->type) {
- case GDitherNone:
- gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S24,
- 256, 32, channel, length, NULL, NULL, x,
- y, MAX_S24, MIN_S24);
- break;
- case GDitherRect:
- gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S24,
- 256, 32, channel, length, NULL, NULL, x,
- y, MAX_S24, MIN_S24);
- break;
- case GDitherTri:
- gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S24,
- 256, 32, channel, length, s->tri_state,
- NULL, x, y, MAX_S24, MIN_S24);
- break;
- case GDitherShaped:
- gdither_innner_loop(GDitherShaped, s->channels, 0.0f, SCALE_S24,
- 256, 32, channel, length,
- NULL, ss, x, y, MAX_S24, MIN_S24);
- break;
- }
- } else if (s->bit_depth == GDitherFloat || s->bit_depth == GDitherDouble) {
- gdither_innner_loop_fp(s->type, s->channels, s->bias, s->scale,
- s->post_scale_fp, s->bit_depth, channel, length,
- s->tri_state, ss, x, y, s->clamp_u, s->clamp_l);
- } else {
- /* no special case handling, just process it from the struct */
-
- gdither_innner_loop(s->type, s->channels, s->bias, s->scale,
- s->post_scale, s->bit_depth, channel,
- length, s->tri_state, ss, x, y, s->clamp_u,
- s->clamp_l);
- }
-}
-
-/* vi:set ts=8 sts=4 sw=4: */
+++ /dev/null
-/*
- * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef GDITHER_H
-#define GDITHER_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "gdither_types.h"
-
-/* Create and initialise a state structure, takes a dither type, a number of
- * channels and a bit depth as input
- *
- * The Dither type is one of
- *
- * GDitherNone - straight nearest neighbour rounding. Theres no pressing
- * reason to do this at 8 or 16 bit, but you might want to at 24, for some
- * reason. At the lest it will save you writing int->float conversion code,
- * which is arder than it sounds.
- *
- * GDitherRect - mathematically most accurate, lowest noise floor, but not
- * that good for audio. It is the fastest though.
- *
- * GDitherTri - a happy medium between Rectangular and Shaped, reasonable
- * noise floor, not too obvious, quite fast.
- *
- * GDitherShaped - should have the least audible impact, but has the highest
- * noise floor, fairly CPU intensive. Not advisible if your going to apply
- * any frequency manipulation afterwards.
- *
- * channels, sets the number of channels in the output data, output data will
- * be written interleaved into the area given to gdither_run(). Set to 1
- * if you are not working with interleaved buffers.
- *
- * bit depth, sets the bit width of the output sample data, it can be one of:
- *
- * GDither8bit - 8 bit unsiged
- * GDither16bit - 16 bit signed
- * GDither32bit - 24+bits in upper bits of a 32 bit word
- * GDitherFloat - IEEE floating point (32bits)
- * GDitherDouble - Double precision IEEE floating point (64bits)
- *
- * dither_depth, set the number of bits before the signal will be truncated to,
- * eg. 16 will produce an output stream with 16bits-worth of signal. Setting to
- * zero or greater than the width of the output format will dither to the
- * maximum precision allowed by the output format.
- */
-GDither gdither_new(GDitherType type, uint32_t channels,
-
- GDitherSize bit_depth, int dither_depth);
-
-/* Frees memory used by gdither_new.
- */
-void gdither_free(GDither s);
-
-/* Applies dithering to the supplied signal.
- *
- * channel is the channel number you are processing (0 - channles-1), length is
- * the length of the input, in samples, x is the input samples (float), y is
- * where the output samples will be written, it should have the approaprate
- * type for the chosen bit depth
- */
-void gdither_runf(GDither s, uint32_t channel, uint32_t length,
- float const *x, void *y);
-
-/* see gdither_runf, vut input argument is double format */
-void gdither_run(GDither s, uint32_t channel, uint32_t length,
- double const *x, void *y);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef GDITHER_TYPES_H
-#define GDITHER_TYPES_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- GDitherNone = 0,
- GDitherRect,
- GDitherTri,
- GDitherShaped
-} GDitherType;
-
-typedef enum {
- GDither8bit = 8,
- GDither16bit = 16,
- GDither32bit = 32,
- GDitherFloat = 25,
- GDitherDouble = 54
-} GDitherSize;
-
-typedef void *GDither;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#ifndef GDITHER_TYPES_H
-#define GDITHER_TYPES_H
-
-#include <stdint.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GDITHER_SH_BUF_SIZE 8
-#define GDITHER_SH_BUF_MASK 7
-
-/* this must agree with whats in gdither_types.h */
-typedef enum {
- GDitherNone = 0,
- GDitherRect,
- GDitherTri,
- GDitherShaped
-} GDitherType;
-
-typedef enum {
- GDither8bit = 8,
- GDither16bit = 16,
- GDither32bit = 32,
- GDitherFloat = 25,
- GDitherDouble = 54
-} GDitherSize;
-
-typedef struct {
- uint32_t phase;
- float buffer[GDITHER_SH_BUF_SIZE];
-} GDitherShapedState;
-
-typedef struct GDither_s {
- GDitherType type;
- uint32_t channels;
- uint32_t bit_depth;
- uint32_t dither_depth;
- float scale;
- uint32_t post_scale;
- float post_scale_fp;
- float bias;
-
- int clamp_u;
-
- int clamp_l;
- float *tri_state;
- GDitherShapedState *shaped_state;
-} *GDither;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/*
- Copyright (C) 2000-2007 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef NOISE_H
-#define NOISE_H
-
-/* Can be overrriden with any code that produces whitenoise between 0.0f and
- * 1.0f, eg (random() / (float)RAND_MAX) should be a good source of noise, but
- * its expensive */
-#ifndef GDITHER_NOISE
-#define GDITHER_NOISE gdither_noise()
-#endif
-
-inline static float gdither_noise()
-{
- static uint32_t rnd = 23232323;
- rnd = (rnd * 196314165) + 907633515;
-
- return rnd * 2.3283064365387e-10f;
-}
-
-#endif
--- /dev/null
+#include "audiographer/general/sample_format_converter.h"
+
+#include "audiographer/exception.h"
+#include "audiographer/type_utils.h"
+#include "private/gdither/gdither.h"
+
+#include <boost/format.hpp>
+
+namespace AudioGrapher
+{
+
+template <typename TOut>
+SampleFormatConverter<TOut>::SampleFormatConverter (ChannelCount channels) :
+ channels (channels),
+ dither (0),
+ data_out_size (0),
+ data_out (0),
+ clip_floats (false)
+{
+}
+
+template <>
+void
+SampleFormatConverter<float>::init (nframes_t max_frames, int type, int 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);
+}
+
+template <>
+void
+SampleFormatConverter<int32_t>::init (nframes_t max_frames, int type, int data_width)
+{
+ if(throw_level (ThrowObject) && data_width < 24) {
+ throw Exception (*this, "Trying to use SampleFormatConverter<int32_t> for data widths < 24");
+ }
+
+ init_common (max_frames);
+
+ if (data_width == 24) {
+ dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
+ } else if (data_width == 32) {
+ dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
+ } else if (throw_level (ThrowObject)) {
+ throw Exception (*this, "Unsupported data width");
+ }
+}
+
+template <>
+void
+SampleFormatConverter<int16_t>::init (nframes_t max_frames, int type, int 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);
+}
+
+template <>
+void
+SampleFormatConverter<uint8_t>::init (nframes_t max_frames, int type, int 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);
+}
+
+template <typename TOut>
+void
+SampleFormatConverter<TOut>::init_common (nframes_t max_frames )
+{
+ reset();
+ if (max_frames > data_out_size) {
+
+ delete[] data_out;
+
+ data_out = new TOut[max_frames];
+ data_out_size = max_frames;
+ }
+}
+
+template <typename TOut>
+SampleFormatConverter<TOut>::~SampleFormatConverter ()
+{
+ reset();
+}
+
+template <typename TOut>
+void
+SampleFormatConverter<TOut>::reset()
+{
+ if (dither) {
+ gdither_free (dither);
+ dither = 0;
+ }
+
+ delete[] data_out;
+ data_out_size = 0;
+ data_out = 0;
+
+ clip_floats = false;
+}
+
+/* Basic const version of process() */
+template <typename TOut>
+void
+SampleFormatConverter<TOut>::process (ProcessContext<float> const & c_in)
+{
+ float const * const data = c_in.data();
+
+ check_frame_and_channel_count (c_in.frames (), c_in.channels ());
+
+ /* Do conversion */
+
+ for (uint32_t chn = 0; chn < c_in.channels(); ++chn) {
+ gdither_runf (dither, chn, c_in.frames_per_channel (), data, data_out);
+ }
+
+ /* Write forward */
+
+ ProcessContext<TOut> c_out(c_in, data_out);
+ output (c_out);
+}
+
+/* Basic non-const version of process(), calls the const one */
+template<typename TOut>
+void
+SampleFormatConverter<TOut>::process (ProcessContext<float> & c_in)
+{
+ process (static_cast<ProcessContext<float> const &> (c_in));
+}
+
+/* template specialization for float, in-place processing (non-const) */
+template<>
+void
+SampleFormatConverter<float>::process (ProcessContext<float> & c_in)
+{
+ nframes_t frames = c_in.frames();
+ float * data = c_in.data();
+
+ if (clip_floats) {
+ for (nframes_t x = 0; x < frames; ++x) {
+ if (data[x] > 1.0f) {
+ data[x] = 1.0f;
+ } else if (data[x] < -1.0f) {
+ data[x] = -1.0f;
+ }
+ }
+ }
+
+ output (c_in);
+}
+
+/* template specialized const version, copies the data, and calls the non-const version */
+template<>
+void
+SampleFormatConverter<float>::process (ProcessContext<float> const & c_in)
+{
+ // Make copy of data and pass it to non-const version
+ 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);
+}
+
+template<typename TOut>
+void
+SampleFormatConverter<TOut>::check_frame_and_channel_count(nframes_t frames, ChannelCount 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 (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));
+ }
+}
+
+template class SampleFormatConverter<uint8_t>;
+template class SampleFormatConverter<int16_t>;
+template class SampleFormatConverter<int32_t>;
+template class SampleFormatConverter<float>;
+
+} // namespace
--- /dev/null
+#include "audiographer/general/sr_converter.h"
+
+#include "audiographer/exception.h"
+#include "audiographer/type_utils.h"
+
+#include <cmath>
+#include <boost/format.hpp>
+
+namespace AudioGrapher
+{
+using boost::format;
+using boost::str;
+
+SampleRateConverter::SampleRateConverter (uint32_t channels)
+ : active (false)
+ , channels (channels)
+ , max_frames_in(0)
+ , leftover_data (0)
+ , leftover_frames (0)
+ , max_leftover_frames (0)
+ , data_out (0)
+ , data_out_size (0)
+ , src_state (0)
+{
+ add_supported_flag (ProcessContext<>::EndOfInput);
+}
+
+void
+SampleRateConverter::init (nframes_t in_rate, nframes_t out_rate, int quality)
+{
+ reset();
+
+ if (in_rate == out_rate) {
+ src_data.src_ratio = 1;
+ return;
+ }
+
+ active = true;
+ int err;
+ src_state = src_new (quality, channels, &err);
+ if (throw_level (ThrowObject) && !src_state) {
+ throw Exception (*this, str (format
+ ("Cannot initialize sample rate converter: %1%")
+ % src_strerror (err)));
+ }
+
+ src_data.src_ratio = (double) out_rate / (double) in_rate;
+}
+
+SampleRateConverter::~SampleRateConverter ()
+{
+ reset();
+}
+
+nframes_t
+SampleRateConverter::allocate_buffers (nframes_t max_frames)
+{
+ if (!active) { return max_frames; }
+
+ nframes_t max_frames_out = (nframes_t) ceil (max_frames * src_data.src_ratio);
+ if (data_out_size < max_frames_out) {
+
+ delete[] data_out;
+ data_out = new float[max_frames_out];
+ src_data.data_out = data_out;
+
+ max_leftover_frames = 4 * max_frames;
+ leftover_data = (float *) realloc (leftover_data, max_leftover_frames * sizeof (float));
+ if (throw_level (ThrowObject) && !leftover_data) {
+ throw Exception (*this, "A memory allocation error occured");
+ }
+
+ max_frames_in = max_frames;
+ data_out_size = max_frames_out;
+ }
+
+ return max_frames_out;
+}
+
+void
+SampleRateConverter::process (ProcessContext<float> const & c)
+{
+ check_flags (*this, c);
+
+ if (!active) {
+ output (c);
+ return;
+ }
+
+ nframes_t frames = c.frames();
+ float * in = const_cast<float *> (c.data()); // TODO check if this is safe!
+
+ 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));
+ }
+
+ int err;
+ bool first_time = true;
+
+ do {
+ src_data.output_frames = data_out_size / channels;
+ src_data.data_out = data_out;
+
+ if (leftover_frames > 0) {
+
+ /* input data will be in leftover_data rather than data_in */
+
+ src_data.data_in = leftover_data;
+
+ if (first_time) {
+
+ /* first time, append new data from data_in into the leftover_data buffer */
+
+ TypeUtils<float>::copy (&leftover_data [leftover_frames * channels], in, frames);
+ src_data.input_frames = frames + leftover_frames;
+ } else {
+
+ /* otherwise, just use whatever is still left in leftover_data; the contents
+ were adjusted using memmove() right after the last SRC call (see
+ below)
+ */
+
+ src_data.input_frames = leftover_frames;
+ }
+
+ } else {
+ src_data.data_in = in;
+ src_data.input_frames = frames / channels;
+ }
+
+ first_time = false;
+
+ if (debug_level (DebugVerbose)) {
+ debug_stream() << "data_in: " << src_data.data_in <<
+ ", input_frames: " << src_data.input_frames <<
+ ", data_out: " << src_data.data_out <<
+ ", output_frames: " << src_data.output_frames << std::endl;
+ }
+
+ err = src_process (src_state, &src_data);
+ if (throw_level (ThrowProcess) && err) {
+ throw Exception (*this, str (format
+ ("An error occured during sample rate conversion: %1%")
+ % src_strerror (err)));
+ }
+
+ leftover_frames = src_data.input_frames - src_data.input_frames_used;
+
+ if (leftover_frames > 0) {
+ if (throw_level (ThrowProcess) && leftover_frames > max_leftover_frames) {
+ throw Exception(*this, "leftover frames overflowed");
+ }
+ TypeUtils<float>::move (&src_data.data_in[src_data.input_frames_used * channels],
+ leftover_data, leftover_frames * channels);
+ }
+
+ ProcessContext<float> c_out (c, data_out, src_data.output_frames_gen * channels);
+ if (!src_data.end_of_input || leftover_frames) {
+ c_out.remove_flag (ProcessContext<float>::EndOfInput);
+ }
+ output (c_out);
+
+ if (debug_level (DebugProcess)) {
+ debug_stream() <<
+ "src_data.output_frames_gen: " << src_data.output_frames_gen <<
+ ", leftover_frames: " << leftover_frames << std::endl;
+ }
+
+ if (throw_level (ThrowProcess) && src_data.output_frames_gen == 0 && leftover_frames) {
+ throw Exception (*this, boost::str (boost::format
+ ("No output frames genereated with %1% leftover frames")
+ % leftover_frames));
+ }
+
+ } while (leftover_frames > frames);
+
+ // src_data.end_of_input has to be checked to prevent infinite recursion
+ if (!src_data.end_of_input && c.has_flag(ProcessContext<float>::EndOfInput)) {
+ set_end_of_input (c);
+ }
+}
+
+void SampleRateConverter::set_end_of_input (ProcessContext<float> const & c)
+{
+ src_data.end_of_input = true;
+
+ float f;
+ ProcessContext<float> const dummy (c, &f, 0, channels);
+
+ /* No idea why this has to be done twice for all data to be written,
+ * but that just seems to be the way it is...
+ */
+ dummy.remove_flag (ProcessContext<float>::EndOfInput);
+ process (dummy);
+ dummy.set_flag (ProcessContext<float>::EndOfInput);
+ process (dummy);
+}
+
+
+void SampleRateConverter::reset ()
+{
+ active = false;
+ max_frames_in = 0;
+ src_data.end_of_input = false;
+
+ if (src_state) {
+ src_delete (src_state);
+ }
+
+ leftover_frames = 0;
+ max_leftover_frames = 0;
+ if (leftover_data) {
+ free (leftover_data);
+ }
+
+ data_out_size = 0;
+ delete [] data_out;
+ data_out = 0;
+}
+
+} // namespace
+++ /dev/null
-#include "audiographer/sample_format_converter.h"
-
-#include "gdither/gdither.h"
-#include "audiographer/exception.h"
-
-#include <boost/format.hpp>
-
-#include <cstring>
-
-namespace AudioGrapher
-{
-
-template <typename TOut>
-SampleFormatConverter<TOut>::SampleFormatConverter (uint32_t channels) :
- channels (channels),
- dither (0),
- data_out_size (0),
- data_out (0),
- clip_floats (false)
-{
-}
-
-template <>
-void
-SampleFormatConverter<float>::init (nframes_t max_frames, int type, int data_width)
-{
- if (data_width != 32) { throw Exception (*this, "Unsupported data width"); }
- init_common (max_frames);
- dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
-}
-
-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"); }
-
- init_common (max_frames);
-
- if (data_width == 24) {
- dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width);
- } else if (data_width == 32) {
- dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width);
- } else {
- throw Exception (*this, "Unsupported data width");
- }
-}
-
-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"); }
- init_common (max_frames);
- dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width);
-}
-
-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"); }
- init_common (max_frames);
- dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width);
-}
-
-template <typename TOut>
-void
-SampleFormatConverter<TOut>::init_common (nframes_t max_frames )
-{
- reset();
- if (max_frames > data_out_size) {
-
- delete[] data_out;
-
- data_out = new TOut[max_frames];
- data_out_size = max_frames;
- }
-}
-
-template <typename TOut>
-SampleFormatConverter<TOut>::~SampleFormatConverter ()
-{
- reset();
-}
-
-template <typename TOut>
-void
-SampleFormatConverter<TOut>::reset()
-{
- if (dither) {
- gdither_free (dither);
- dither = 0;
- }
-
- delete[] data_out;
- data_out_size = 0;
- data_out = 0;
-
- clip_floats = false;
-}
-
-/* Basic const version of process() */
-template <typename TOut>
-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);
-
- /* Do conversion */
-
- for (uint32_t chn = 0; chn < channels; ++chn) {
- gdither_runf (dither, chn, frames / channels, data, data_out);
- }
-
- /* Write forward */
-
- ProcessContext<TOut> c_out(c_in, data_out);
- output (c_out);
-}
-
-/* Basic non-const version of process(), calls the const one */
-template<typename TOut>
-void
-SampleFormatConverter<TOut>::process (ProcessContext<float> & c_in)
-{
- process (static_cast<ProcessContext<float> const &> (c_in));
-}
-
-/* template specialization for float, in-place processing (non-const) */
-template<>
-void
-SampleFormatConverter<float>::process (ProcessContext<float> & c_in)
-{
- nframes_t frames = c_in.frames();
- float * data = c_in.data();
-
- if (clip_floats) {
- for (nframes_t x = 0; x < frames; ++x) {
- if (data[x] > 1.0f) {
- data[x] = 1.0f;
- } else if (data[x] < -1.0f) {
- data[x] = -1.0f;
- }
- }
- }
-
- output (c_in);
-}
-
-/* template specialized const version, copies the data, and calls the non-const version */
-template<>
-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));
-
- ProcessContext<float> c (c_in, data_out);
- process (c);
-}
-
-template<typename TOut>
-void
-SampleFormatConverter<TOut>::check_frame_count(nframes_t frames)
-{
- 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 (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));
- }
-}
-
-template class SampleFormatConverter<uint8_t>;
-template class SampleFormatConverter<int16_t>;
-template class SampleFormatConverter<int32_t>;
-template class SampleFormatConverter<float>;
-
-} // namespace
+++ /dev/null
-#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
+++ /dev/null
-#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
\ No newline at end of file
+++ /dev/null
-#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
+++ /dev/null
-#include "audiographer/sr_converter.h"
-#include "audiographer/exception.h"
-#include "audiographer/type_utils.h"
-
-#include <cmath>
-#include <boost/format.hpp>
-
-namespace AudioGrapher
-{
-using boost::format;
-using boost::str;
-
-SampleRateConverter::SampleRateConverter (uint32_t channels)
- : active (false)
- , channels (channels)
- , max_frames_in(0)
- , leftover_data (0)
- , leftover_frames (0)
- , max_leftover_frames (0)
- , data_out (0)
- , data_out_size (0)
- , src_state (0)
-{
-}
-
-void
-SampleRateConverter::init (nframes_t in_rate, nframes_t out_rate, int quality)
-{
- reset();
-
- if (in_rate == out_rate) {
- src_data.src_ratio = 1;
- return;
- }
-
- active = true;
- int err;
- src_state = src_new (quality, channels, &err);
- if (throw_level (ThrowObject) && !src_state) {
- throw Exception (*this, str (format
- ("Cannot initialize sample rate converter: %1%")
- % src_strerror (err)));
- }
-
- src_data.src_ratio = (double) out_rate / (double) in_rate;
-}
-
-SampleRateConverter::~SampleRateConverter ()
-{
- reset();
-}
-
-nframes_t
-SampleRateConverter::allocate_buffers (nframes_t max_frames)
-{
- if (!active) { return max_frames; }
-
- nframes_t max_frames_out = (nframes_t) ceil (max_frames * src_data.src_ratio);
- if (data_out_size < max_frames_out) {
-
- delete[] data_out;
- data_out = new float[max_frames_out];
- src_data.data_out = data_out;
-
- max_leftover_frames = 4 * max_frames;
- leftover_data = (float *) realloc (leftover_data, max_leftover_frames * sizeof (float));
- if (throw_level (ThrowObject) && !leftover_data) {
- throw Exception (*this, "A memory allocation error occured");
- }
-
- max_frames_in = max_frames;
- data_out_size = max_frames_out;
- }
-
- return max_frames_out;
-}
-
-void
-SampleRateConverter::process (ProcessContext<float> const & c)
-{
- if (!active) {
- output (c);
- return;
- }
-
- 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) {
- 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;
-
- do {
- src_data.output_frames = data_out_size / channels;
- src_data.data_out = data_out;
-
- if (leftover_frames > 0) {
-
- /* input data will be in leftover_data rather than data_in */
-
- src_data.data_in = leftover_data;
-
- if (first_time) {
-
- /* first time, append new data from data_in into the leftover_data buffer */
-
- TypeUtils<float>::copy (&leftover_data [leftover_frames * channels], in, frames);
- src_data.input_frames = frames + leftover_frames;
- } else {
-
- /* otherwise, just use whatever is still left in leftover_data; the contents
- were adjusted using memmove() right after the last SRC call (see
- below)
- */
-
- src_data.input_frames = leftover_frames;
- }
-
- } else {
- src_data.data_in = in;
- src_data.input_frames = frames / channels;
- }
-
- first_time = false;
-
- if (debug_level (DebugVerbose)) {
- debug_stream() << "data_in: " << src_data.data_in <<
- ", input_frames: " << src_data.input_frames <<
- ", data_out: " << src_data.data_out <<
- ", output_frames: " << src_data.output_frames << std::endl;
- }
-
- err = src_process (src_state, &src_data);
- if (throw_level (ThrowProcess) && err) {
- throw Exception (*this, str (format
- ("An error occured during sample rate conversion: %1%")
- % src_strerror (err)));
- }
-
- leftover_frames = src_data.input_frames - src_data.input_frames_used;
-
- if (leftover_frames > 0) {
- if (throw_level (ThrowProcess) && leftover_frames > max_leftover_frames) {
- throw Exception(*this, "leftover frames overflowed");
- }
- TypeUtils<float>::move (&src_data.data_in[src_data.input_frames_used * channels],
- leftover_data, leftover_frames * channels);
- }
-
- ProcessContext<float> c_out (c, data_out, src_data.output_frames_gen * channels);
- if (!src_data.end_of_input || leftover_frames) {
- c_out.remove_flag (ProcessContext<float>::EndOfInput);
- }
- output (c_out);
-
- if (debug_level (DebugProcess)) {
- debug_stream() <<
- "src_data.output_frames_gen: " << src_data.output_frames_gen <<
- ", leftover_frames: " << leftover_frames << std::endl;
- }
-
- if (throw_level (ThrowProcess) && src_data.output_frames_gen == 0 && leftover_frames) {
- throw Exception (*this, boost::str (boost::format
- ("No output frames genereated with %1% leftover frames")
- % leftover_frames));
- }
-
- } while (leftover_frames > frames);
-
- // src_data.end_of_input has to be checked to prevent infinite recursion
- if (!src_data.end_of_input && c.has_flag(ProcessContext<float>::EndOfInput)) {
- set_end_of_input (c);
- }
-}
-
-void SampleRateConverter::set_end_of_input (ProcessContext<float> const & c)
-{
- src_data.end_of_input = true;
-
- float f;
- ProcessContext<float> const dummy (c, &f, 0, channels);
-
- /* No idea why this has to be done twice for all data to be written,
- * but that just seems to be the way it is...
- */
- dummy.remove_flag (ProcessContext<float>::EndOfInput);
- process (dummy);
- dummy.set_flag (ProcessContext<float>::EndOfInput);
- process (dummy);
-}
-
-
-void SampleRateConverter::reset ()
-{
- active = false;
- max_frames_in = 0;
- src_data.end_of_input = false;
-
- if (src_state) {
- src_delete (src_state);
- }
-
- leftover_frames = 0;
- max_leftover_frames = 0;
- if (leftover_data) {
- free (leftover_data);
- }
-
- data_out_size = 0;
- delete [] data_out;
- data_out = 0;
-}
-
-} // namespace
+++ /dev/null
-#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;
-}
\ No newline at end of file
+++ /dev/null
-#include "utils.h"
-#include "audiographer/chunker.h"
-
-#include <cassert>
-
-using namespace AudioGrapher;
-
-class ChunkerTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (ChunkerTest);
- CPPUNIT_TEST (testSynchronousProcess);
- CPPUNIT_TEST (testAsynchronousProcess);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 128;
- random_data = TestUtils::init_random_data(frames);
- sink.reset (new VectorSink<float>());
- chunker.reset (new Chunker<float>(frames * 2));
- }
-
- void tearDown()
- {
- delete [] random_data;
- }
-
- void testSynchronousProcess()
- {
- chunker->add_output (sink);
- nframes_t frames_output = 0;
-
- ProcessContext<float> const context (random_data, frames, 1);
-
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
-
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames], frames));
-
- sink->reset();
-
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
-
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames], frames));
- }
-
- void testAsynchronousProcess()
- {
- assert (frames % 2 == 0);
-
- 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) 0, frames_output);
-
- // 1.5
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
-
- // 2.5
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (2 * frames, 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));
-
- sink->reset();
-
- // 3.5
- chunker->process (context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
-
- // 4.0
- chunker->process (half_context);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_equals (&random_data[frames / 2], 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;
- boost::shared_ptr<VectorSink<float> > sink;
-
- float * random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (ChunkerTest);
-
+++ /dev/null
-#include "utils.h"
-#include "audiographer/deinterleaver.h"
-
-using namespace AudioGrapher;
-
-class DeInterleaverTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (DeInterleaverTest);
- CPPUNIT_TEST (testUninitialized);
- CPPUNIT_TEST (testInvalidOutputIndex);
- CPPUNIT_TEST (testInvalidInputSize);
- CPPUNIT_TEST (testOutputSize);
- CPPUNIT_TEST (testZeroInput);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- channels = 3;
- frames_per_channel = 128;
- total_frames = channels * frames_per_channel;
- random_data = TestUtils::init_random_data (total_frames, 1.0);
-
- deinterleaver.reset (new DeInterleaver<float>());
- sink_a.reset (new VectorSink<float>());
- sink_b.reset (new VectorSink<float>());
- sink_c.reset (new VectorSink<float>());
- }
-
- void tearDown()
- {
- delete [] random_data;
- }
-
- void testUninitialized()
- {
- deinterleaver.reset (new DeInterleaver<float>());
- CPPUNIT_ASSERT_THROW (deinterleaver->output(0)->add_output (sink_a), Exception);
- }
-
- void testInvalidOutputIndex()
- {
- deinterleaver->init (3, frames_per_channel);
- CPPUNIT_ASSERT_THROW (deinterleaver->output(3)->add_output (sink_a), Exception);
- }
-
- void testInvalidInputSize()
- {
- deinterleaver->init (channels, frames_per_channel);
-
- ProcessContext<float> c (random_data, 0, channels);
-
- // Too many, frames % channels == 0
- c.frames() = total_frames + channels;
- CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
-
- // Too many, frames % channels != 0
- c.frames() = total_frames + 1;
- CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
-
- // Too few, frames % channels != 0
- c.frames() = total_frames - 1;
- CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception);
- }
-
- void assert_outputs (nframes_t expected_frames)
- {
- nframes_t generated_frames = 0;
-
- generated_frames = sink_a->get_data().size();
- CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
-
- generated_frames = sink_b->get_data().size();
- CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
-
- generated_frames = sink_c->get_data().size();
- CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
- }
-
- void testOutputSize()
- {
- deinterleaver->init (channels, frames_per_channel);
-
- deinterleaver->output (0)->add_output (sink_a);
- deinterleaver->output (1)->add_output (sink_b);
- deinterleaver->output (2)->add_output (sink_c);
-
- // Test maximum frame input
- ProcessContext<float> c (random_data, total_frames, channels);
- deinterleaver->process (c);
- assert_outputs (frames_per_channel);
-
- // Now with less frames
- nframes_t const less_frames = frames_per_channel / 4;
- c.frames() = less_frames * channels;
- deinterleaver->process (c);
- assert_outputs (less_frames);
- }
-
- void testZeroInput()
- {
- deinterleaver->init (channels, frames_per_channel);
-
- deinterleaver->output (0)->add_output (sink_a);
- deinterleaver->output (1)->add_output (sink_b);
- deinterleaver->output (2)->add_output (sink_c);
-
- // Input zero frames
- ProcessContext<float> c (random_data, 0, channels);
- deinterleaver->process (c);
-
- // ...and now test regular input
- c.frames() = total_frames;
- deinterleaver->process (c);
- assert_outputs (frames_per_channel);
- }
-
-
- private:
- boost::shared_ptr<DeInterleaver<float> > deinterleaver;
-
- boost::shared_ptr<VectorSink<float> > sink_a;
- boost::shared_ptr<VectorSink<float> > sink_b;
- boost::shared_ptr<VectorSink<float> > sink_c;
-
- float * random_data;
- nframes_t frames_per_channel;
- nframes_t total_frames;
- unsigned int channels;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (DeInterleaverTest);
-
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/chunker.h"
+
+#include <cassert>
+
+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:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+ sink.reset (new VectorSink<float>());
+ chunker.reset (new Chunker<float>(frames * 2));
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testSynchronousProcess()
+ {
+ chunker->add_output (sink);
+ nframes_t frames_output = 0;
+
+ ProcessContext<float> const context (random_data, frames, 1);
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames], frames));
+
+ sink->reset();
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames], frames));
+ }
+
+ void testAsynchronousProcess()
+ {
+ assert (frames % 2 == 0);
+
+ 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) 0, frames_output);
+
+ // 1.5
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ // 2.5
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, 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));
+
+ sink->reset();
+
+ // 3.5
+ chunker->process (context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_output);
+
+ // 4.0
+ chunker->process (half_context);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals (&random_data[frames / 2], 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));
+ }
+
+ 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;
+ boost::shared_ptr<VectorSink<float> > sink;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (ChunkerTest);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/deinterleaver.h"
+
+using namespace AudioGrapher;
+
+class DeInterleaverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (DeInterleaverTest);
+ CPPUNIT_TEST (testUninitialized);
+ CPPUNIT_TEST (testInvalidOutputIndex);
+ CPPUNIT_TEST (testInvalidInputSize);
+ CPPUNIT_TEST (testOutputSize);
+ CPPUNIT_TEST (testZeroInput);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ channels = 3;
+ frames_per_channel = 128;
+ total_frames = channels * frames_per_channel;
+ random_data = TestUtils::init_random_data (total_frames, 1.0);
+
+ deinterleaver.reset (new DeInterleaver<float>());
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ sink_c.reset (new VectorSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testUninitialized()
+ {
+ deinterleaver.reset (new DeInterleaver<float>());
+ CPPUNIT_ASSERT_THROW (deinterleaver->output(0)->add_output (sink_a), Exception);
+ }
+
+ void testInvalidOutputIndex()
+ {
+ deinterleaver->init (3, frames_per_channel);
+ CPPUNIT_ASSERT_THROW (deinterleaver->output(3)->add_output (sink_a), Exception);
+ }
+
+ void testInvalidInputSize()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+
+ ProcessContext<float> c (random_data, 2 * total_frames, channels);
+
+ // Too many, frames % channels == 0
+ CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames + channels)), Exception);
+
+ // Too many, frames % channels != 0
+ CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames + 1)), Exception);
+
+ // Too few, frames % channels != 0
+ CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames - 1)), Exception);
+ }
+
+ void assert_outputs (nframes_t expected_frames)
+ {
+ nframes_t generated_frames = 0;
+
+ generated_frames = sink_a->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+
+ generated_frames = sink_b->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+
+ generated_frames = sink_c->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+ }
+
+ void testOutputSize()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+
+ deinterleaver->output (0)->add_output (sink_a);
+ deinterleaver->output (1)->add_output (sink_b);
+ deinterleaver->output (2)->add_output (sink_c);
+
+ // Test maximum frame input
+ ProcessContext<float> c (random_data, total_frames, channels);
+ deinterleaver->process (c);
+ assert_outputs (frames_per_channel);
+
+ // Now with less frames
+ nframes_t const less_frames = frames_per_channel / 4;
+ deinterleaver->process (c.beginning (less_frames * channels));
+ assert_outputs (less_frames);
+ }
+
+ void testZeroInput()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+
+ deinterleaver->output (0)->add_output (sink_a);
+ deinterleaver->output (1)->add_output (sink_b);
+ deinterleaver->output (2)->add_output (sink_c);
+
+ // Input zero frames
+ ProcessContext<float> c (random_data, total_frames, channels);
+ deinterleaver->process (c.beginning (0));
+
+ // ...and now test regular input
+ deinterleaver->process (c);
+ assert_outputs (frames_per_channel);
+ }
+
+
+ private:
+ boost::shared_ptr<DeInterleaver<float> > deinterleaver;
+
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+ boost::shared_ptr<VectorSink<float> > sink_c;
+
+ float * random_data;
+ nframes_t frames_per_channel;
+ nframes_t total_frames;
+ unsigned int channels;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (DeInterleaverTest);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/interleaver.h"
+#include "audiographer/general/deinterleaver.h"
+
+using namespace AudioGrapher;
+
+class InterleaverDeInterleaverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (InterleaverDeInterleaverTest);
+ CPPUNIT_TEST (testInterleavedInput);
+ CPPUNIT_TEST (testDeInterleavedInput);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ channels = 3;
+ frames_per_channel = 128;
+ total_frames = channels * frames_per_channel;
+
+ random_data_a = TestUtils::init_random_data (total_frames, 1.0);
+ random_data_b = TestUtils::init_random_data (frames_per_channel, 1.0);
+ random_data_c = TestUtils::init_random_data (frames_per_channel, 1.0);
+
+ deinterleaver.reset (new DeInterleaver<float>());
+ interleaver.reset (new Interleaver<float>());
+
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ sink_c.reset (new VectorSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data_a;
+ delete [] random_data_b;
+ delete [] random_data_c;
+ }
+
+ void testInterleavedInput()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+ interleaver->init (channels, frames_per_channel);
+
+ deinterleaver->output (0)->add_output (interleaver->input (0));
+ deinterleaver->output (1)->add_output (interleaver->input (1));
+ deinterleaver->output (2)->add_output (interleaver->input (2));
+
+ interleaver->add_output (sink_a);
+
+ // Process and assert
+ ProcessContext<float> c (random_data_a, total_frames, channels);
+ deinterleaver->process (c);
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), total_frames));
+
+ // And a second round...
+ nframes_t less_frames = (frames_per_channel / 10) * channels;
+ deinterleaver->process (c.beginning (less_frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames));
+ }
+
+ void testDeInterleavedInput()
+ {
+ deinterleaver->init (channels, frames_per_channel);
+ interleaver->init (channels, frames_per_channel);
+
+ interleaver->add_output (deinterleaver);
+
+ deinterleaver->output (0)->add_output (sink_a);
+ deinterleaver->output (1)->add_output (sink_b);
+ deinterleaver->output (2)->add_output (sink_c);
+
+ ProcessContext<float> c_a (random_data_a, frames_per_channel, 1);
+ ProcessContext<float> c_b (random_data_b, frames_per_channel, 1);
+ ProcessContext<float> c_c (random_data_c, frames_per_channel, 1);
+
+ // Process and assert
+ interleaver->input (0)->process (c_a);
+ interleaver->input (1)->process (c_b);
+ interleaver->input (2)->process (c_c);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), frames_per_channel));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_b, sink_b->get_array(), frames_per_channel));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_c, sink_c->get_array(), frames_per_channel));
+
+ // And a second round...
+ nframes_t less_frames = frames_per_channel / 5;
+ 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));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data_c, sink_c->get_array(), less_frames));
+
+ }
+
+ private:
+ boost::shared_ptr<Interleaver<float> > interleaver;
+ boost::shared_ptr<DeInterleaver<float> > deinterleaver;
+
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+ boost::shared_ptr<VectorSink<float> > sink_c;
+
+ float * random_data_a;
+ float * random_data_b;
+ float * random_data_c;
+
+ nframes_t frames_per_channel;
+ nframes_t total_frames;
+ unsigned int channels;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (InterleaverDeInterleaverTest);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/interleaver.h"
+
+using namespace AudioGrapher;
+
+class InterleaverTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (InterleaverTest);
+ CPPUNIT_TEST (testUninitialized);
+ CPPUNIT_TEST (testInvalidInputIndex);
+ CPPUNIT_TEST (testInvalidInputSize);
+ CPPUNIT_TEST (testOutputSize);
+ CPPUNIT_TEST (testZeroInput);
+ CPPUNIT_TEST (testChannelSync);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ channels = 3;
+ frames = 128;
+ random_data = TestUtils::init_random_data (frames, 1.0);
+
+ interleaver.reset (new Interleaver<float>());
+ sink.reset (new VectorSink<float>());
+
+ interleaver->init (channels, frames);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testUninitialized()
+ {
+ interleaver.reset (new Interleaver<float>());
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (interleaver->input(0)->process (c), Exception);
+ }
+
+ void testInvalidInputIndex()
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (interleaver->input (3)->process (c), Exception);
+ }
+
+ void testInvalidInputSize()
+ {
+ ProcessContext<float> c (random_data, frames + 1, 1);
+ CPPUNIT_ASSERT_THROW (interleaver->input (0)->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.beginning (frames - 1));
+ interleaver->input (1)->process (c.beginning (frames - 1));
+ CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c.beginning (frames)), Exception);
+ }
+
+ void testOutputSize()
+ {
+ interleaver->add_output (sink);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ interleaver->input (2)->process (c);
+
+ nframes_t expected_frames = frames * channels;
+ nframes_t generated_frames = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+
+ nframes_t less_frames = frames / 2;
+ 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();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+ }
+
+ void testZeroInput()
+ {
+ interleaver->add_output (sink);
+
+ // input zero frames to all inputs
+ 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
+ interleaver->input (0)->process (c);
+ interleaver->input (1)->process (c);
+ interleaver->input (2)->process (c);
+
+ nframes_t expected_frames = frames * channels;
+ nframes_t generated_frames = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
+ }
+
+ void testChannelSync()
+ {
+ interleaver->add_output (sink);
+ ProcessContext<float> c (random_data, frames, 1);
+ interleaver->input (0)->process (c);
+ CPPUNIT_ASSERT_THROW (interleaver->input (0)->process (c), Exception);
+ }
+
+
+ private:
+ boost::shared_ptr<Interleaver<float> > interleaver;
+
+ boost::shared_ptr<VectorSink<float> > sink;
+
+ nframes_t channels;
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (InterleaverTest);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/normalizer.h"
+#include "audiographer/general/peak_reader.h"
+
+using namespace AudioGrapher;
+
+class NormalizerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (NormalizerTest);
+ CPPUNIT_TEST (testConstAmplify);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 1024;
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testConstAmplify()
+ {
+ float target = 0.0;
+ random_data = TestUtils::init_random_data(frames, 0.5);
+
+ normalizer.reset (new Normalizer(target));
+ peak_reader.reset (new PeakReader());
+ sink.reset (new VectorSink<float>());
+
+ ProcessContext<float> const c (random_data, frames, 1);
+ peak_reader->process (c);
+
+ float peak = peak_reader->get_peak();
+ normalizer->alloc_buffer (frames);
+ normalizer->set_peak (peak);
+ normalizer->add_output (sink);
+ normalizer->process (c);
+
+ peak_reader->reset();
+ ConstProcessContext<float> normalized (sink->get_array(), frames, 1);
+ peak_reader->process (normalized);
+
+ peak = peak_reader->get_peak();
+ CPPUNIT_ASSERT (-FLT_EPSILON <= (peak - 1.0) && (peak - 1.0) <= 0.0);
+ }
+
+ private:
+ boost::shared_ptr<Normalizer> normalizer;
+ boost::shared_ptr<PeakReader> peak_reader;
+ boost::shared_ptr<VectorSink<float> > sink;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (NormalizerTest);
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/peak_reader.h"
+
+using namespace AudioGrapher;
+
+class PeakReaderTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (PeakReaderTest);
+ 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()
+ {
+ reader.reset (new PeakReader());
+ ProcessContext<float> c (random_data, frames, 1);
+
+ float peak = 1.5;
+ random_data[10] = peak;
+ reader->process (c);
+ CPPUNIT_ASSERT_EQUAL(peak, reader->get_peak());
+
+ peak = 2.0;
+ random_data[10] = peak;
+ reader->process (c);
+ CPPUNIT_ASSERT_EQUAL(peak, reader->get_peak());
+
+ peak = -2.1;
+ random_data[10] = peak;
+ reader->process (c);
+ float expected = fabs(peak);
+ CPPUNIT_ASSERT_EQUAL(expected, reader->get_peak());
+ }
+
+ private:
+ boost::shared_ptr<PeakReader> reader;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (PeakReaderTest);
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/sample_format_converter.h"
+
+using namespace AudioGrapher;
+
+class SampleFormatConverterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SampleFormatConverterTest);
+ CPPUNIT_TEST (testInit);
+ CPPUNIT_TEST (testFrameCount);
+ CPPUNIT_TEST (testFloat);
+ CPPUNIT_TEST (testInt32);
+ CPPUNIT_TEST (testInt24);
+ CPPUNIT_TEST (testInt16);
+ CPPUNIT_TEST (testUint8);
+ CPPUNIT_TEST (testChannelCount);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames, 1.0);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testInit()
+ {
+ boost::shared_ptr<SampleFormatConverter<float> > f_converter (new SampleFormatConverter<float>(1));
+ f_converter->init (frames, D_Tri, 32); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 24), Exception);
+ CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 48), Exception);
+
+ boost::shared_ptr<SampleFormatConverter<int32_t> > i_converter (new SampleFormatConverter<int32_t>(1));
+ i_converter->init (frames, D_Tri, 32); // Doesn't throw
+ i_converter->init (frames, D_Tri, 24); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 8), Exception);
+ CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 16), Exception);
+ CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 48), Exception);
+
+ boost::shared_ptr<SampleFormatConverter<int16_t> > i16_converter (new SampleFormatConverter<int16_t>(1));
+ i16_converter->init (frames, D_Tri, 16); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 8), Exception);
+ CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 32), Exception);
+ CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 48), Exception);
+
+ boost::shared_ptr<SampleFormatConverter<uint8_t> > ui_converter (new SampleFormatConverter<uint8_t>(1));
+ ui_converter->init (frames, D_Tri, 8); // Doesn't throw
+ CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 4), Exception);
+ CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 16), Exception);
+ }
+
+ void testFrameCount()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+
+ converter->init (frames, D_Tri, 32);
+ converter->add_output (sink);
+ nframes_t frames_output = 0;
+
+ {
+ ProcessContext<float> pc(random_data, frames / 2, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames / 2, frames_output);
+ }
+
+ {
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ }
+
+ {
+ ProcessContext<float> pc(random_data, frames + 1, 1);
+ CPPUNIT_ASSERT_THROW(converter->process (pc), Exception);
+ }
+ }
+
+ void testFloat()
+ {
+ boost::shared_ptr<SampleFormatConverter<float> > converter (new SampleFormatConverter<float>(1));
+ boost::shared_ptr<VectorSink<float> > sink (new VectorSink<float>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 32);
+ converter->add_output (sink);
+
+ converter->set_clip_floats (false);
+ ProcessContext<float> const pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_equals(sink->get_array(), random_data, frames));
+
+ // Make sure a few samples are < -1.0 and > 1.0
+ random_data[10] = -1.5;
+ random_data[20] = 1.5;
+
+ converter->set_clip_floats (true);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+
+ for (nframes_t i = 0; i < frames; ++i) {
+ // fp comparison needs a bit of tolerance, 1.01 << 1.5
+ CPPUNIT_ASSERT(sink->get_data()[i] < 1.01);
+ CPPUNIT_ASSERT(sink->get_data()[i] > -1.01);
+ }
+ }
+
+ void testInt32()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 32);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testInt24()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 24);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testInt16()
+ {
+ boost::shared_ptr<SampleFormatConverter<int16_t> > converter (new SampleFormatConverter<int16_t>(1));
+ boost::shared_ptr<VectorSink<int16_t> > sink (new VectorSink<int16_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 16);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testUint8()
+ {
+ boost::shared_ptr<SampleFormatConverter<uint8_t> > converter (new SampleFormatConverter<uint8_t>(1));
+ boost::shared_ptr<VectorSink<uint8_t> > sink (new VectorSink<uint8_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 8);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, frames, 1);
+ converter->process (pc);
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
+ }
+
+ void testChannelCount()
+ {
+ boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(3));
+ boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
+ nframes_t frames_output = 0;
+
+ converter->init(frames, D_Tri, 32);
+ converter->add_output (sink);
+
+ ProcessContext<float> pc(random_data, 4, 1);
+ CPPUNIT_ASSERT_THROW (converter->process (pc), Exception);
+
+ 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 (new_frame_count, frames_output);
+ CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), pc.frames()));
+ }
+
+ private:
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SampleFormatConverterTest);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/silence_trimmer.h"
+
+using namespace AudioGrapher;
+
+class SilenceTrimmerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SilenceTrimmerTest);
+ CPPUNIT_TEST (testExceptions);
+ CPPUNIT_TEST (testFullBuffers);
+ CPPUNIT_TEST (testPartialBuffers);
+ CPPUNIT_TEST (testAddSilenceBeginning);
+ CPPUNIT_TEST (testAddSilenceEnd);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+
+ random_data = TestUtils::init_random_data(frames);
+ random_data[0] = 0.5;
+ random_data[frames - 1] = 0.5;
+
+ zero_data = new float[frames];
+ memset(zero_data, 0, frames * sizeof(float));
+
+ half_random_data = TestUtils::init_random_data(frames);
+ memset(half_random_data, 0, (frames / 2) * sizeof(float));
+
+ trimmer.reset (new SilenceTrimmer<float> (frames / 2));
+ sink.reset (new AppendingVectorSink<float>());
+
+ trimmer->set_trim_beginning (true);
+ trimmer->set_trim_end (true);
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ delete [] zero_data;
+ delete [] half_random_data;
+ }
+
+ void testFullBuffers()
+ {
+ trimmer->add_output (sink);
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_processed);
+ }
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_processed);
+ }
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (3 * frames, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames], zero_data, frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[2 * frames], random_data, frames));
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (3 * frames, frames_processed);
+ }
+ }
+
+ void testPartialBuffers()
+ {
+ trimmer->add_output (sink);
+ trimmer->reset (frames / 4);
+ trimmer->set_trim_beginning (true);
+ trimmer->set_trim_end (true);
+
+ {
+ ProcessContext<float> c (half_random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames / 2, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), &half_random_data[frames / 2], frames / 2));
+ }
+
+ {
+ ProcessContext<float> c (zero_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames / 2, frames_processed);
+ }
+
+ {
+ ProcessContext<float> c (half_random_data, frames, 1);
+ trimmer->process (c);
+ nframes_t frames_processed = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (2 * frames + frames / 2, frames_processed);
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames + frames / 2], half_random_data, frames));
+ }
+ }
+
+ void testExceptions()
+ {
+ {
+ CPPUNIT_ASSERT_THROW (trimmer->reset (0), Exception);
+ }
+ }
+
+ void testAddSilenceBeginning()
+ {
+ trimmer->add_output (sink);
+
+ nframes_t silence = frames / 2;
+ trimmer->add_silence_to_beginning (silence);
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ }
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), zero_data, silence));
+ CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[silence], random_data, frames));
+ }
+
+ void testAddSilenceEnd()
+ {
+ trimmer->add_output (sink);
+
+ nframes_t silence = frames / 3;
+ trimmer->add_silence_to_end (silence);
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ trimmer->process (c);
+ }
+
+ {
+ ProcessContext<float> c (random_data, frames, 1);
+ c.set_flag (ProcessContext<float>::EndOfInput);
+ 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));
+ }
+
+ private:
+ boost::shared_ptr<SilenceTrimmer<float> > trimmer;
+ boost::shared_ptr<AppendingVectorSink<float> > sink;
+
+ float * random_data;
+ float * zero_data;
+ float * half_random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SilenceTrimmerTest);
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/sr_converter.h"
+
+using namespace AudioGrapher;
+
+class SampleRateConverterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (SampleRateConverterTest);
+ CPPUNIT_TEST (testNoConversion);
+ CPPUNIT_TEST (testUpsampleLength);
+ CPPUNIT_TEST (testDownsampleLength);
+ CPPUNIT_TEST (testRespectsEndOfInput);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+ sink.reset (new AppendingVectorSink<float>());
+ grabber.reset (new ProcessContextGrabber<float>());
+ converter.reset (new SampleRateConverter (1));
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ }
+
+ void testNoConversion()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+ nframes_t frames_output = 0;
+
+ converter->init (44100, 44100);
+ converter->add_output (sink);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ frames_output = sink->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
+ }
+
+ void testUpsampleLength()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+ nframes_t frames_output = 0;
+
+ converter->init (44100, 88200);
+ converter->allocate_buffers (half_frames);
+ converter->add_output (sink);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ frames_output = sink->get_data().size();
+ nframes_t tolerance = 3;
+ CPPUNIT_ASSERT (2 * frames - tolerance < frames_output && frames_output < 2 * frames + tolerance);
+ }
+
+ void testDownsampleLength()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+ nframes_t frames_output = 0;
+
+ converter->init (88200, 44100);
+ converter->allocate_buffers (half_frames);
+ converter->add_output (sink);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ frames_output = sink->get_data().size();
+ nframes_t tolerance = 3;
+ CPPUNIT_ASSERT (half_frames - tolerance < frames_output && frames_output < half_frames + tolerance);
+ }
+
+ void testRespectsEndOfInput()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+
+ converter->init (44100, 48000);
+ converter->allocate_buffers (half_frames);
+ converter->add_output (grabber);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames / 2, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ for (std::list<ProcessContext<float> >::iterator it = grabber->contexts.begin(); it != grabber->contexts.end(); ++it) {
+ std::list<ProcessContext<float> >::iterator next = it; ++next;
+ if (next == grabber->contexts.end()) {
+ CPPUNIT_ASSERT (it->has_flag (ProcessContext<float>::EndOfInput));
+ } else {
+ CPPUNIT_ASSERT (!it->has_flag (ProcessContext<float>::EndOfInput));
+ }
+ }
+ }
+
+
+ private:
+ boost::shared_ptr<SampleRateConverter > converter;
+ boost::shared_ptr<AppendingVectorSink<float> > sink;
+ boost::shared_ptr<ProcessContextGrabber<float> > grabber;
+
+ float * random_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (SampleRateConverterTest);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/general/threader.h"
+
+using namespace AudioGrapher;
+
+class ThreaderTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (ThreaderTest);
+ CPPUNIT_TEST (testProcess);
+ CPPUNIT_TEST (testRemoveOutput);
+ CPPUNIT_TEST (testClearOutputs);
+ CPPUNIT_TEST (testExceptions);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data (frames, 1.0);
+
+ zero_data = new float[frames];
+ memset (zero_data, 0, frames * sizeof(float));
+
+ thread_pool = new Glib::ThreadPool (3);
+ threader.reset (new Threader<float> (*thread_pool));
+
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ sink_c.reset (new VectorSink<float>());
+ sink_d.reset (new VectorSink<float>());
+ sink_e.reset (new VectorSink<float>());
+ sink_f.reset (new VectorSink<float>());
+
+ throwing_sink.reset (new ThrowingSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ delete [] zero_data;
+
+ thread_pool->shutdown();
+ delete thread_pool;
+ }
+
+ void testProcess()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (sink_d);
+ threader->add_output (sink_e);
+ threader->add_output (sink_f);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ threader->process (c);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_d->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
+ }
+
+ void testRemoveOutput()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (sink_d);
+ threader->add_output (sink_e);
+ threader->add_output (sink_f);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ threader->process (c);
+
+ // Remove a, b and f
+ threader->remove_output (sink_a);
+ threader->remove_output (sink_b);
+ threader->remove_output (sink_f);
+
+ ProcessContext<float> zc (zero_data, frames, 1);
+ threader->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_d->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_e->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
+ }
+
+ void testClearOutputs()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (sink_d);
+ threader->add_output (sink_e);
+ threader->add_output (sink_f);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ threader->process (c);
+
+ threader->clear_outputs();
+ ProcessContext<float> zc (zero_data, frames, 1);
+ threader->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_d->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
+ }
+
+ void testExceptions()
+ {
+ threader->add_output (sink_a);
+ threader->add_output (sink_b);
+ threader->add_output (sink_c);
+ threader->add_output (throwing_sink);
+ threader->add_output (sink_e);
+ threader->add_output (throwing_sink);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ CPPUNIT_ASSERT_THROW (threader->process (c), Exception);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
+ }
+
+ private:
+ Glib::ThreadPool * thread_pool;
+
+ boost::shared_ptr<Threader<float> > threader;
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+ boost::shared_ptr<VectorSink<float> > sink_c;
+ boost::shared_ptr<VectorSink<float> > sink_d;
+ boost::shared_ptr<VectorSink<float> > sink_e;
+ boost::shared_ptr<VectorSink<float> > sink_f;
+
+ boost::shared_ptr<ThrowingSink<float> > throwing_sink;
+
+ float * random_data;
+ float * zero_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (ThreaderTest);
+
+++ /dev/null
-#include "utils.h"
-#include "audiographer/identity_vertex.h"
-
-using namespace AudioGrapher;
-
-class IdentityVertexTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (IdentityVertexTest);
- CPPUNIT_TEST (testProcess);
- CPPUNIT_TEST (testRemoveOutput);
- CPPUNIT_TEST (testClearOutputs);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 128;
- random_data = TestUtils::init_random_data(frames);
-
- zero_data = new float[frames];
- memset (zero_data, 0, frames * sizeof(float));
-
- sink_a.reset (new VectorSink<float>());
- sink_b.reset (new VectorSink<float>());
- }
-
- void tearDown()
- {
- delete [] random_data;
- delete [] zero_data;
- }
-
- void testProcess()
- {
- vertex.reset (new IdentityVertex<float>());
- vertex->add_output (sink_a);
- vertex->add_output (sink_b);
-
- nframes_t frames_output = 0;
-
- ProcessContext<float> c (random_data, frames, 1);
- vertex->process (c);
-
- frames_output = sink_a->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
-
- frames_output = sink_b->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
-
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_b->get_array(), frames));
- }
-
- void testRemoveOutput()
- {
- vertex.reset (new IdentityVertex<float>());
- vertex->add_output (sink_a);
- vertex->add_output (sink_b);
-
- ProcessContext<float> c (random_data, frames, 1);
- vertex->process (c);
-
- vertex->remove_output (sink_a);
- ProcessContext<float> zc (zero_data, frames, 1);
- vertex->process (zc);
-
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (zero_data, sink_b->get_array(), frames));
- }
-
- void testClearOutputs()
- {
- vertex.reset (new IdentityVertex<float>());
- vertex->add_output (sink_a);
- vertex->add_output (sink_b);
-
- ProcessContext<float> c (random_data, frames, 1);
- vertex->process (c);
-
- vertex->clear_outputs ();
- ProcessContext<float> zc (zero_data, frames, 1);
- vertex->process (zc);
-
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_b->get_array(), frames));
- }
-
- private:
- boost::shared_ptr<IdentityVertex<float> > vertex;
- boost::shared_ptr<VectorSink<float> > sink_a;
- boost::shared_ptr<VectorSink<float> > sink_b;
-
- float * random_data;
- float * zero_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (IdentityVertexTest);
-
+++ /dev/null
-#include "utils.h"
-#include "audiographer/interleaver.h"
-#include "audiographer/deinterleaver.h"
-
-using namespace AudioGrapher;
-
-class InterleaverDeInterleaverTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (InterleaverDeInterleaverTest);
- CPPUNIT_TEST (testInterleavedInput);
- CPPUNIT_TEST (testDeInterleavedInput);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- channels = 3;
- frames_per_channel = 128;
- total_frames = channels * frames_per_channel;
-
- random_data_a = TestUtils::init_random_data (total_frames, 1.0);
- random_data_b = TestUtils::init_random_data (frames_per_channel, 1.0);
- random_data_c = TestUtils::init_random_data (frames_per_channel, 1.0);
-
- deinterleaver.reset (new DeInterleaver<float>());
- interleaver.reset (new Interleaver<float>());
-
- sink_a.reset (new VectorSink<float>());
- sink_b.reset (new VectorSink<float>());
- sink_c.reset (new VectorSink<float>());
- }
-
- void tearDown()
- {
- delete [] random_data_a;
- delete [] random_data_b;
- delete [] random_data_c;
- }
-
- void testInterleavedInput()
- {
- deinterleaver->init (channels, frames_per_channel);
- interleaver->init (channels, frames_per_channel);
-
- deinterleaver->output (0)->add_output (interleaver->input (0));
- deinterleaver->output (1)->add_output (interleaver->input (1));
- deinterleaver->output (2)->add_output (interleaver->input (2));
-
- interleaver->add_output (sink_a);
-
- // Process and assert
- ProcessContext<float> c (random_data_a, total_frames, channels);
- deinterleaver->process (c);
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), total_frames));
-
- // And a second round...
- nframes_t less_frames = (frames_per_channel / 10) * channels;
- c.frames() = less_frames;
- deinterleaver->process (c);
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames));
- }
-
- void testDeInterleavedInput()
- {
- deinterleaver->init (channels, frames_per_channel);
- interleaver->init (channels, frames_per_channel);
-
- interleaver->add_output (deinterleaver);
-
- deinterleaver->output (0)->add_output (sink_a);
- deinterleaver->output (1)->add_output (sink_b);
- deinterleaver->output (2)->add_output (sink_c);
-
- ProcessContext<float> c_a (random_data_a, frames_per_channel, 1);
- ProcessContext<float> c_b (random_data_b, frames_per_channel, 1);
- ProcessContext<float> c_c (random_data_c, frames_per_channel, 1);
-
- // Process and assert
- interleaver->input (0)->process (c_a);
- interleaver->input (1)->process (c_b);
- interleaver->input (2)->process (c_c);
-
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), frames_per_channel));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data_b, sink_b->get_array(), frames_per_channel));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data_c, sink_c->get_array(), frames_per_channel));
-
- // 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);
-
- 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));
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data_c, sink_c->get_array(), less_frames));
-
- }
-
- private:
- boost::shared_ptr<Interleaver<float> > interleaver;
- boost::shared_ptr<DeInterleaver<float> > deinterleaver;
-
- boost::shared_ptr<VectorSink<float> > sink_a;
- boost::shared_ptr<VectorSink<float> > sink_b;
- boost::shared_ptr<VectorSink<float> > sink_c;
-
- float * random_data_a;
- float * random_data_b;
- float * random_data_c;
-
- nframes_t frames_per_channel;
- nframes_t total_frames;
- unsigned int channels;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (InterleaverDeInterleaverTest);
-
+++ /dev/null
-#include "utils.h"
-#include "audiographer/interleaver.h"
-
-using namespace AudioGrapher;
-
-class InterleaverTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (InterleaverTest);
- CPPUNIT_TEST (testUninitialized);
- CPPUNIT_TEST (testInvalidInputIndex);
- CPPUNIT_TEST (testInvalidInputSize);
- CPPUNIT_TEST (testOutputSize);
- CPPUNIT_TEST (testZeroInput);
- CPPUNIT_TEST (testChannelSync);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- channels = 3;
- frames = 128;
- random_data = TestUtils::init_random_data (frames, 1.0);
-
- interleaver.reset (new Interleaver<float>());
- sink.reset (new VectorSink<float>());
-
- interleaver->init (channels, frames);
- }
-
- void tearDown()
- {
- delete [] random_data;
- }
-
- void testUninitialized()
- {
- interleaver.reset (new Interleaver<float>());
- ProcessContext<float> c (random_data, frames, 1);
- CPPUNIT_ASSERT_THROW (interleaver->input(0)->process (c), Exception);
- }
-
- void testInvalidInputIndex()
- {
- ProcessContext<float> c (random_data, frames, 1);
- CPPUNIT_ASSERT_THROW (interleaver->input (3)->process (c), Exception);
- }
-
- void testInvalidInputSize()
- {
- 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);
- interleaver->input (1)->process (c);
- c.frames() = frames;
- CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception);
- }
-
- void testOutputSize()
- {
- interleaver->add_output (sink);
-
- ProcessContext<float> c (random_data, frames, 1);
- interleaver->input (0)->process (c);
- interleaver->input (1)->process (c);
- interleaver->input (2)->process (c);
-
- nframes_t expected_frames = frames * channels;
- nframes_t generated_frames = sink->get_data().size();
- 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);
-
- expected_frames = less_frames * channels;
- generated_frames = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
- }
-
- void testZeroInput()
- {
- 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);
-
- // 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);
-
- nframes_t expected_frames = frames * channels;
- nframes_t generated_frames = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames);
- }
-
- void testChannelSync()
- {
- interleaver->add_output (sink);
- ProcessContext<float> c (random_data, frames, 1);
- interleaver->input (0)->process (c);
- CPPUNIT_ASSERT_THROW (interleaver->input (0)->process (c), Exception);
- }
-
-
- private:
- boost::shared_ptr<Interleaver<float> > interleaver;
-
- boost::shared_ptr<VectorSink<float> > sink;
-
- nframes_t channels;
- float * random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (InterleaverTest);
-
+++ /dev/null
-#include "utils.h"
-
-#include "audiographer/normalizer.h"
-#include "audiographer/peak_reader.h"
-
-using namespace AudioGrapher;
-
-class NormalizerTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (NormalizerTest);
- CPPUNIT_TEST (testConstAmplify);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 1024;
- }
-
- void tearDown()
- {
- delete [] random_data;
- }
-
- void testConstAmplify()
- {
- float target = 0.0;
- random_data = TestUtils::init_random_data(frames, 0.5);
-
- normalizer.reset (new Normalizer(target));
- peak_reader.reset (new PeakReader());
- sink.reset (new VectorSink<float>());
-
- ProcessContext<float> const c (random_data, frames, 1);
- peak_reader->process (c);
-
- float peak = peak_reader->get_peak();
- normalizer->alloc_buffer (frames);
- normalizer->set_peak (peak);
- normalizer->add_output (sink);
- normalizer->process (c);
-
- peak_reader->reset();
- ConstProcessContext<float> normalized (sink->get_array(), frames, 1);
- peak_reader->process (normalized);
-
- peak = peak_reader->get_peak();
- CPPUNIT_ASSERT (-FLT_EPSILON <= (peak - 1.0) && (peak - 1.0) <= 0.0);
- }
-
- private:
- boost::shared_ptr<Normalizer> normalizer;
- boost::shared_ptr<PeakReader> peak_reader;
- boost::shared_ptr<VectorSink<float> > sink;
-
- float * random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (NormalizerTest);
+++ /dev/null
-#include "utils.h"
-#include "audiographer/peak_reader.h"
-
-using namespace AudioGrapher;
-
-class PeakReaderTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (PeakReaderTest);
- 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()
- {
- reader.reset (new PeakReader());
- ProcessContext<float> c (random_data, frames, 1);
-
- float peak = 1.5;
- random_data[10] = peak;
- reader->process (c);
- CPPUNIT_ASSERT_EQUAL(peak, reader->get_peak());
-
- peak = 2.0;
- random_data[10] = peak;
- reader->process (c);
- CPPUNIT_ASSERT_EQUAL(peak, reader->get_peak());
-
- peak = -2.1;
- random_data[10] = peak;
- reader->process (c);
- float expected = fabs(peak);
- CPPUNIT_ASSERT_EQUAL(expected, reader->get_peak());
- }
-
- private:
- boost::shared_ptr<PeakReader> reader;
-
- float * random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (PeakReaderTest);
+++ /dev/null
-#include "utils.h"
-#include "audiographer/sample_format_converter.h"
-
-using namespace AudioGrapher;
-
-class SampleFormatConverterTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (SampleFormatConverterTest);
- CPPUNIT_TEST (testInit);
- CPPUNIT_TEST (testFrameCount);
- CPPUNIT_TEST (testFloat);
- CPPUNIT_TEST (testInt32);
- CPPUNIT_TEST (testInt24);
- CPPUNIT_TEST (testInt16);
- CPPUNIT_TEST (testUint8);
- CPPUNIT_TEST (testChannelCount);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 128;
- random_data = TestUtils::init_random_data(frames, 1.0);
- }
-
- void tearDown()
- {
- delete [] random_data;
- }
-
- void testInit()
- {
- boost::shared_ptr<SampleFormatConverter<float> > f_converter (new SampleFormatConverter<float>(1));
- f_converter->init (frames, D_Tri, 32); // Doesn't throw
- CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 24), Exception);
- CPPUNIT_ASSERT_THROW (f_converter->init (frames, D_Tri, 48), Exception);
-
- boost::shared_ptr<SampleFormatConverter<int32_t> > i_converter (new SampleFormatConverter<int32_t>(1));
- i_converter->init (frames, D_Tri, 32); // Doesn't throw
- i_converter->init (frames, D_Tri, 24); // Doesn't throw
- CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 8), Exception);
- CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 16), Exception);
- CPPUNIT_ASSERT_THROW (i_converter->init (frames, D_Tri, 48), Exception);
-
- boost::shared_ptr<SampleFormatConverter<int16_t> > i16_converter (new SampleFormatConverter<int16_t>(1));
- i16_converter->init (frames, D_Tri, 16); // Doesn't throw
- CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 8), Exception);
- CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 32), Exception);
- CPPUNIT_ASSERT_THROW (i16_converter->init (frames, D_Tri, 48), Exception);
-
- boost::shared_ptr<SampleFormatConverter<uint8_t> > ui_converter (new SampleFormatConverter<uint8_t>(1));
- ui_converter->init (frames, D_Tri, 8); // Doesn't throw
- CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 4), Exception);
- CPPUNIT_ASSERT_THROW (ui_converter->init (frames, D_Tri, 16), Exception);
- }
-
- void testFrameCount()
- {
- boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
- boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
-
- converter->init (frames, D_Tri, 32);
- converter->add_output (sink);
- nframes_t frames_output = 0;
-
- {
- ProcessContext<float> pc(random_data, frames / 2, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames / 2, frames_output);
- }
-
- {
- ProcessContext<float> pc(random_data, frames, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- }
-
- {
- ProcessContext<float> pc(random_data, frames + 1, 1);
- CPPUNIT_ASSERT_THROW(converter->process (pc), Exception);
- }
- }
-
- void testFloat()
- {
- boost::shared_ptr<SampleFormatConverter<float> > converter (new SampleFormatConverter<float>(1));
- boost::shared_ptr<VectorSink<float> > sink (new VectorSink<float>());
- nframes_t frames_output = 0;
-
- converter->init(frames, D_Tri, 32);
- converter->add_output (sink);
-
- converter->set_clip_floats (false);
- ProcessContext<float> const pc(random_data, frames, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_equals(sink->get_array(), random_data, frames));
-
- // Make sure a few samples are < -1.0 and > 1.0
- random_data[10] = -1.5;
- random_data[20] = 1.5;
-
- converter->set_clip_floats (true);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
-
- for (nframes_t i = 0; i < frames; ++i) {
- // fp comparison needs a bit of tolerance, 1.01 << 1.5
- CPPUNIT_ASSERT(sink->get_data()[i] < 1.01);
- CPPUNIT_ASSERT(sink->get_data()[i] > -1.01);
- }
- }
-
- void testInt32()
- {
- boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
- boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
- nframes_t frames_output = 0;
-
- converter->init(frames, D_Tri, 32);
- converter->add_output (sink);
-
- ProcessContext<float> pc(random_data, frames, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
- }
-
- void testInt24()
- {
- boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(1));
- boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
- nframes_t frames_output = 0;
-
- converter->init(frames, D_Tri, 24);
- converter->add_output (sink);
-
- ProcessContext<float> pc(random_data, frames, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
- }
-
- void testInt16()
- {
- boost::shared_ptr<SampleFormatConverter<int16_t> > converter (new SampleFormatConverter<int16_t>(1));
- boost::shared_ptr<VectorSink<int16_t> > sink (new VectorSink<int16_t>());
- nframes_t frames_output = 0;
-
- converter->init(frames, D_Tri, 16);
- converter->add_output (sink);
-
- ProcessContext<float> pc(random_data, frames, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
- }
-
- void testUint8()
- {
- boost::shared_ptr<SampleFormatConverter<uint8_t> > converter (new SampleFormatConverter<uint8_t>(1));
- boost::shared_ptr<VectorSink<uint8_t> > sink (new VectorSink<uint8_t>());
- nframes_t frames_output = 0;
-
- converter->init(frames, D_Tri, 8);
- converter->add_output (sink);
-
- ProcessContext<float> pc(random_data, frames, 1);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
- CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), frames));
- }
-
- void testChannelCount()
- {
- boost::shared_ptr<SampleFormatConverter<int32_t> > converter (new SampleFormatConverter<int32_t>(3));
- boost::shared_ptr<VectorSink<int32_t> > sink (new VectorSink<int32_t>());
- nframes_t frames_output = 0;
-
- converter->init(frames, D_Tri, 32);
- converter->add_output (sink);
-
- ProcessContext<float> pc(random_data, 4, 1);
- CPPUNIT_ASSERT_THROW (converter->process (pc), Exception);
-
- pc.frames() = frames - (frames % 3);
- converter->process (pc);
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (pc.frames(), frames_output);
- CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), pc.frames()));
- }
-
- private:
-
- float * random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (SampleFormatConverterTest);
-
+++ /dev/null
-#include "utils.h"
-
-#include "audiographer/silence_trimmer.h"
-
-using namespace AudioGrapher;
-
-class SilenceTrimmerTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (SilenceTrimmerTest);
- CPPUNIT_TEST (testExceptions);
- CPPUNIT_TEST (testFullBuffers);
- CPPUNIT_TEST (testPartialBuffers);
- CPPUNIT_TEST (testAddSilenceBeginning);
- CPPUNIT_TEST (testAddSilenceEnd);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 128;
-
- random_data = TestUtils::init_random_data(frames);
- random_data[0] = 0.5;
- random_data[frames - 1] = 0.5;
-
- zero_data = new float[frames];
- memset(zero_data, 0, frames * sizeof(float));
-
- half_random_data = TestUtils::init_random_data(frames);
- memset(half_random_data, 0, (frames / 2) * sizeof(float));
-
- trimmer.reset (new SilenceTrimmer<float>());
- sink.reset (new AppendingVectorSink<float>());
-
- trimmer->set_trim_beginning (true);
- trimmer->set_trim_end (true);
- }
-
- void tearDown()
- {
- 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);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL ((nframes_t) 0, frames_processed);
- }
-
- {
- ProcessContext<float> c (random_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_processed);
- CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
- }
-
- {
- ProcessContext<float> c (zero_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_processed);
- }
-
- {
- ProcessContext<float> c (random_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (3 * frames, frames_processed);
- CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames], zero_data, frames));
- CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[2 * frames], random_data, frames));
- }
-
- {
- ProcessContext<float> c (zero_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (3 * frames, frames_processed);
- }
- }
-
- void testPartialBuffers()
- {
- trimmer->add_output (sink);
- AudioGrapher::Utils::init_zeros<float>(frames / 4);
-
- {
- ProcessContext<float> c (half_random_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames / 2, frames_processed);
- CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), &half_random_data[frames / 2], frames / 2));
- }
-
- {
- ProcessContext<float> c (zero_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames / 2, frames_processed);
- }
-
- {
- ProcessContext<float> c (half_random_data, frames, 1);
- trimmer->process (c);
- nframes_t frames_processed = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (2 * frames + frames / 2, frames_processed);
- CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames + frames / 2], half_random_data, frames));
- }
- }
-
- 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);
- }
- }
-
- void testAddSilenceBeginning()
- {
- trimmer->add_output (sink);
- AudioGrapher::Utils::init_zeros<float>(frames / 2);
-
- nframes_t silence = frames / 2;
- trimmer->add_silence_to_beginning (silence);
-
- {
- ProcessContext<float> c (random_data, frames, 1);
- trimmer->process (c);
- }
-
- CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), zero_data, silence));
- CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[silence], random_data, frames));
- }
-
- void testAddSilenceEnd()
- {
- trimmer->add_output (sink);
- AudioGrapher::Utils::init_zeros<float>(frames / 2);
-
- nframes_t silence = frames / 3;
- trimmer->add_silence_to_end (silence);
-
- {
- ProcessContext<float> c (random_data, frames, 1);
- trimmer->process (c);
- }
-
- {
- ProcessContext<float> c (random_data, frames, 1);
- c.set_flag (ProcessContext<float>::EndOfInput);
- trimmer->process (c);
- }
-
- 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));
- }
-
- private:
- boost::shared_ptr<SilenceTrimmer<float> > trimmer;
- boost::shared_ptr<AppendingVectorSink<float> > sink;
-
- float * random_data;
- float * zero_data;
- float * half_random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (SilenceTrimmerTest);
--- /dev/null
+#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);
+
+++ /dev/null
-#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);
-
+++ /dev/null
-#include "utils.h"
-#include "audiographer/sr_converter.h"
-
-using namespace AudioGrapher;
-
-class SampleRateConverterTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (SampleRateConverterTest);
- CPPUNIT_TEST (testNoConversion);
- CPPUNIT_TEST (testUpsampleLength);
- CPPUNIT_TEST (testDownsampleLength);
- CPPUNIT_TEST (testRespectsEndOfInput);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 128;
- random_data = TestUtils::init_random_data(frames);
- sink.reset (new AppendingVectorSink<float>());
- grabber.reset (new ProcessContextGrabber<float>());
- converter.reset (new SampleRateConverter (1));
- }
-
- void tearDown()
- {
- delete [] random_data;
- }
-
- void testNoConversion()
- {
- assert (frames % 2 == 0);
- nframes_t const half_frames = frames / 2;
- nframes_t frames_output = 0;
-
- converter->init (44100, 44100);
- converter->add_output (sink);
-
- ProcessContext<float> c (random_data, half_frames, 1);
- converter->process (c);
- ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
- c2.set_flag (ProcessContext<float>::EndOfInput);
- converter->process (c2);
-
- frames_output = sink->get_data().size();
- CPPUNIT_ASSERT_EQUAL (frames, frames_output);
-
- CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink->get_array(), frames));
- }
-
- void testUpsampleLength()
- {
- assert (frames % 2 == 0);
- nframes_t const half_frames = frames / 2;
- nframes_t frames_output = 0;
-
- converter->init (44100, 88200);
- converter->allocate_buffers (half_frames);
- converter->add_output (sink);
-
- ProcessContext<float> c (random_data, half_frames, 1);
- converter->process (c);
- ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
- c2.set_flag (ProcessContext<float>::EndOfInput);
- converter->process (c2);
-
- frames_output = sink->get_data().size();
- nframes_t tolerance = 3;
- CPPUNIT_ASSERT (2 * frames - tolerance < frames_output && frames_output < 2 * frames + tolerance);
- }
-
- void testDownsampleLength()
- {
- assert (frames % 2 == 0);
- nframes_t const half_frames = frames / 2;
- nframes_t frames_output = 0;
-
- converter->init (88200, 44100);
- converter->allocate_buffers (half_frames);
- converter->add_output (sink);
-
- ProcessContext<float> c (random_data, half_frames, 1);
- converter->process (c);
- ProcessContext<float> c2 (&random_data[half_frames], half_frames, 1);
- c2.set_flag (ProcessContext<float>::EndOfInput);
- converter->process (c2);
-
- frames_output = sink->get_data().size();
- nframes_t tolerance = 3;
- CPPUNIT_ASSERT (half_frames - tolerance < frames_output && frames_output < half_frames + tolerance);
- }
-
- void testRespectsEndOfInput()
- {
- assert (frames % 2 == 0);
- nframes_t const half_frames = frames / 2;
-
- converter->init (44100, 48000);
- converter->allocate_buffers (half_frames);
- converter->add_output (grabber);
-
- ProcessContext<float> c (random_data, half_frames, 1);
- converter->process (c);
- ProcessContext<float> c2 (&random_data[half_frames], half_frames / 2, 1);
- c2.set_flag (ProcessContext<float>::EndOfInput);
- converter->process (c2);
-
- for (std::list<ProcessContext<float> >::iterator it = grabber->contexts.begin(); it != grabber->contexts.end(); ++it) {
- std::list<ProcessContext<float> >::iterator next = it; ++next;
- if (next == grabber->contexts.end()) {
- CPPUNIT_ASSERT (it->has_flag (ProcessContext<float>::EndOfInput));
- } else {
- CPPUNIT_ASSERT (!it->has_flag (ProcessContext<float>::EndOfInput));
- }
- }
- }
-
-
- private:
- boost::shared_ptr<SampleRateConverter > converter;
- boost::shared_ptr<AppendingVectorSink<float> > sink;
- boost::shared_ptr<ProcessContextGrabber<float> > grabber;
-
- float * random_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (SampleRateConverterTest);
-
+++ /dev/null
-#include "utils.h"
-#include "audiographer/threader.h"
-
-using namespace AudioGrapher;
-
-class ThreaderTest : public CppUnit::TestFixture
-{
- CPPUNIT_TEST_SUITE (ThreaderTest);
- CPPUNIT_TEST (testProcess);
- CPPUNIT_TEST (testRemoveOutput);
- CPPUNIT_TEST (testClearOutputs);
- CPPUNIT_TEST (testExceptions);
- CPPUNIT_TEST_SUITE_END ();
-
- public:
- void setUp()
- {
- frames = 128;
- random_data = TestUtils::init_random_data (frames, 1.0);
-
- zero_data = new float[frames];
- memset (zero_data, 0, frames * sizeof(float));
-
- thread_pool = new Glib::ThreadPool (3);
- threader.reset (new Threader<float> (*thread_pool));
-
- sink_a.reset (new VectorSink<float>());
- sink_b.reset (new VectorSink<float>());
- sink_c.reset (new VectorSink<float>());
- sink_d.reset (new VectorSink<float>());
- sink_e.reset (new VectorSink<float>());
- sink_f.reset (new VectorSink<float>());
-
- throwing_sink.reset (new ThrowingSink<float>());
- }
-
- void tearDown()
- {
- delete [] random_data;
- delete [] zero_data;
-
- thread_pool->shutdown();
- delete thread_pool;
- }
-
- void testProcess()
- {
- threader->add_output (sink_a);
- threader->add_output (sink_b);
- threader->add_output (sink_c);
- threader->add_output (sink_d);
- threader->add_output (sink_e);
- threader->add_output (sink_f);
-
- ProcessContext<float> c (random_data, frames, 1);
- threader->process (c);
-
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_d->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
- }
-
- void testRemoveOutput()
- {
- threader->add_output (sink_a);
- threader->add_output (sink_b);
- threader->add_output (sink_c);
- threader->add_output (sink_d);
- threader->add_output (sink_e);
- threader->add_output (sink_f);
-
- ProcessContext<float> c (random_data, frames, 1);
- threader->process (c);
-
- // Remove a, b and f
- threader->remove_output (sink_a);
- threader->remove_output (sink_b);
- threader->remove_output (sink_f);
-
- ProcessContext<float> zc (zero_data, frames, 1);
- threader->process (zc);
-
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_c->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_d->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(zero_data, sink_e->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
- }
-
- void testClearOutputs()
- {
- threader->add_output (sink_a);
- threader->add_output (sink_b);
- threader->add_output (sink_c);
- threader->add_output (sink_d);
- threader->add_output (sink_e);
- threader->add_output (sink_f);
-
- ProcessContext<float> c (random_data, frames, 1);
- threader->process (c);
-
- threader->clear_outputs();
- ProcessContext<float> zc (zero_data, frames, 1);
- threader->process (zc);
-
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_d->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_f->get_array(), frames));
- }
-
- void testExceptions()
- {
- threader->add_output (sink_a);
- threader->add_output (sink_b);
- threader->add_output (sink_c);
- threader->add_output (throwing_sink);
- threader->add_output (sink_e);
- threader->add_output (throwing_sink);
-
- ProcessContext<float> c (random_data, frames, 1);
- CPPUNIT_ASSERT_THROW (threader->process (c), Exception);
-
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_a->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_b->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_c->get_array(), frames));
- CPPUNIT_ASSERT (TestUtils::array_equals(random_data, sink_e->get_array(), frames));
- }
-
- private:
- Glib::ThreadPool * thread_pool;
-
- boost::shared_ptr<Threader<float> > threader;
- boost::shared_ptr<VectorSink<float> > sink_a;
- boost::shared_ptr<VectorSink<float> > sink_b;
- boost::shared_ptr<VectorSink<float> > sink_c;
- boost::shared_ptr<VectorSink<float> > sink_d;
- boost::shared_ptr<VectorSink<float> > sink_e;
- boost::shared_ptr<VectorSink<float> > sink_f;
-
- boost::shared_ptr<ThrowingSink<float> > throwing_sink;
-
- float * random_data;
- float * zero_data;
- nframes_t frames;
-};
-
-CPPUNIT_TEST_SUITE_REGISTRATION (ThreaderTest);
-
--- /dev/null
+#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);
+
--- /dev/null
+#include "tests/utils.h"
+
+#include "audiographer/utils/identity_vertex.h"
+
+using namespace AudioGrapher;
+
+class IdentityVertexTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE (IdentityVertexTest);
+ CPPUNIT_TEST (testProcess);
+ CPPUNIT_TEST (testRemoveOutput);
+ CPPUNIT_TEST (testClearOutputs);
+ CPPUNIT_TEST_SUITE_END ();
+
+ public:
+ void setUp()
+ {
+ frames = 128;
+ random_data = TestUtils::init_random_data(frames);
+
+ zero_data = new float[frames];
+ memset (zero_data, 0, frames * sizeof(float));
+
+ sink_a.reset (new VectorSink<float>());
+ sink_b.reset (new VectorSink<float>());
+ }
+
+ void tearDown()
+ {
+ delete [] random_data;
+ delete [] zero_data;
+ }
+
+ void testProcess()
+ {
+ vertex.reset (new IdentityVertex<float>());
+ vertex->add_output (sink_a);
+ vertex->add_output (sink_b);
+
+ nframes_t frames_output = 0;
+
+ ProcessContext<float> c (random_data, frames, 1);
+ vertex->process (c);
+
+ frames_output = sink_a->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+
+ frames_output = sink_b->get_data().size();
+ CPPUNIT_ASSERT_EQUAL (frames, frames_output);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_b->get_array(), frames));
+ }
+
+ void testRemoveOutput()
+ {
+ vertex.reset (new IdentityVertex<float>());
+ vertex->add_output (sink_a);
+ vertex->add_output (sink_b);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ vertex->process (c);
+
+ vertex->remove_output (sink_a);
+ ProcessContext<float> zc (zero_data, frames, 1);
+ vertex->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (zero_data, sink_b->get_array(), frames));
+ }
+
+ void testClearOutputs()
+ {
+ vertex.reset (new IdentityVertex<float>());
+ vertex->add_output (sink_a);
+ vertex->add_output (sink_b);
+
+ ProcessContext<float> c (random_data, frames, 1);
+ vertex->process (c);
+
+ vertex->clear_outputs ();
+ ProcessContext<float> zc (zero_data, frames, 1);
+ vertex->process (zc);
+
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_a->get_array(), frames));
+ CPPUNIT_ASSERT (TestUtils::array_equals (random_data, sink_b->get_array(), frames));
+ }
+
+ private:
+ boost::shared_ptr<IdentityVertex<float> > vertex;
+ boost::shared_ptr<VectorSink<float> > sink_a;
+ boost::shared_ptr<VectorSink<float> > sink_b;
+
+ float * random_data;
+ float * zero_data;
+ nframes_t frames;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION (IdentityVertexTest);
+
# 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'
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'