From 830911f6f9451d83a58043b3f9084d3caa164b7b Mon Sep 17 00:00:00 2001 From: Sakari Bergen Date: Mon, 15 Mar 2010 19:11:48 +0000 Subject: [PATCH] Fix export, which has been broken since the boost::signals2 changes. Also update Audiographer, bacause of its incomplete sndfile handling. Audiographer is equal to revision 74 git-svn-id: svn://localhost/ardour2/branches/3.0@6760 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/export_format_base.h | 2 +- libs/ardour/ardour/export_graph_builder.h | 2 +- libs/ardour/export_graph_builder.cc | 43 +- libs/audiographer/README | 22 + libs/audiographer/audiographer/chunker.h | 54 --- libs/audiographer/audiographer/debug_utils.h | 6 + libs/audiographer/audiographer/debuggable.h | 27 +- .../audiographer/deinterleaver-inl.h | 78 ---- .../audiographer/audiographer/deinterleaver.h | 46 -- libs/audiographer/audiographer/exception.h | 3 + .../audiographer/flag_debuggable.h | 51 +++ libs/audiographer/audiographer/flag_field.h | 106 +++++ .../audiographer/general/chunker.h | 85 ++++ .../audiographer/general/deinterleaver.h | 109 +++++ .../audiographer/general/interleaver.h | 152 +++++++ .../audiographer/{ => general}/normalizer.h | 23 +- .../audiographer/{ => general}/peak_reader.h | 18 +- .../{ => general}/sample_format_converter.h | 17 +- .../{ => general}/silence_trimmer.h | 140 ++++-- .../audiographer/{ => general}/sr_converter.h | 25 +- .../audiographer/{ => general}/threader.h | 22 +- .../audiographer/interleaver-inl.h | 92 ---- libs/audiographer/audiographer/interleaver.h | 71 ---- .../audiographer/process_context.h | 62 ++- libs/audiographer/audiographer/routines.h | 36 +- libs/audiographer/audiographer/sink.h | 3 + .../audiographer/sndfile/sndfile.h | 30 ++ .../audiographer/sndfile/sndfile_base.h | 28 ++ .../audiographer/sndfile/sndfile_reader.h | 57 +++ .../audiographer/sndfile/sndfile_writer.h | 80 ++++ .../audiographer/sndfile/tmp_file.h | 27 ++ libs/audiographer/audiographer/sndfile_base.h | 31 -- .../audiographer/sndfile_reader.h | 40 -- .../audiographer/sndfile_writer.h | 29 -- libs/audiographer/audiographer/source.h | 8 + libs/audiographer/audiographer/throwing.h | 31 +- libs/audiographer/audiographer/tmp_file.h | 25 -- libs/audiographer/audiographer/type_utils.h | 38 +- libs/audiographer/audiographer/types.h | 26 +- libs/audiographer/audiographer/utils.h | 59 --- .../{ => utils}/identity_vertex.h | 7 +- .../audiographer/{ => utils}/listed_source.h | 7 +- libs/audiographer/doc/doxyfile | 284 +++++++++++++ libs/audiographer/doc/mainpage.dox | 26 ++ .../{src => private}/gdither/gdither.cc | 0 .../{src => private}/gdither/gdither.h | 0 .../{src => private}/gdither/gdither_types.h | 0 .../gdither/gdither_types_internal.h | 0 .../{src => private}/gdither/noise.h | 0 libs/audiographer/private/sndfile.hh | 399 ++++++++++++++++++ libs/audiographer/src/debug_utils.cc | 26 ++ .../{ => general}/sample_format_converter.cc | 55 +-- .../src/{ => general}/sr_converter.cc | 14 +- libs/audiographer/src/sndfile_base.cc | 56 --- libs/audiographer/src/sndfile_reader.cc | 68 --- libs/audiographer/src/sndfile_writer.cc | 74 ---- libs/audiographer/src/utils.cc | 14 - .../tests/{ => general}/chunker_test.cc | 40 +- .../tests/{ => general}/deinterleaver_test.cc | 24 +- .../interleaver_deinterleaver_test.cc | 19 +- .../tests/{ => general}/interleaver_test.cc | 36 +- .../tests/{ => general}/normalizer_test.cc | 6 +- .../tests/{ => general}/peak_reader_test.cc | 5 +- .../sample_format_converter_test.cc | 11 +- .../{ => general}/silence_trimmer_test.cc | 36 +- .../tests/{ => general}/sr_converter_test.cc | 5 +- .../tests/{ => general}/threader_test.cc | 5 +- .../tests/sndfile/tmp_file_test.cc | 47 +++ .../audiographer/tests/sndfile_writer_test.cc | 42 -- libs/audiographer/tests/type_utils_test.cc | 112 +++++ .../tests/{ => utils}/identity_vertex_test.cc | 5 +- libs/audiographer/wscript | 45 +- 72 files changed, 2182 insertions(+), 1090 deletions(-) create mode 100644 libs/audiographer/README delete mode 100644 libs/audiographer/audiographer/chunker.h delete mode 100644 libs/audiographer/audiographer/deinterleaver-inl.h delete mode 100644 libs/audiographer/audiographer/deinterleaver.h create mode 100644 libs/audiographer/audiographer/flag_debuggable.h create mode 100644 libs/audiographer/audiographer/flag_field.h create mode 100644 libs/audiographer/audiographer/general/chunker.h create mode 100644 libs/audiographer/audiographer/general/deinterleaver.h create mode 100644 libs/audiographer/audiographer/general/interleaver.h rename libs/audiographer/audiographer/{ => general}/normalizer.h (61%) rename libs/audiographer/audiographer/{ => general}/peak_reader.h (57%) rename libs/audiographer/audiographer/{ => general}/sample_format_converter.h (83%) rename libs/audiographer/audiographer/{ => general}/silence_trimmer.h (52%) rename libs/audiographer/audiographer/{ => general}/sr_converter.h (62%) rename libs/audiographer/audiographer/{ => general}/threader.h (78%) delete mode 100644 libs/audiographer/audiographer/interleaver-inl.h delete mode 100644 libs/audiographer/audiographer/interleaver.h create mode 100644 libs/audiographer/audiographer/sndfile/sndfile.h create mode 100644 libs/audiographer/audiographer/sndfile/sndfile_base.h create mode 100644 libs/audiographer/audiographer/sndfile/sndfile_reader.h create mode 100644 libs/audiographer/audiographer/sndfile/sndfile_writer.h create mode 100644 libs/audiographer/audiographer/sndfile/tmp_file.h delete mode 100644 libs/audiographer/audiographer/sndfile_base.h delete mode 100644 libs/audiographer/audiographer/sndfile_reader.h delete mode 100644 libs/audiographer/audiographer/sndfile_writer.h delete mode 100644 libs/audiographer/audiographer/tmp_file.h delete mode 100644 libs/audiographer/audiographer/utils.h rename libs/audiographer/audiographer/{ => utils}/identity_vertex.h (69%) rename libs/audiographer/audiographer/{ => utils}/listed_source.h (85%) create mode 100644 libs/audiographer/doc/doxyfile create mode 100644 libs/audiographer/doc/mainpage.dox rename libs/audiographer/{src => private}/gdither/gdither.cc (100%) rename libs/audiographer/{src => private}/gdither/gdither.h (100%) rename libs/audiographer/{src => private}/gdither/gdither_types.h (100%) rename libs/audiographer/{src => private}/gdither/gdither_types_internal.h (100%) rename libs/audiographer/{src => private}/gdither/noise.h (100%) create mode 100644 libs/audiographer/private/sndfile.hh create mode 100644 libs/audiographer/src/debug_utils.cc rename libs/audiographer/src/{ => general}/sample_format_converter.cc (68%) rename libs/audiographer/src/{ => general}/sr_converter.cc (93%) delete mode 100644 libs/audiographer/src/sndfile_base.cc delete mode 100644 libs/audiographer/src/sndfile_reader.cc delete mode 100644 libs/audiographer/src/sndfile_writer.cc delete mode 100644 libs/audiographer/src/utils.cc rename libs/audiographer/tests/{ => general}/chunker_test.cc (72%) rename libs/audiographer/tests/{ => general}/deinterleaver_test.cc (83%) rename libs/audiographer/tests/{ => general}/interleaver_deinterleaver_test.cc (90%) rename libs/audiographer/tests/{ => general}/interleaver_test.cc (75%) rename libs/audiographer/tests/{ => general}/normalizer_test.cc (91%) rename libs/audiographer/tests/{ => general}/peak_reader_test.cc (93%) rename libs/audiographer/tests/{ => general}/sample_format_converter_test.cc (96%) rename libs/audiographer/tests/{ => general}/silence_trimmer_test.cc (85%) rename libs/audiographer/tests/{ => general}/sr_converter_test.cc (98%) rename libs/audiographer/tests/{ => general}/threader_test.cc (98%) create mode 100644 libs/audiographer/tests/sndfile/tmp_file_test.cc delete mode 100644 libs/audiographer/tests/sndfile_writer_test.cc create mode 100644 libs/audiographer/tests/type_utils_test.cc rename libs/audiographer/tests/{ => utils}/identity_vertex_test.cc (97%) diff --git a/libs/ardour/ardour/export_format_base.h b/libs/ardour/ardour/export_format_base.h index 08bcbfb2bd..e372553f29 100644 --- a/libs/ardour/ardour/export_format_base.h +++ b/libs/ardour/ardour/export_format_base.h @@ -31,7 +31,7 @@ #include "ardour/ardour.h" -#include "audiographer/sample_format_converter.h" +#include "audiographer/general/sample_format_converter.h" namespace ARDOUR { diff --git a/libs/ardour/ardour/export_graph_builder.h b/libs/ardour/ardour/export_graph_builder.h index f98ffeb8eb..7bb5cf9aa1 100644 --- a/libs/ardour/ardour/export_graph_builder.h +++ b/libs/ardour/ardour/export_graph_builder.h @@ -26,7 +26,7 @@ #include "ardour/export_channel.h" #include "ardour/export_format_base.h" -#include "audiographer/identity_vertex.h" +#include "audiographer/utils/identity_vertex.h" #include diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc index 01c2934555..738f888b3d 100644 --- a/libs/ardour/export_graph_builder.cc +++ b/libs/ardour/export_graph_builder.cc @@ -1,16 +1,15 @@ #include "ardour/export_graph_builder.h" -#include "audiographer/interleaver.h" -#include "audiographer/normalizer.h" -#include "audiographer/peak_reader.h" #include "audiographer/process_context.h" -#include "audiographer/sample_format_converter.h" -#include "audiographer/sndfile_writer.h" -#include "audiographer/sr_converter.h" -#include "audiographer/silence_trimmer.h" -#include "audiographer/threader.h" -#include "audiographer/tmp_file.h" -#include "audiographer/utils.h" +#include "audiographer/general/interleaver.h" +#include "audiographer/general/normalizer.h" +#include "audiographer/general/peak_reader.h" +#include "audiographer/general/sample_format_converter.h" +#include "audiographer/general/sr_converter.h" +#include "audiographer/general/silence_trimmer.h" +#include "audiographer/general/threader.h" +#include "audiographer/sndfile/tmp_file.h" +#include "audiographer/sndfile/sndfile_writer.h" #include "ardour/audioengine.h" #include "ardour/export_channel_configuration.h" @@ -30,17 +29,11 @@ ExportGraphBuilder::ExportGraphBuilder (Session const & session) { process_buffer_frames = session.engine().frames_per_cycle(); process_buffer = new Sample[process_buffer_frames]; - - // TODO move and/or use global silent buffers - AudioGrapher::Utils::init_zeros (process_buffer_frames); } ExportGraphBuilder::~ExportGraphBuilder () { delete [] process_buffer; - - // TODO see bove - AudioGrapher::Utils::free_resources(); } int @@ -150,7 +143,7 @@ ExportGraphBuilder::Encoder::init_writer (boost::shared_ptrget_path (config.format); - writer.reset (new AudioGrapher::SndfileWriter (channels, config.format->sample_rate(), format, filename)); + writer.reset (new AudioGrapher::SndfileWriter (filename, format, channels, config.format->sample_rate())); writer->FileWritten.connect (sigc::mem_fun (*this, &ExportGraphBuilder::Encoder::copy_files)); } @@ -236,8 +229,8 @@ ExportGraphBuilder::Normalizer::init (FileSpec const & new_config, nframes_t /*m normalizer->add_output (threader); int format = ExportFormatBase::F_RAW | ExportFormatBase::SF_Float; - tmp_file.reset (new TmpFile (config.channel_config->get_n_chans(), - config.format->sample_rate(), format)); + tmp_file.reset (new TmpFile (format, config.channel_config->get_n_chans(), + config.format->sample_rate())); tmp_file->FileWritten.connect (sigc::hide (sigc::mem_fun (*this, &Normalizer::start_post_processing))); add_child (new_config); @@ -270,17 +263,16 @@ ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const bool ExportGraphBuilder::Normalizer::process() { - ProcessContext 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::SeekBeginning); + tmp_file->seek (0, SEEK_SET); + tmp_file->add_output (normalizer); parent.normalizers.push_back (this); } @@ -339,12 +331,11 @@ ExportGraphBuilder::SilenceHandler::init (FileSpec const & new_config, nframes_t max_frames_in = max_frames; nframes_t sample_rate = parent.session.nominal_frame_rate(); - silence_trimmer.reset (new SilenceTrimmer()); + silence_trimmer.reset (new SilenceTrimmer(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); diff --git a/libs/audiographer/README b/libs/audiographer/README new file mode 100644 index 0000000000..613b964d8f --- /dev/null +++ b/libs/audiographer/README @@ -0,0 +1,22 @@ +AudioGrapher is Copyright Sakari Bergen 2009-2010 + +AudioGrapher is best described as a signal flow management library. +It includes facilities to build graphs out of signal processing elements. +Once a graph is set up, all signal flow within the graph happens automatically. + +The data flow model in Audiographer is dynamic instead of synchronous - the type +and amount of data that goes in to a graph may differ from what comes out. +AudioGrapher is aimed mostly for usage by developers, as it includes lots of +facilities that ease the development process. + +The main aim of AudioGrapher is to ease development and debugging of signal flow +graphs. It makes heavy use of modern C++ techniques like templates, and uses the +boost libraries a lot. + +The essential classes in AudioGrapher are Sink, Source and ProcessContext. These +three define the signal flow in a graph. In addition, the core of AudioGrapher +includes lots of utility classes. + +AudioGrapher includes a bunch of ready Sink, Source and Vertex implementations. +Some are utilities used when developing more vertices, while others are general +utilities (file i/o, sample rate conversion etc). \ No newline at end of file diff --git a/libs/audiographer/audiographer/chunker.h b/libs/audiographer/audiographer/chunker.h deleted file mode 100644 index afce921cc2..0000000000 --- a/libs/audiographer/audiographer/chunker.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef AUDIOGRAPHER_CHUNKER_H -#define AUDIOGRAPHER_CHUNKER_H - -#include "listed_source.h" -#include "sink.h" -#include - -namespace AudioGrapher -{ - -template -class Chunker : public ListedSource, public Sink -{ - public: - Chunker (nframes_t chunk_size) - : chunk_size (chunk_size) - , position (0) - { - buffer = new T[chunk_size]; - } - - ~Chunker() - { - delete [] buffer; - } - - void process (ProcessContext 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 c_out (context, buffer, chunk_size); - ListedSource::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::process; - - private: - nframes_t chunk_size; - nframes_t position; - T * buffer; - -}; - -} // namespace - -#endif // AUDIOGRAPHER_CHUNKER_H - diff --git a/libs/audiographer/audiographer/debug_utils.h b/libs/audiographer/audiographer/debug_utils.h index 8b45f0d65c..a1d8259716 100644 --- a/libs/audiographer/audiographer/debug_utils.h +++ b/libs/audiographer/audiographer/debug_utils.h @@ -1,6 +1,8 @@ #ifndef AUDIOGRAPHER_DEBUG_UTILS_H #define AUDIOGRAPHER_DEBUG_UTILS_H +#include "flag_field.h" + #include #ifdef __GNUC__ @@ -10,8 +12,10 @@ namespace AudioGrapher { +/// Utilities for debugging struct DebugUtils { + /// Returns the demangled name of the object passed as the parameter template static std::string demangled_name (T const & obj) { @@ -27,6 +31,8 @@ struct DebugUtils return typeid(obj).name(); } + /// Returns name of ProcessContext::Flag + static std::string process_context_flag_name (FlagField::Flag flag); }; } // namespace diff --git a/libs/audiographer/audiographer/debuggable.h b/libs/audiographer/audiographer/debuggable.h index 4126327b86..5ef382890b 100644 --- a/libs/audiographer/audiographer/debuggable.h +++ b/libs/audiographer/audiographer/debuggable.h @@ -10,16 +10,31 @@ namespace AudioGrapher { +/// Compile time defined debug level enum DebugLevel { - DebugNone, //< Disabled - DebugObject, //< Object level stuff, ctors, initalizers etc. - DebugProcess, //< Process cycle level stuff - DebugVerbose, //< Lots of output, not on sample level - DebugSample //< Sample level stuff + DebugNone, ///< Disabled + DebugObject, ///< Object level stuff, ctors, initalizers etc. + DebugFlags, ///< Debug ProcessContext flags only on process cycle level + DebugProcess, ///< Process cycle level stuff + DebugVerbose, ///< Lots of output, not on sample level + DebugSample ///< Sample level stuff }; -/// Class that allows optimizing out debugging code during compile time +/** Class that allows optimizing out debugging code during compile time. + * Usage: to take all advantage of this class you should wrap all + * debugging statemets like this: + * \code + * if (debug_level (SomeDebugLevel) && other_optional_conditionals) { + * debug_stream() << "Debug output" << std::endl; + * } + * \endcode + * + * The order of the conditionals in the if-clause is important. + * The checks specified in \a other_optional_conditionals are only + * optimized out if \a debug_level() is placed before it with a + * logical and (short-circuiting). + */ template class Debuggable { diff --git a/libs/audiographer/audiographer/deinterleaver-inl.h b/libs/audiographer/audiographer/deinterleaver-inl.h deleted file mode 100644 index f93fdc53a4..0000000000 --- a/libs/audiographer/audiographer/deinterleaver-inl.h +++ /dev/null @@ -1,78 +0,0 @@ -template -DeInterleaver::DeInterleaver() - : channels (0) - , max_frames (0) - , buffer (0) - {} - -template -void -DeInterleaver::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)); - } -} - -template -typename DeInterleaver::SourcePtr -DeInterleaver::output (unsigned int channel) -{ - if (channel >= channels) { - throw Exception (*this, "channel out of range"); - } - - return boost::static_pointer_cast > (outputs[channel]); -} - -template -void -DeInterleaver::process (ProcessContext 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::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 c_out (c, buffer, frames_per_channel, 1); - (*it)->process (c_out); - } -} - -template -void -DeInterleaver::reset () -{ - outputs.clear(); - delete [] buffer; - buffer = 0; - channels = 0; - max_frames = 0; -} diff --git a/libs/audiographer/audiographer/deinterleaver.h b/libs/audiographer/audiographer/deinterleaver.h deleted file mode 100644 index 3a4aa53c43..0000000000 --- a/libs/audiographer/audiographer/deinterleaver.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef AUDIOGRAPHER_DEINTERLEAVER_H -#define AUDIOGRAPHER_DEINTERLEAVER_H - -#include "types.h" -#include "source.h" -#include "sink.h" -#include "identity_vertex.h" -#include "exception.h" - -#include - -namespace AudioGrapher -{ - -template -class DeInterleaver : public Sink -{ - private: - typedef boost::shared_ptr > OutputPtr; - - public: - DeInterleaver(); - ~DeInterleaver() { reset(); } - - typedef boost::shared_ptr > SourcePtr; - - void init (unsigned int num_channels, nframes_t max_frames_per_channel); - SourcePtr output (unsigned int channel); - void process (ProcessContext const & c); - using Sink::process; - - private: - - void reset (); - - std::vector outputs; - unsigned int channels; - nframes_t max_frames; - T * buffer; -}; - -#include "deinterleaver-inl.h" - -} // namespace - -#endif // AUDIOGRAPHER_DEINTERLEAVER_H diff --git a/libs/audiographer/audiographer/exception.h b/libs/audiographer/audiographer/exception.h index 43891db619..5583a2620b 100644 --- a/libs/audiographer/audiographer/exception.h +++ b/libs/audiographer/audiographer/exception.h @@ -11,6 +11,9 @@ namespace AudioGrapher { +/** AudioGrapher Exception class. + * Automatically tells which class an exception was thrown from. + */ class Exception : public std::exception { public: diff --git a/libs/audiographer/audiographer/flag_debuggable.h b/libs/audiographer/audiographer/flag_debuggable.h new file mode 100644 index 0000000000..b70480cd91 --- /dev/null +++ b/libs/audiographer/audiographer/flag_debuggable.h @@ -0,0 +1,51 @@ +#ifndef AUDIOGRAPHER_FLAG_DEBUGGABLE_H +#define AUDIOGRAPHER_FLAG_DEBUGGABLE_H + +#include "debuggable.h" +#include "debug_utils.h" +#include "process_context.h" +#include "types.h" + +#include + +namespace AudioGrapher +{ + +/// A debugging class for nodes that support a certain set of flags. +template +class FlagDebuggable : public Debuggable +{ + 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 + void check_flags (SelfType & self, ProcessContext context) + { + if (!Debuggable::debug_level (DebugFlags)) { return; } + FlagField unsupported = flags.unsupported_flags_of (context.flags()); + + for (FlagField::iterator it = unsupported.begin(); it != unsupported.end(); ++it) { + Debuggable::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 diff --git a/libs/audiographer/audiographer/flag_field.h b/libs/audiographer/audiographer/flag_field.h new file mode 100644 index 0000000000..df81aa1766 --- /dev/null +++ b/libs/audiographer/audiographer/flag_field.h @@ -0,0 +1,106 @@ +#ifndef AUDIOGRAPHER_FLAG_FIELD_H +#define AUDIOGRAPHER_FLAG_FIELD_H + +#include +#include +#include + +#include + +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 + , boost::equivalent + , boost::equality_comparable +{ + 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 + , public boost::less_than_comparable + , boost::equivalent + , boost::equality_comparable + { + 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 diff --git a/libs/audiographer/audiographer/general/chunker.h b/libs/audiographer/audiographer/general/chunker.h new file mode 100644 index 0000000000..38345acb1f --- /dev/null +++ b/libs/audiographer/audiographer/general/chunker.h @@ -0,0 +1,85 @@ +#ifndef AUDIOGRAPHER_CHUNKER_H +#define AUDIOGRAPHER_CHUNKER_H + +#include "audiographer/flag_debuggable.h" +#include "audiographer/sink.h" +#include "audiographer/type_utils.h" +#include "audiographer/utils/listed_source.h" + +namespace AudioGrapher +{ + +/// A class that chunks process cycles into equal sized frames +template +class Chunker + : public ListedSource + , public Sink + , 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::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 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::copy (&context.data()[input_position], &buffer[position], frames_to_copy); + + // Output whole buffer + ProcessContext c_out (context, buffer, chunk_size); + ListedSource::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::copy (&context.data()[input_position], &buffer[position], frames_left); + position += frames_left; + } + + if (context.has_flag (ProcessContext::EndOfInput)) { + ProcessContext c_out (context, buffer, position); + ListedSource::output (c_out); + } + } + using Sink::process; + + private: + nframes_t chunk_size; + nframes_t position; + T * buffer; + +}; + +} // namespace + +#endif // AUDIOGRAPHER_CHUNKER_H + diff --git a/libs/audiographer/audiographer/general/deinterleaver.h b/libs/audiographer/audiographer/general/deinterleaver.h new file mode 100644 index 0000000000..2a6ad64ef2 --- /dev/null +++ b/libs/audiographer/audiographer/general/deinterleaver.h @@ -0,0 +1,109 @@ +#ifndef AUDIOGRAPHER_DEINTERLEAVER_H +#define AUDIOGRAPHER_DEINTERLEAVER_H + +#include "audiographer/types.h" +#include "audiographer/source.h" +#include "audiographer/sink.h" +#include "audiographer/exception.h" +#include "audiographer/utils/identity_vertex.h" + +#include + +namespace AudioGrapher +{ + +/// Converts on stream of interleaved data to many streams of uninterleaved data. +template +class DeInterleaver + : public Sink + , public Throwing<> +{ + private: + typedef boost::shared_ptr > OutputPtr; + + public: + /// Constructor. \n RT safe + DeInterleaver() + : channels (0) + , max_frames (0) + , buffer (0) + {} + + ~DeInterleaver() { reset(); } + + typedef boost::shared_ptr > 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)); + } + } + + /// 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 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::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 c_out (c, buffer, frames_per_channel, 1); + (*it)->process (c_out); + } + } + + using Sink::process; + + private: + + void reset () + { + outputs.clear(); + delete [] buffer; + buffer = 0; + channels = 0; + max_frames = 0; + } + + std::vector outputs; + unsigned int channels; + nframes_t max_frames; + T * buffer; +}; + +} // namespace + +#endif // AUDIOGRAPHER_DEINTERLEAVER_H diff --git a/libs/audiographer/audiographer/general/interleaver.h b/libs/audiographer/audiographer/general/interleaver.h new file mode 100644 index 0000000000..98a71f3afd --- /dev/null +++ b/libs/audiographer/audiographer/general/interleaver.h @@ -0,0 +1,152 @@ +#ifndef AUDIOGRAPHER_INTERLEAVER_H +#define AUDIOGRAPHER_INTERLEAVER_H + +#include "audiographer/types.h" +#include "audiographer/sink.h" +#include "audiographer/exception.h" +#include "audiographer/throwing.h" +#include "audiographer/utils/listed_source.h" + +#include +#include + +namespace AudioGrapher +{ + +/// Interleaves many streams of non-interleaved data into one interleaved stream +template +class Interleaver + : public ListedSource + , 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::SinkPtr input (unsigned int channel) + { + if (throw_level (ThrowObject) && channel >= channels) { + throw Exception (*this, "Channel out of range"); + } + + return boost::static_pointer_cast > (inputs[channel]); + } + + private: + + class Input : public Sink + { + public: + Input (Interleaver & parent, unsigned int channel) + : frames_written (0), parent (parent), channel (channel) {} + + void process (ProcessContext 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::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 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 c_out (c, buffer, ready_frames, channels); + ListedSource::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 InputPtr; + std::vector inputs; + + unsigned int channels; + nframes_t max_frames; + T * buffer; +}; + +} // namespace + +#endif // AUDIOGRAPHER_INTERLEAVER_H diff --git a/libs/audiographer/audiographer/normalizer.h b/libs/audiographer/audiographer/general/normalizer.h similarity index 61% rename from libs/audiographer/audiographer/normalizer.h rename to libs/audiographer/audiographer/general/normalizer.h index dcaac75568..205f6e70e5 100644 --- a/libs/audiographer/audiographer/normalizer.h +++ b/libs/audiographer/audiographer/general/normalizer.h @@ -1,18 +1,23 @@ #ifndef AUDIOGRAPHER_NORMALIZER_H #define AUDIOGRAPHER_NORMALIZER_H -#include "listed_source.h" -#include "sink.h" -#include "routines.h" +#include "audiographer/sink.h" +#include "audiographer/routines.h" +#include "audiographer/utils/listed_source.h" #include namespace AudioGrapher { -class Normalizer : public ListedSource, Sink +/// A class for normalizing to a specified target in dB +class Normalizer + : public ListedSource + , public Sink + , public Throwing<> { public: + /// Constructs a normalizer with a specific target in dB \n RT safe Normalizer (float target_dB) : enabled (false) , buffer (0) @@ -26,6 +31,7 @@ class Normalizer : public ListedSource, Sink delete [] buffer; } + /// Sets the peak found in the material to be normalized \see PeakReader \n RT safe void set_peak (float peak) { if (peak == 0.0f || peak == target) { @@ -37,6 +43,11 @@ class Normalizer : public ListedSource, Sink } } + /** Allocates a buffer for using with const ProcessContexts + * This function does not need to be called if + * non-const ProcessContexts are given to \a process() . + * \n Not RT safe + */ void alloc_buffer(nframes_t frames) { delete [] buffer; @@ -44,9 +55,10 @@ class Normalizer : public ListedSource, Sink buffer_size = frames; } + /// Process a const ProcessContext \see alloc_buffer() \n RT safe void process (ProcessContext const & c) { - if (c.frames() > buffer_size) { + if (throw_level (ThrowProcess) && c.frames() > buffer_size) { throw Exception (*this, "Too many frames given to process()"); } @@ -59,6 +71,7 @@ class Normalizer : public ListedSource, Sink ListedSource::output (c_out); } + /// Process a non-const ProcsesContext in-place \n RT safe void process (ProcessContext & c) { if (enabled) { diff --git a/libs/audiographer/audiographer/peak_reader.h b/libs/audiographer/audiographer/general/peak_reader.h similarity index 57% rename from libs/audiographer/audiographer/peak_reader.h rename to libs/audiographer/audiographer/general/peak_reader.h index e5aaf7081c..a257621cde 100644 --- a/libs/audiographer/audiographer/peak_reader.h +++ b/libs/audiographer/audiographer/general/peak_reader.h @@ -1,32 +1,34 @@ #ifndef AUDIOGRAPHER_PEAK_READER_H #define AUDIOGRAPHER_PEAK_READER_H -#include "listed_source.h" -#include "sink.h" -#include "routines.h" +#include "audiographer/sink.h" +#include "audiographer/routines.h" +#include "audiographer/utils/listed_source.h" namespace AudioGrapher { +/// A class that reads the maximum value from a stream class PeakReader : public ListedSource, public Sink { 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 const & c) { peak = Routines::compute_peak (c.data(), c.frames(), peak); ListedSource::output(c); } - void process (ProcessContext & c) - { - peak = Routines::compute_peak (c.data(), c.frames(), peak); - ListedSource::output(c); - } + using Sink::process; private: float peak; diff --git a/libs/audiographer/audiographer/sample_format_converter.h b/libs/audiographer/audiographer/general/sample_format_converter.h similarity index 83% rename from libs/audiographer/audiographer/sample_format_converter.h rename to libs/audiographer/audiographer/general/sample_format_converter.h index 12976ffdd2..8dda57c4cc 100644 --- a/libs/audiographer/audiographer/sample_format_converter.h +++ b/libs/audiographer/audiographer/general/sample_format_converter.h @@ -1,9 +1,9 @@ #ifndef AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H #define AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H -#include "listed_source.h" -#include "sink.h" -#include "gdither/gdither_types.h" +#include "audiographer/sink.h" +#include "audiographer/utils/listed_source.h" +#include "private/gdither/gdither_types.h" namespace AudioGrapher { @@ -21,13 +21,16 @@ enum DitherType * This class can only convert floats to either \a float, \a int32_t, \a int16_t, or \a uint8_t */ template -class SampleFormatConverter : public Sink, public ListedSource +class SampleFormatConverter + : public Sink + , public ListedSource + , public Throwing<> { public: /** Constructor * \param channels number of channels in stream */ - SampleFormatConverter (uint32_t channels); + SampleFormatConverter (ChannelCount channels); ~SampleFormatConverter (); /** Initialize and allocate buffers for processing. @@ -51,9 +54,9 @@ class SampleFormatConverter : public Sink, public ListedSource private: void reset(); void init_common(nframes_t max_frames); // not-template-specialized part of init - void check_frame_count(nframes_t frames); + void check_frame_and_channel_count(nframes_t frames, ChannelCount channels_); - uint32_t channels; + ChannelCount channels; GDither dither; nframes_t data_out_size; TOut * data_out; diff --git a/libs/audiographer/audiographer/silence_trimmer.h b/libs/audiographer/audiographer/general/silence_trimmer.h similarity index 52% rename from libs/audiographer/audiographer/silence_trimmer.h rename to libs/audiographer/audiographer/general/silence_trimmer.h index 46190cfeae..5256d4a8cc 100644 --- a/libs/audiographer/audiographer/silence_trimmer.h +++ b/libs/audiographer/audiographer/general/silence_trimmer.h @@ -1,27 +1,59 @@ #ifndef AUDIOGRAPHER_SILENCE_TRIMMER_H #define AUDIOGRAPHER_SILENCE_TRIMMER_H -#include "listed_source.h" -#include "sink.h" -#include "exception.h" -#include "utils.h" +#include "audiographer/debug_utils.h" +#include "audiographer/flag_debuggable.h" +#include "audiographer/sink.h" +#include "audiographer/exception.h" +#include "audiographer/utils/listed_source.h" #include namespace AudioGrapher { -template -class SilenceTrimmer : public ListedSource, public Sink +/// Removes and adds silent frames to beginning and/or end of stream +template +class SilenceTrimmer + : public ListedSource + , public Sink + , public FlagDebuggable<> + , public Throwing<> { public: - SilenceTrimmer() + /// Constructor, \see reset() \n Not RT safe + SilenceTrimmer(nframes_t silence_buffer_size_ = 1024) + : silence_buffer_size (0) + , silence_buffer (0) { - reset (); + reset (silence_buffer_size_); + add_supported_flag (ProcessContext::EndOfInput); } - void reset() + ~SilenceTrimmer() { + delete [] silence_buffer; + } + + /** Reset state \n Not RT safe + * Allocates a buffer the size of \a silence_buffer_size_ + * This also defines the maximum length of output process context + * which can be output during long intermediate silence. + */ + void reset (nframes_t silence_buffer_size_ = 1024) + { + if (throw_level (ThrowObject) && silence_buffer_size_ == 0) { + throw Exception (*this, + "Silence trimmer constructor and reset() must be called with a non-zero parameter!"); + } + + if (silence_buffer_size != silence_buffer_size_) { + silence_buffer_size = silence_buffer_size_; + delete [] silence_buffer; + silence_buffer = new T[silence_buffer_size]; + TypeUtils::zero_fill (silence_buffer, silence_buffer_size); + } + in_beginning = true; in_end = false; trim_beginning = false; @@ -32,46 +64,71 @@ class SilenceTrimmer : public ListedSource, public Sink add_to_end = 0; } + /** Tells that \a frames_per_channel frames of silence per channel should be added to beginning + * Needs to be called before starting processing. + * \n RT safe + */ void add_silence_to_beginning (nframes_t frames_per_channel) { - if (!in_beginning) { + if (throw_level (ThrowObject) && !in_beginning) { throw Exception(*this, "Tried to add silence to beginning after already outputting data"); } add_to_beginning = frames_per_channel; } + /** Tells that \a frames_per_channel frames of silence per channel should be added to end + * Needs to be called before end is reached. + * \n RT safe + */ void add_silence_to_end (nframes_t frames_per_channel) { - if (in_end) { + if (throw_level (ThrowObject) && in_end) { throw Exception(*this, "Tried to add silence to end after already reaching end"); } add_to_end = frames_per_channel; } + /** Tells whether ot nor silence should be trimmed from the beginning + * Has to be called before starting processing. + * \n RT safe + */ void set_trim_beginning (bool yn) { - if (!in_beginning) { + if (throw_level (ThrowObject) && !in_beginning) { throw Exception(*this, "Tried to set beginning trim after already outputting data"); } trim_beginning = yn; } + /** Tells whether ot nor silence should be trimmed from the end + * Has to be called before the is reached. + * \n RT safe + */ void set_trim_end (bool yn) { - if (in_end) { + if (throw_level (ThrowObject) && in_end) { throw Exception(*this, "Tried to set end trim after already reaching end"); } trim_end = yn; } - - void limit_output_size (nframes_t max_frames) - { - max_output_frames = max_frames; - } + /** Process stream according to current settings. + * Note that some calls will not produce any output, + * while others may produce many. \see reset() + * \n RT safe + */ void process (ProcessContext const & c) { - if (in_end) { throw Exception(*this, "process() after reacing end of input"); } + if (debug_level (DebugVerbose)) { + debug_stream () << DebugUtils::demangled_name (*this) << + "::process()" << std::endl; + } + + check_flags (*this, c); + + if (throw_level (ThrowStrict) && in_end) { + throw Exception(*this, "process() after reacing end of input"); + } in_end = c.has_flag (ProcessContext::EndOfInput); nframes_t frame_index = 0; @@ -88,6 +145,12 @@ class SilenceTrimmer : public ListedSource, public Sink // 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 c_copy (c); if (has_data) { // There will be more output, so remove flag c_copy().remove_flag (ProcessContext::EndOfInput); @@ -100,6 +163,12 @@ class SilenceTrimmer : public ListedSource, public Sink // 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 c_out (c, &c.data()[frame_index], c.frames() - frame_index); ListedSource::output (c_out); @@ -108,20 +177,43 @@ class SilenceTrimmer : public ListedSource, public Sink } 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::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::output (c); } - // Finally if in end, add silence to end + // Finally, if in end, add silence to end if (in_end && add_to_end) { + + if (debug_level (DebugVerbose)) { + debug_stream () << DebugUtils::demangled_name (*this) << + " adding to end" << std::endl; + } + add_to_end *= c.channels(); output_silence_frames (c, add_to_end, true); } @@ -146,9 +238,6 @@ class SilenceTrimmer : public ListedSource, public Sink void output_silence_frames (ProcessContext const & c, nframes_t & total_frames, bool adding_to_end = false) { - nframes_t silence_buffer_size = Utils::get_zero_buffer_size(); - if (silence_buffer_size == 0) { throw Exception (*this, "Utils::init_zeros has not been called!"); } - bool end_of_input = c.has_flag (ProcessContext::EndOfInput); c.remove_flag (ProcessContext::EndOfInput); @@ -160,7 +249,7 @@ class SilenceTrimmer : public ListedSource, public Sink frames -= frames % c.channels(); total_frames -= frames; - ConstProcessContext c_out (c, Utils::get_zeros(frames), frames); + ConstProcessContext c_out (c, silence_buffer, frames); // boolean commentation :) bool const no_more_silence_will_be_added = adding_to_end || (add_to_end == 0); @@ -184,6 +273,9 @@ class SilenceTrimmer : public ListedSource, public Sink nframes_t add_to_beginning; nframes_t add_to_end; + + nframes_t silence_buffer_size; + T * silence_buffer; }; } // namespace diff --git a/libs/audiographer/audiographer/sr_converter.h b/libs/audiographer/audiographer/general/sr_converter.h similarity index 62% rename from libs/audiographer/audiographer/sr_converter.h rename to libs/audiographer/audiographer/general/sr_converter.h index a9bfc7c4c3..f28c8f6953 100644 --- a/libs/audiographer/audiographer/sr_converter.h +++ b/libs/audiographer/audiographer/general/sr_converter.h @@ -3,32 +3,39 @@ #include -#include "debuggable.h" -#include "listed_source.h" -#include "sink.h" -#include "throwing.h" -#include "types.h" +#include "audiographer/flag_debuggable.h" +#include "audiographer/sink.h" +#include "audiographer/throwing.h" +#include "audiographer/types.h" +#include "audiographer/utils/listed_source.h" namespace AudioGrapher { +/// Samplerate converter class SampleRateConverter : public ListedSource , public Sink - , public Debuggable<> + , public FlagDebuggable<> , public Throwing<> { public: + /// Constructor. \n RT safe SampleRateConverter (uint32_t channels); ~SampleRateConverter (); - // not RT safe + /// Init converter \n Not RT safe void init (nframes_t in_rate, nframes_t out_rate, int quality = 0); - // returns max amount of frames that will be output + /// Returns max amount of frames that will be output \n RT safe nframes_t allocate_buffers (nframes_t max_frames); - // could be RT safe (check libsamplerate to be sure) + /** Does sample rate conversion. + * Note that outpt size may vary a lot. + * May or may not output several contexts of data. + * \n Should be RT safe. + * \TODO Check RT safety from libsamplerate + */ void process (ProcessContext const & c); using Sink::process; diff --git a/libs/audiographer/audiographer/threader.h b/libs/audiographer/audiographer/general/threader.h similarity index 78% rename from libs/audiographer/audiographer/threader.h rename to libs/audiographer/audiographer/general/threader.h index ad6a542126..e5a4e744cc 100644 --- a/libs/audiographer/audiographer/threader.h +++ b/libs/audiographer/audiographer/general/threader.h @@ -9,13 +9,14 @@ #include #include -#include "source.h" -#include "sink.h" -#include "exception.h" +#include "audiographer/source.h" +#include "audiographer/sink.h" +#include "audiographer/exception.h" namespace AudioGrapher { +/// Class that stores exceptions thrown from different threads class ThreaderException : public Exception { public: @@ -28,7 +29,8 @@ class ThreaderException : public Exception { } }; -template +/// Class for distributing processing across several threads +template class Threader : public Source, public Sink { private: @@ -36,6 +38,11 @@ class Threader : public Source, public Sink public: + /** Constructor + * \n RT safe + * \param thread_pool a thread pool from which all tasks are scheduled + * \param wait_timeout_milliseconds maximum time allowed for threads to use in processing + */ Threader (Glib::ThreadPool & thread_pool, long wait_timeout_milliseconds = 1000) : thread_pool (thread_pool) , readers (0) @@ -44,14 +51,19 @@ class Threader : public Source, public Sink virtual ~Threader () {} + /// Adds output \n RT safe void add_output (typename Source::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::SinkPtr output) { typename OutputVec::iterator new_end = std::remove(outputs.begin(), outputs.end(), output); outputs.erase (new_end, outputs.end()); } - /* The context has to be const, because this is working concurrently */ + /// Processes context concurrently by scheduling each output separately to the given thread pool void process (ProcessContext const & c) { wait_mutex.lock(); diff --git a/libs/audiographer/audiographer/interleaver-inl.h b/libs/audiographer/audiographer/interleaver-inl.h deleted file mode 100644 index 07e93b2a85..0000000000 --- a/libs/audiographer/audiographer/interleaver-inl.h +++ /dev/null @@ -1,92 +0,0 @@ -template -Interleaver::Interleaver() - : channels (0) - , max_frames (0) - , buffer (0) -{} - -template -void -Interleaver::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 Source::SinkPtr -Interleaver::input (unsigned int channel) -{ - if (channel >= channels) { - throw Exception (*this, "Channel out of range"); - } - - return boost::static_pointer_cast > (inputs[channel]); -} - -template -void -Interleaver::reset_channels () -{ - for (unsigned int i = 0; i < channels; ++i) { - inputs[i]->reset(); - } - -} - -template -void -Interleaver::reset () -{ - inputs.clear(); - delete [] buffer; - buffer = 0; - channels = 0; - max_frames = 0; -} - -template -void -Interleaver::write_channel (ProcessContext 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 c_out (c, buffer, ready_frames, channels); - ListedSource::output (c_out); - reset_channels (); - } -} - -template -nframes_t -Interleaver::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; -} diff --git a/libs/audiographer/audiographer/interleaver.h b/libs/audiographer/audiographer/interleaver.h deleted file mode 100644 index 3d51fed5a5..0000000000 --- a/libs/audiographer/audiographer/interleaver.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef AUDIOGRAPHER_INTERLEAVER_H -#define AUDIOGRAPHER_INTERLEAVER_H - -#include "types.h" -#include "listed_source.h" -#include "sink.h" -#include "exception.h" - -#include -#include - -namespace AudioGrapher -{ - -template -class Interleaver : public ListedSource -{ - public: - - Interleaver(); - ~Interleaver() { reset(); } - - void init (unsigned int num_channels, nframes_t max_frames_per_channel); - typename Source::SinkPtr input (unsigned int channel); - - private: - - class Input : public Sink - { - public: - Input (Interleaver & parent, unsigned int channel) - : frames_written (0), parent (parent), channel (channel) {} - - void process (ProcessContext 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::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 const & c, unsigned int channel); - nframes_t ready_to_output(); - void output(); - - typedef boost::shared_ptr InputPtr; - std::vector inputs; - - unsigned int channels; - nframes_t max_frames; - T * buffer; -}; - -#include "interleaver-inl.h" - -} // namespace - -#endif // AUDIOGRAPHER_INTERLEAVER_H diff --git a/libs/audiographer/audiographer/process_context.h b/libs/audiographer/audiographer/process_context.h index 654da2565a..3ad84d7708 100644 --- a/libs/audiographer/audiographer/process_context.h +++ b/libs/audiographer/audiographer/process_context.h @@ -3,8 +3,13 @@ #include #include +#include +#include "exception.h" +#include "debug_utils.h" #include "types.h" +#include "flag_field.h" +#include "throwing.h" #include "type_utils.h" namespace AudioGrapher @@ -14,8 +19,10 @@ namespace AudioGrapher * Processing context. Constness only applies to data, not flags */ -template -class ProcessContext { +template +class ProcessContext + : public Throwing<> +{ BOOST_STATIC_ASSERT (boost::has_trivial_destructor::value); @@ -31,26 +38,44 @@ public: /// Basic constructor with data, frame and channel count ProcessContext (T * data, nframes_t frames, ChannelCount channels) - : _data (data), _frames (frames), _channels (channels) {} + : _data (data), _frames (frames), _channels (channels) + { validate_data(); } /// Normal copy constructor ProcessContext (ProcessContext 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 ProcessContext (ProcessContext 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 ProcessContext (ProcessContext 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 ProcessContext (ProcessContext const & other, T * data) - : _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags()) {} + : _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags()) + { /* No need to validate data */ } + + /// Make new Context out of the beginning of this context + ProcessContext beginning (nframes_t frames) + { + if (throw_level (ThrowProcess) && frames > _frames) { + throw Exception (*this, boost::str (boost::format + ("Trying to use too many frames of %1% for a new Context: %2% instead of %3%") + % DebugUtils::demangled_name (*this) % frames % _frames)); + } + validate_data (); + + return ProcessContext (*this, _data, frames); + } virtual ~ProcessContext () {} @@ -60,13 +85,11 @@ public: /// \a frames tells how many frames the array pointed by data contains inline nframes_t const & frames() const { return _frames; } - inline nframes_t & frames() { return _frames; } /** \a channels tells how many interleaved channels \a data contains * If \a channels is greater than 1, each channel contains \a frames / \a channels frames of data */ inline ChannelCount const & channels() const { return _channels; } - inline ChannelCount & channels() { return _channels; } /// Returns the amount of frames per channel inline nframes_t frames_per_channel() const { return _frames / _channels; } @@ -84,20 +107,35 @@ protected: ChannelCount _channels; mutable FlagField _flags; + + private: + inline void validate_data() + { + if (throw_level (ThrowProcess) && (_frames % _channels != 0)) { + throw Exception (*this, boost::str (boost::format + ("Number of frames given to %1% was not a multiple of channels: %2% frames with %3% channels") + % DebugUtils::demangled_name (*this) % _frames % _channels)); + } + } }; /// A process context that allocates and owns it's data buffer -template +template struct AllocatingProcessContext : public ProcessContext { /// Allocates uninitialized memory AllocatingProcessContext (nframes_t frames, ChannelCount channels) : ProcessContext (new T[frames], frames, channels) {} + /// Allocates and copies data from raw buffer + AllocatingProcessContext (T const * data, nframes_t frames, ChannelCount channels) + : ProcessContext (new T[frames], frames, channels) + { TypeUtils::copy (data, ProcessContext::_data, frames); } + /// Copy constructor, copies data from other ProcessContext AllocatingProcessContext (ProcessContext const & other) : ProcessContext (other, new T[other._frames]) - { memcpy (ProcessContext::_data, other._data, other._channels * other._frames * sizeof (T)); } + { TypeUtils::copy (ProcessContext::_data, other._data, other._frames); } /// "Copy constructor" with uninitialized data, unique frame and channel count, but copies flags template @@ -118,7 +156,7 @@ struct AllocatingProcessContext : public ProcessContext }; /// A wrapper for a const ProcesContext which can be created from const data -template +template class ConstProcessContext { public: diff --git a/libs/audiographer/audiographer/routines.h b/libs/audiographer/audiographer/routines.h index fd077e1b3f..b3b7f0921b 100644 --- a/libs/audiographer/audiographer/routines.h +++ b/libs/audiographer/audiographer/routines.h @@ -5,43 +5,57 @@ #include -#define routines_nframes_t uint32_t - namespace AudioGrapher { +/// Allows overriding some routines with more efficient ones. class Routines { public: - typedef float (*compute_peak_t) (float const *, routines_nframes_t, float); - typedef void (*apply_gain_to_buffer_t) (float *, routines_nframes_t, float); + typedef uint32_t uint_type; + + typedef float (*compute_peak_t) (float const *, uint_type, float); + typedef void (*apply_gain_to_buffer_t) (float *, uint_type, float); static void override_compute_peak (compute_peak_t func) { _compute_peak = func; } static void override_apply_gain_to_buffer (apply_gain_to_buffer_t func) { _apply_gain_to_buffer = func; } - static inline float compute_peak (float const * data, routines_nframes_t frames, float current_peak) + /** Computes peak in float buffer + * \n RT safe + * \param data buffer from which the peak is computed + * \param frames length of the portion of \a buffer that is checked + * \param current_peak current peak of buffer, if calculated in several passes + * \return maximum of values in [\a data, \a data + \a frames) and \a current_peak + */ + static inline float compute_peak (float const * data, uint_type frames, float current_peak) { return (*_compute_peak) (data, frames, current_peak); } - static inline void apply_gain_to_buffer (float * data, routines_nframes_t frames, float gain) + /** Applies constant gain to buffer + * \n RT safe + * \param data data to which the gain is applied + * \param frames length of data + * \param gain gain that is applied + */ + static inline void apply_gain_to_buffer (float * data, uint_type frames, float gain) { (*_apply_gain_to_buffer) (data, frames, gain); } private: - static inline float default_compute_peak (float const * data, routines_nframes_t frames, float current_peak) + static inline float default_compute_peak (float const * data, uint_type frames, float current_peak) { - for (routines_nframes_t i = 0; i < frames; ++i) { + for (uint_type i = 0; i < frames; ++i) { float abs = std::fabs(data[i]); if (abs > current_peak) { current_peak = abs; } } return current_peak; } - static inline void default_apply_gain_to_buffer (float * data, routines_nframes_t frames, float gain) + static inline void default_apply_gain_to_buffer (float * data, uint_type frames, float gain) { - for (routines_nframes_t i = 0; i < frames; ++i) { + for (uint_type i = 0; i < frames; ++i) { data[i] *= gain; } } @@ -52,6 +66,4 @@ class Routines } // namespace -#undef routines_nframes_t - #endif // AUDIOGRAPHER_ROUTINES_H diff --git a/libs/audiographer/audiographer/sink.h b/libs/audiographer/audiographer/sink.h index 486fccfd13..0a7004464e 100644 --- a/libs/audiographer/audiographer/sink.h +++ b/libs/audiographer/audiographer/sink.h @@ -8,6 +8,9 @@ namespace AudioGrapher { +/** A sink for data + * This is a pure virtual interface for all data sinks in AudioGrapher + */ template class Sink { public: diff --git a/libs/audiographer/audiographer/sndfile/sndfile.h b/libs/audiographer/audiographer/sndfile/sndfile.h new file mode 100644 index 0000000000..4db2298a90 --- /dev/null +++ b/libs/audiographer/audiographer/sndfile/sndfile.h @@ -0,0 +1,30 @@ +#ifndef AUDIOGRAPHER_SNDFILE_H +#define AUDIOGRAPHER_SNDFILE_H + +#include "sndfile_writer.h" +#include "sndfile_reader.h" + +namespace AudioGrapher +{ + +/** Reader/Writer for audio files using libsndfile. + * Only short, int and float are valid template parameters + */ +template +class Sndfile : public SndfileWriter, public SndfileReader +{ + 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 diff --git a/libs/audiographer/audiographer/sndfile/sndfile_base.h b/libs/audiographer/audiographer/sndfile/sndfile_base.h new file mode 100644 index 0000000000..02d5a7f6ea --- /dev/null +++ b/libs/audiographer/audiographer/sndfile/sndfile_base.h @@ -0,0 +1,28 @@ +#ifndef AUDIOGRAPHER_SNDFILE_BASE_H +#define AUDIOGRAPHER_SNDFILE_BASE_H + +// We need to use our modified version until +// the fd patch is accepted upstream +#include "private/sndfile.hh" + +namespace AudioGrapher +{ + +/// Base class for all classes using libsndfile +class SndfileBase : public virtual AudioGrapher::SndfileHandle +{ + public: + enum Mode + { + Read = SFM_READ, + Write = SFM_WRITE, + ReadWrite = SFM_RDWR + }; + + protected: + SndfileBase () {} +}; + +} // namespace + +#endif // AUDIOGRAPHER_SNDFILE_BASE_H \ No newline at end of file diff --git a/libs/audiographer/audiographer/sndfile/sndfile_reader.h b/libs/audiographer/audiographer/sndfile/sndfile_reader.h new file mode 100644 index 0000000000..f5f7adcb3a --- /dev/null +++ b/libs/audiographer/audiographer/sndfile/sndfile_reader.h @@ -0,0 +1,57 @@ +#ifndef AUDIOGRAPHER_SNDFILE_READER_H +#define AUDIOGRAPHER_SNDFILE_READER_H + +#include "audiographer/utils/listed_source.h" +#include "audiographer/process_context.h" +#include "audiographer/sndfile/sndfile_base.h" + +namespace AudioGrapher +{ + +/** Reader for audio files using libsndfile. + * Only short, int and float are valid template parameters + */ +template +class SndfileReader + : public virtual SndfileBase + , public ListedSource + , 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 & 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 c_out = context.beginning (frames_read); + + if (frames_read < context.frames()) { + c_out.set_flag (ProcessContext::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 diff --git a/libs/audiographer/audiographer/sndfile/sndfile_writer.h b/libs/audiographer/audiographer/sndfile/sndfile_writer.h new file mode 100644 index 0000000000..aa06a2c3ef --- /dev/null +++ b/libs/audiographer/audiographer/sndfile/sndfile_writer.h @@ -0,0 +1,80 @@ +#ifndef AUDIOGRAPHER_SNDFILE_WRITER_H +#define AUDIOGRAPHER_SNDFILE_WRITER_H + +#include +#include +#include + +#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 +class SndfileWriter + : public virtual SndfileBase + , public Sink + , 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::EndOfInput); + } + + virtual ~SndfileWriter () {} + + SndfileWriter (SndfileWriter const & other) : SndfileHandle (other) {} + using SndfileHandle::operator=; + + /// Writes data to file + void process (ProcessContext 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::EndOfInput)) { + writeSync(); + FileWritten (path); + } + } + + using Sink::process; + + boost::signals2::signal FileWritten; + + protected: + /// SndfileHandle has to be constructed directly by deriving classes + SndfileWriter () + { + add_supported_flag (ProcessContext::EndOfInput); + } + + protected: + std::string path; +}; + +} // namespace + +#endif // AUDIOGRAPHER_SNDFILE_WRITER_H \ No newline at end of file diff --git a/libs/audiographer/audiographer/sndfile/tmp_file.h b/libs/audiographer/audiographer/sndfile/tmp_file.h new file mode 100644 index 0000000000..e312007247 --- /dev/null +++ b/libs/audiographer/audiographer/sndfile/tmp_file.h @@ -0,0 +1,27 @@ +#ifndef AUDIOGRAPHER_TMP_FILE_H +#define AUDIOGRAPHER_TMP_FILE_H + +#include "sndfile_writer.h" +#include "sndfile_reader.h" + +namespace AudioGrapher +{ + +/// A temporary file deleted after this class is destructed +template +class TmpFile : public SndfileWriter, public SndfileReader +{ + 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 diff --git a/libs/audiographer/audiographer/sndfile_base.h b/libs/audiographer/audiographer/sndfile_base.h deleted file mode 100644 index 7f8da752e8..0000000000 --- a/libs/audiographer/audiographer/sndfile_base.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef AUDIOGRAPHER_SNDFILE_BASE_H -#define AUDIOGRAPHER_SNDFILE_BASE_H - -#include -#include -#include - -#include "types.h" -#include "debuggable.h" - -namespace AudioGrapher { - -/// Common interface for templated libsndfile readers/writers -class SndfileBase : public Debuggable<> -{ - public: - - sigc::signal 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 diff --git a/libs/audiographer/audiographer/sndfile_reader.h b/libs/audiographer/audiographer/sndfile_reader.h deleted file mode 100644 index 9e47da56b2..0000000000 --- a/libs/audiographer/audiographer/sndfile_reader.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef AUDIOGRAPHER_SNDFILE_READER_H -#define AUDIOGRAPHER_SNDFILE_READER_H - -#include "sndfile_base.h" -#include "listed_source.h" -#include "process_context.h" - -namespace AudioGrapher -{ - -/** Reader for audio files using libsndfile. - * Once again only short, int and float are valid template parameters - */ -template -class SndfileReader : public virtual SndfileBase, public ListedSource -{ - 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 & 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 diff --git a/libs/audiographer/audiographer/sndfile_writer.h b/libs/audiographer/audiographer/sndfile_writer.h deleted file mode 100644 index a92da982c1..0000000000 --- a/libs/audiographer/audiographer/sndfile_writer.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef AUDIOGRAPHER_SNDFILE_WRITER_H -#define AUDIOGRAPHER_SNDFILE_WRITER_H - -#include "sndfile_base.h" -#include "types.h" -#include "sink.h" - -namespace AudioGrapher -{ - -/// Template parameter specific parts of sndfile writer -template -class SndfileWriter : public virtual SndfileBase, public Sink -{ - public: - SndfileWriter (ChannelCount channels, nframes_t samplerate, int format, std::string const & path); - - void process (ProcessContext const & c); - using Sink::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 diff --git a/libs/audiographer/audiographer/source.h b/libs/audiographer/audiographer/source.h index 8ffad204ba..deefb900ee 100644 --- a/libs/audiographer/audiographer/source.h +++ b/libs/audiographer/audiographer/source.h @@ -9,6 +9,9 @@ namespace AudioGrapher { +/** A source for data + * This is a pure virtual interface for all data sources in AudioGrapher + */ template class Source { @@ -17,8 +20,13 @@ class Source typedef boost::shared_ptr > 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; }; diff --git a/libs/audiographer/audiographer/throwing.h b/libs/audiographer/audiographer/throwing.h index 05a056d5e9..eefade1db0 100644 --- a/libs/audiographer/audiographer/throwing.h +++ b/libs/audiographer/audiographer/throwing.h @@ -8,16 +8,35 @@ namespace AudioGrapher { +/** Compile time defined throw level. + * Throw levels less than ThrowStrict should be used with caution. + * Not throwing could mean getting a segfault. + * However, if you want ultra-optimized code and/or don't care about handling + * error situations, feel free to use whatever you want. + */ enum ThrowLevel { - ThrowNone, //< Not allowed to throw - ThrowObject, //< Object level stuff, ctors, initalizers etc. - ThrowProcess, //< Process cycle level stuff - ThrowStrict, //< Stricter checks than ThrowProcess, less than ThrowSample - ThrowSample //< Sample level stuff + ThrowNone, ///< Not allowed to throw + ThrowObject, ///< Object level stuff, ctors, initalizers etc. + ThrowProcess, ///< Process cycle level stuff + ThrowStrict, ///< Stricter checks than ThrowProcess, less than ThrowSample + ThrowSample ///< Sample level stuff }; -/// Class that allows optimizing out error checking during compile time +/** Class that allows optimizing out error checking during compile time. + * Usage: to take all advantage of this class you should wrap all + * throwing statemets like this: + * \code + * if (throw_level (SomeThrowLevel) && other_optional_conditionals) { + * throw Exception (...); + * } + * \endcode + * + * The order of the conditionals in the if-clause is important. + * The checks specified in \a other_optional_conditionals are only + * optimized out if \a throw_level() is placed before it with a + * logical and (short-circuiting). + */ template class Throwing { diff --git a/libs/audiographer/audiographer/tmp_file.h b/libs/audiographer/audiographer/tmp_file.h deleted file mode 100644 index a5537e3a69..0000000000 --- a/libs/audiographer/audiographer/tmp_file.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef AUDIOGRAPHER_TMP_FILE_H -#define AUDIOGRAPHER_TMP_FILE_H - -#include "sndfile_writer.h" -#include "sndfile_reader.h" - -namespace AudioGrapher -{ - -template -class TmpFile : public SndfileWriter, public SndfileReader -{ - public: - - TmpFile (ChannelCount channels, nframes_t samplerate, int format) - : SndfileBase (channels, samplerate, format, "temp") - , SndfileWriter (channels, samplerate, format, "temp") - , SndfileReader (channels, samplerate, format, "temp") - {} - -}; - -} // namespace - -#endif // AUDIOGRAPHER_TMP_FILE_H \ No newline at end of file diff --git a/libs/audiographer/audiographer/type_utils.h b/libs/audiographer/audiographer/type_utils.h index 84a28f3d07..d17f3634ed 100644 --- a/libs/audiographer/audiographer/type_utils.h +++ b/libs/audiographer/audiographer/type_utils.h @@ -1,7 +1,7 @@ #ifndef AUDIOGRAPHER_TYPE_UTILS_H #define AUDIOGRAPHER_TYPE_UTILS_H -#include "types.h" +#include "audiographer/types.h" #include #include #include @@ -10,22 +10,22 @@ namespace AudioGrapher { +/// Non-template base class for TypeUtils class TypeUtilsBase { protected: template - static void do_fill(T * buffer, nframes_t frames, const boost::integral_constant&) + static void do_zero_fill(T * buffer, nframes_t frames, const boost::integral_constant&) { std::uninitialized_fill_n (buffer, frames, T()); } template - 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 +/// Utilities for initializing, copying, moving, etc. data +template class TypeUtils : private TypeUtilsBase { BOOST_STATIC_ASSERT (boost::has_trivial_destructor::value); @@ -34,23 +34,33 @@ class TypeUtils : private TypeUtilsBase boost::is_floating_point::value || boost::is_signed::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: - }; diff --git a/libs/audiographer/audiographer/types.h b/libs/audiographer/audiographer/types.h index 5d31899748..558bab302f 100644 --- a/libs/audiographer/audiographer/types.h +++ b/libs/audiographer/audiographer/types.h @@ -8,30 +8,8 @@ namespace AudioGrapher { typedef int64_t nframes_t; typedef uint8_t ChannelCount; -/** Flag field capable of holding 32 flags. - Easily grown in size to 64 flags by changing storage_type */ -class FlagField { - public: - typedef uint8_t Flag; - typedef uint32_t storage_type; - - FlagField() : _flags (0) {} - FlagField(FlagField const & other) : _flags (other._flags) {} - - inline bool has (Flag flag) const { return _flags & (1 << flag); } - inline storage_type flags () const { return _flags; } - inline operator bool() const { return _flags; } - inline void set (Flag flag) { _flags |= (1 << flag); } - inline void remove (Flag flag) { _flags &= ~(1 << flag); } - inline void reset () { _flags = 0; } - - inline FlagField & operator+= (FlagField const & other) { _flags |= other._flags; return *this; } - inline bool operator== (FlagField const & other) const { return _flags == other._flags; } - - private: - storage_type _flags; -}; +typedef float DefaultSampleType; } // namespace -#endif // __audiographer_types_h__ \ No newline at end of file +#endif // AUDIOGRAPHER_TYPES_H \ No newline at end of file diff --git a/libs/audiographer/audiographer/utils.h b/libs/audiographer/audiographer/utils.h deleted file mode 100644 index 2f9c51918b..0000000000 --- a/libs/audiographer/audiographer/utils.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef AUDIOGRAPHER_UTILS_H -#define AUDIOGRAPHER_UTILS_H - -#include "types.h" -#include "exception.h" - -#include - -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 - 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(buffer); - } else { - zeros = new char[n_zeros]; - memset (const_cast(zeros), 0, n_zeros); - } - num_zeros = n_zeros; - } - - template - 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 (zeros); - } - - template - 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 diff --git a/libs/audiographer/audiographer/identity_vertex.h b/libs/audiographer/audiographer/utils/identity_vertex.h similarity index 69% rename from libs/audiographer/audiographer/identity_vertex.h rename to libs/audiographer/audiographer/utils/identity_vertex.h index b53bd96851..147939a7c5 100644 --- a/libs/audiographer/audiographer/identity_vertex.h +++ b/libs/audiographer/audiographer/utils/identity_vertex.h @@ -1,13 +1,14 @@ #ifndef AUDIOGRAPHER_IDENTITY_VERTEX_H #define AUDIOGRAPHER_IDENTITY_VERTEX_H -#include "listed_source.h" -#include "sink.h" +#include "audiographer/utils/listed_source.h" +#include "audiographer/sink.h" namespace AudioGrapher { -template +/// Outputs its input directly to a number of Sinks +template class IdentityVertex : public ListedSource, Sink { public: diff --git a/libs/audiographer/audiographer/listed_source.h b/libs/audiographer/audiographer/utils/listed_source.h similarity index 85% rename from libs/audiographer/audiographer/listed_source.h rename to libs/audiographer/audiographer/utils/listed_source.h index bc8f144d84..b030281207 100644 --- a/libs/audiographer/audiographer/listed_source.h +++ b/libs/audiographer/audiographer/utils/listed_source.h @@ -1,15 +1,16 @@ #ifndef AUDIOGRAPHER_LISTED_SOURCE_H #define AUDIOGRAPHER_LISTED_SOURCE_H -#include "types.h" -#include "source.h" +#include "audiographer/types.h" +#include "audiographer/source.h" #include namespace AudioGrapher { -template +/// An generic \a Source that uses a \a std::list for managing outputs +template class ListedSource : public Source { public: diff --git a/libs/audiographer/doc/doxyfile b/libs/audiographer/doc/doxyfile new file mode 100644 index 0000000000..f44dfeced1 --- /dev/null +++ b/libs/audiographer/doc/doxyfile @@ -0,0 +1,284 @@ + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = AudioGrapher +PROJECT_NUMBER = +OUTPUT_DIRECTORY = ./doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = ./ +STRIP_FROM_INC_PATH = ./ +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = YES +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 4 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +BUILTIN_STL_SUPPORT = YES +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = NO +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = audiographer \ + src \ + doc/mainpage.dox +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = *.h \ + *.cc +RECURSIVE = YES +EXCLUDE = src/gdither +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +HTML_DYNAMIC_SECTIONS = NO +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_BUNDLE_ID = org.doxygen.Project +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +USE_INLINE_TREES = NO +TREEVIEW_WIDTH = 250 +FORMULA_FONTSIZE = 10 +SEARCHENGINE = YES + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +DOT_FONTNAME = FreeSans +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/libs/audiographer/doc/mainpage.dox b/libs/audiographer/doc/mainpage.dox new file mode 100644 index 0000000000..8f6532a14b --- /dev/null +++ b/libs/audiographer/doc/mainpage.dox @@ -0,0 +1,26 @@ +/** @mainpage + +@section Overview + +AudioGrapher is best described as a signal flow management library. +It includes facilities to build graphs out of signal processing elements. +Once a graph is set up, all signal flow within the graph happens automatically. + +The data flow model in Audiographer is dynamic instead of synchronous - +the type and amount of data that goes in to a graph may differ from what comes out. +AudioGrapher is aimed mostly for usage by developers, +as it includes lots of facilities that ease the development process. + +The main aim of AudioGrapher is to ease development and debugging of signal flow graphs. +It makes heavy use of modern C++ techniques like templates, +and uses the boost libraries a lot. + +The essential classes in AudioGrapher are Sink, Source and ProcessContext. +These three define the signal flow in a graph. +In addition, the core of AudioGrapher includes lots of utility classes. + +AudioGrapher includes a bunch of ready Sink, Source and Vertex implementations. +Some are utilities used when developing more vertices, +while others are general utilities (file i/o, sample rate conversion etc.). + +*/ diff --git a/libs/audiographer/src/gdither/gdither.cc b/libs/audiographer/private/gdither/gdither.cc similarity index 100% rename from libs/audiographer/src/gdither/gdither.cc rename to libs/audiographer/private/gdither/gdither.cc diff --git a/libs/audiographer/src/gdither/gdither.h b/libs/audiographer/private/gdither/gdither.h similarity index 100% rename from libs/audiographer/src/gdither/gdither.h rename to libs/audiographer/private/gdither/gdither.h diff --git a/libs/audiographer/src/gdither/gdither_types.h b/libs/audiographer/private/gdither/gdither_types.h similarity index 100% rename from libs/audiographer/src/gdither/gdither_types.h rename to libs/audiographer/private/gdither/gdither_types.h diff --git a/libs/audiographer/src/gdither/gdither_types_internal.h b/libs/audiographer/private/gdither/gdither_types_internal.h similarity index 100% rename from libs/audiographer/src/gdither/gdither_types_internal.h rename to libs/audiographer/private/gdither/gdither_types_internal.h diff --git a/libs/audiographer/src/gdither/noise.h b/libs/audiographer/private/gdither/noise.h similarity index 100% rename from libs/audiographer/src/gdither/noise.h rename to libs/audiographer/private/gdither/noise.h diff --git a/libs/audiographer/private/sndfile.hh b/libs/audiographer/private/sndfile.hh new file mode 100644 index 0000000000..d8f459f196 --- /dev/null +++ b/libs/audiographer/private/sndfile.hh @@ -0,0 +1,399 @@ +/* +** Copyright (C) 2005-2007 Erik de Castro Lopo +** +** 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 + +#include +#include // 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 */ + diff --git a/libs/audiographer/src/debug_utils.cc b/libs/audiographer/src/debug_utils.cc new file mode 100644 index 0000000000..352c549f06 --- /dev/null +++ b/libs/audiographer/src/debug_utils.cc @@ -0,0 +1,26 @@ +#include "audiographer/debug_utils.h" + +#include "audiographer/process_context.h" + +#include + +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 diff --git a/libs/audiographer/src/sample_format_converter.cc b/libs/audiographer/src/general/sample_format_converter.cc similarity index 68% rename from libs/audiographer/src/sample_format_converter.cc rename to libs/audiographer/src/general/sample_format_converter.cc index 5b2d3d6e8c..5f63fe9f2f 100644 --- a/libs/audiographer/src/sample_format_converter.cc +++ b/libs/audiographer/src/general/sample_format_converter.cc @@ -1,17 +1,16 @@ -#include "audiographer/sample_format_converter.h" +#include "audiographer/general/sample_format_converter.h" -#include "gdither/gdither.h" #include "audiographer/exception.h" +#include "audiographer/type_utils.h" +#include "private/gdither/gdither.h" #include -#include - namespace AudioGrapher { template -SampleFormatConverter::SampleFormatConverter (uint32_t channels) : +SampleFormatConverter::SampleFormatConverter (ChannelCount channels) : channels (channels), dither (0), data_out_size (0), @@ -24,7 +23,9 @@ template <> void SampleFormatConverter::init (nframes_t max_frames, int type, int data_width) { - if (data_width != 32) { throw Exception (*this, "Unsupported data width"); } + if (throw_level (ThrowObject) && data_width != 32) { + throw Exception (*this, "Unsupported data width"); + } init_common (max_frames); dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width); } @@ -33,7 +34,9 @@ template <> void SampleFormatConverter::init (nframes_t max_frames, int type, int data_width) { - if(data_width < 24) { throw Exception (*this, "Use SampleFormatConverter for data widths < 24"); } + if(throw_level (ThrowObject) && data_width < 24) { + throw Exception (*this, "Trying to use SampleFormatConverter for data widths < 24"); + } init_common (max_frames); @@ -41,7 +44,7 @@ SampleFormatConverter::init (nframes_t max_frames, int type, int data_w dither = gdither_new ((GDitherType) type, channels, GDither32bit, data_width); } else if (data_width == 32) { dither = gdither_new (GDitherNone, channels, GDitherFloat, data_width); - } else { + } else if (throw_level (ThrowObject)) { throw Exception (*this, "Unsupported data width"); } } @@ -50,7 +53,9 @@ template <> void SampleFormatConverter::init (nframes_t max_frames, int type, int data_width) { - if (data_width != 16) { throw Exception (*this, "Unsupported data width"); } + if (throw_level (ThrowObject) && data_width != 16) { + throw Exception (*this, "Unsupported data width"); + } init_common (max_frames); dither = gdither_new ((GDitherType) type, channels, GDither16bit, data_width); } @@ -59,7 +64,9 @@ template <> void SampleFormatConverter::init (nframes_t max_frames, int type, int data_width) { - if (data_width != 8) { throw Exception (*this, "Unsupported data width"); } + if (throw_level (ThrowObject) && data_width != 8) { + throw Exception (*this, "Unsupported data width"); + } init_common (max_frames); dither = gdither_new ((GDitherType) type, channels, GDither8bit, data_width); } @@ -106,14 +113,13 @@ void SampleFormatConverter::process (ProcessContext const & c_in) { float const * const data = c_in.data(); - nframes_t const frames = c_in.frames(); - check_frame_count (frames); + check_frame_and_channel_count (c_in.frames (), c_in.channels ()); /* Do conversion */ - for (uint32_t chn = 0; chn < channels; ++chn) { - gdither_runf (dither, chn, frames / channels, data, data_out); + for (uint32_t chn = 0; chn < c_in.channels(); ++chn) { + gdither_runf (dither, chn, c_in.frames_per_channel (), data, data_out); } /* Write forward */ @@ -157,9 +163,8 @@ void SampleFormatConverter::process (ProcessContext const & c_in) { // Make copy of data and pass it to non-const version - nframes_t frames = c_in.frames(); - check_frame_count (frames); - memcpy (data_out, c_in.data(), frames * sizeof(float)); + check_frame_and_channel_count (c_in.frames(), c_in.channels()); + TypeUtils::copy (c_in.data(), data_out, c_in.frames()); ProcessContext c (c_in, data_out); process (c); @@ -167,17 +172,17 @@ SampleFormatConverter::process (ProcessContext const & c_in) template void -SampleFormatConverter::check_frame_count(nframes_t frames) +SampleFormatConverter::check_frame_and_channel_count(nframes_t frames, ChannelCount channels_) { - if (frames % channels != 0) { - throw Exception (*this, boost::str (boost::format ( - "Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels") - % frames % channels)); + if (throw_level (ThrowStrict) && channels_ != channels) { + throw Exception (*this, boost::str (boost::format + ("Wrong channel count given to process(), %1% instead of %2%") + % channels_ % channels)); } - if (frames > data_out_size) { - throw Exception (*this, boost::str (boost::format ( - "Too many frames given to process(), %1% instad of %2%") + if (throw_level (ThrowProcess) && frames > data_out_size) { + throw Exception (*this, boost::str (boost::format + ("Too many frames given to process(), %1% instad of %2%") % frames % data_out_size)); } } diff --git a/libs/audiographer/src/sr_converter.cc b/libs/audiographer/src/general/sr_converter.cc similarity index 93% rename from libs/audiographer/src/sr_converter.cc rename to libs/audiographer/src/general/sr_converter.cc index c62fd719e8..1fe51742a0 100644 --- a/libs/audiographer/src/sr_converter.cc +++ b/libs/audiographer/src/general/sr_converter.cc @@ -1,4 +1,5 @@ -#include "audiographer/sr_converter.h" +#include "audiographer/general/sr_converter.h" + #include "audiographer/exception.h" #include "audiographer/type_utils.h" @@ -21,6 +22,7 @@ SampleRateConverter::SampleRateConverter (uint32_t channels) , data_out_size (0) , src_state (0) { + add_supported_flag (ProcessContext<>::EndOfInput); } void @@ -78,6 +80,8 @@ SampleRateConverter::allocate_buffers (nframes_t max_frames) void SampleRateConverter::process (ProcessContext const & c) { + check_flags (*this, c); + if (!active) { output (c); return; @@ -86,17 +90,11 @@ SampleRateConverter::process (ProcessContext const & c) nframes_t frames = c.frames(); float * in = const_cast (c.data()); // TODO check if this is safe! - if (throw_level (ThrowStrict) && frames > max_frames_in) { + if (throw_level (ThrowProcess) && frames > max_frames_in) { throw Exception (*this, str (format ( "process() called with too many frames, %1% instead of %2%") % frames % max_frames_in)); } - - if (throw_level (ThrowStrict) && frames % channels != 0) { - throw Exception (*this, boost::str (boost::format ( - "Number of frames given to process() was not a multiple of channels: %1% frames with %2% channels") - % frames % channels)); - } int err; bool first_time = true; diff --git a/libs/audiographer/src/sndfile_base.cc b/libs/audiographer/src/sndfile_base.cc deleted file mode 100644 index 8d12f9341b..0000000000 --- a/libs/audiographer/src/sndfile_base.cc +++ /dev/null @@ -1,56 +0,0 @@ -#include "audiographer/sndfile_base.h" -#include "audiographer/exception.h" - -#include - -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 diff --git a/libs/audiographer/src/sndfile_reader.cc b/libs/audiographer/src/sndfile_reader.cc deleted file mode 100644 index 8297844721..0000000000 --- a/libs/audiographer/src/sndfile_reader.cc +++ /dev/null @@ -1,68 +0,0 @@ -#include "audiographer/sndfile_reader.h" - -#include - -#include "audiographer/exception.h" - -namespace AudioGrapher -{ - -template -SndfileReader::SndfileReader (ChannelCount channels, nframes_t samplerate, int format, std::string path) - : SndfileBase (channels, samplerate, format, path) -{ - init (); -} - -template -nframes_t -SndfileReader::seek (nframes_t frames, SeekType whence) -{ - return sf_seek (sndfile, frames, whence); -} - -template -nframes_t -SndfileReader::read (ProcessContext & 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::EndOfInput); - } - output (context); - return frames_read; -} - -template<> -void -SndfileReader::init() -{ - read_func = &sf_read_short; -} - -template<> -void -SndfileReader::init() -{ - read_func = &sf_read_int; -} - -template<> -void -SndfileReader::init() -{ - read_func = &sf_read_float; -} - -template class SndfileReader; -template class SndfileReader; -template class SndfileReader; - -} // namespace \ No newline at end of file diff --git a/libs/audiographer/src/sndfile_writer.cc b/libs/audiographer/src/sndfile_writer.cc deleted file mode 100644 index f8c9ea63b5..0000000000 --- a/libs/audiographer/src/sndfile_writer.cc +++ /dev/null @@ -1,74 +0,0 @@ -#include "audiographer/sndfile_writer.h" -#include "audiographer/exception.h" - -#include - -#include - -namespace AudioGrapher -{ - -using std::string; -using boost::str; -using boost::format; - -template -SndfileWriter::SndfileWriter (ChannelCount channels, nframes_t samplerate, int format, string const & path) : - SndfileBase (channels, samplerate, format, path) -{ - // init write function - init (); -} - -template <> -void -SndfileWriter::init () -{ - write_func = &sf_write_float; -} - -template <> -void -SndfileWriter::init () -{ - write_func = &sf_write_int; -} - -template <> -void -SndfileWriter::init () -{ - write_func = &sf_write_short; -} - -template -void -SndfileWriter::process (ProcessContext 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::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; -template class SndfileWriter; -template class SndfileWriter; - -} // namespace diff --git a/libs/audiographer/src/utils.cc b/libs/audiographer/src/utils.cc deleted file mode 100644 index 018fad3113..0000000000 --- a/libs/audiographer/src/utils.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include "audiographer/utils.h" - -using namespace AudioGrapher; - -char const * Utils::zeros = 0; -unsigned long Utils::num_zeros = 0; - -void -Utils::free_resources() -{ - num_zeros = 0; - delete [] zeros; - zeros = 0; -} \ No newline at end of file diff --git a/libs/audiographer/tests/chunker_test.cc b/libs/audiographer/tests/general/chunker_test.cc similarity index 72% rename from libs/audiographer/tests/chunker_test.cc rename to libs/audiographer/tests/general/chunker_test.cc index ab0c04d7f9..c667360601 100644 --- a/libs/audiographer/tests/chunker_test.cc +++ b/libs/audiographer/tests/general/chunker_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/chunker.h" +#include "tests/utils.h" + +#include "audiographer/general/chunker.h" #include @@ -7,9 +8,12 @@ using namespace AudioGrapher; class ChunkerTest : public CppUnit::TestFixture { + // TODO: Test EndOfInput handling + CPPUNIT_TEST_SUITE (ChunkerTest); CPPUNIT_TEST (testSynchronousProcess); CPPUNIT_TEST (testAsynchronousProcess); + CPPUNIT_TEST (testChoppingProcess); CPPUNIT_TEST_SUITE_END (); public: @@ -99,6 +103,38 @@ class ChunkerTest : public CppUnit::TestFixture CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[frames / 2], frames)); CPPUNIT_ASSERT (TestUtils::array_equals (random_data, &sink->get_array()[ 3 * frames / 2], frames / 2)); } + + void testChoppingProcess() + { + sink.reset (new AppendingVectorSink()); + + assert (frames % 2 == 0); + chunker.reset (new Chunker(frames / 4)); + + chunker->add_output (sink); + nframes_t frames_output = 0; + + ProcessContext const half_context (random_data, frames / 2, 1); + ProcessContext 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; diff --git a/libs/audiographer/tests/deinterleaver_test.cc b/libs/audiographer/tests/general/deinterleaver_test.cc similarity index 83% rename from libs/audiographer/tests/deinterleaver_test.cc rename to libs/audiographer/tests/general/deinterleaver_test.cc index b0adbc0444..3d644a3cf1 100644 --- a/libs/audiographer/tests/deinterleaver_test.cc +++ b/libs/audiographer/tests/general/deinterleaver_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/deinterleaver.h" +#include "tests/utils.h" + +#include "audiographer/general/deinterleaver.h" using namespace AudioGrapher; @@ -48,19 +49,16 @@ class DeInterleaverTest : public CppUnit::TestFixture { deinterleaver->init (channels, frames_per_channel); - ProcessContext c (random_data, 0, channels); + ProcessContext c (random_data, 2 * total_frames, channels); // Too many, frames % channels == 0 - c.frames() = total_frames + channels; - CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception); + CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames + channels)), Exception); // Too many, frames % channels != 0 - c.frames() = total_frames + 1; - CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception); + CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames + 1)), Exception); // Too few, frames % channels != 0 - c.frames() = total_frames - 1; - CPPUNIT_ASSERT_THROW (deinterleaver->process (c), Exception); + CPPUNIT_ASSERT_THROW (deinterleaver->process (c.beginning (total_frames - 1)), Exception); } void assert_outputs (nframes_t expected_frames) @@ -92,8 +90,7 @@ class DeInterleaverTest : public CppUnit::TestFixture // Now with less frames nframes_t const less_frames = frames_per_channel / 4; - c.frames() = less_frames * channels; - deinterleaver->process (c); + deinterleaver->process (c.beginning (less_frames * channels)); assert_outputs (less_frames); } @@ -106,11 +103,10 @@ class DeInterleaverTest : public CppUnit::TestFixture deinterleaver->output (2)->add_output (sink_c); // Input zero frames - ProcessContext c (random_data, 0, channels); - deinterleaver->process (c); + ProcessContext c (random_data, total_frames, channels); + deinterleaver->process (c.beginning (0)); // ...and now test regular input - c.frames() = total_frames; deinterleaver->process (c); assert_outputs (frames_per_channel); } diff --git a/libs/audiographer/tests/interleaver_deinterleaver_test.cc b/libs/audiographer/tests/general/interleaver_deinterleaver_test.cc similarity index 90% rename from libs/audiographer/tests/interleaver_deinterleaver_test.cc rename to libs/audiographer/tests/general/interleaver_deinterleaver_test.cc index 5655253e62..908abafdf2 100644 --- a/libs/audiographer/tests/interleaver_deinterleaver_test.cc +++ b/libs/audiographer/tests/general/interleaver_deinterleaver_test.cc @@ -1,6 +1,7 @@ -#include "utils.h" -#include "audiographer/interleaver.h" -#include "audiographer/deinterleaver.h" +#include "tests/utils.h" + +#include "audiographer/general/interleaver.h" +#include "audiographer/general/deinterleaver.h" using namespace AudioGrapher; @@ -55,8 +56,7 @@ class InterleaverDeInterleaverTest : public CppUnit::TestFixture // And a second round... nframes_t less_frames = (frames_per_channel / 10) * channels; - c.frames() = less_frames; - deinterleaver->process (c); + deinterleaver->process (c.beginning (less_frames)); CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames)); } @@ -86,12 +86,9 @@ class InterleaverDeInterleaverTest : public CppUnit::TestFixture // And a second round... nframes_t less_frames = frames_per_channel / 5; - c_a.frames() = less_frames; - c_b.frames() = less_frames; - c_c.frames() = less_frames; - interleaver->input (0)->process (c_a); - interleaver->input (1)->process (c_b); - interleaver->input (2)->process (c_c); + interleaver->input (0)->process (c_a.beginning (less_frames)); + interleaver->input (1)->process (c_b.beginning (less_frames)); + interleaver->input (2)->process (c_c.beginning (less_frames)); CPPUNIT_ASSERT (TestUtils::array_equals (random_data_a, sink_a->get_array(), less_frames)); CPPUNIT_ASSERT (TestUtils::array_equals (random_data_b, sink_b->get_array(), less_frames)); diff --git a/libs/audiographer/tests/interleaver_test.cc b/libs/audiographer/tests/general/interleaver_test.cc similarity index 75% rename from libs/audiographer/tests/interleaver_test.cc rename to libs/audiographer/tests/general/interleaver_test.cc index abe385699d..1512d054fc 100644 --- a/libs/audiographer/tests/interleaver_test.cc +++ b/libs/audiographer/tests/general/interleaver_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/interleaver.h" +#include "tests/utils.h" + +#include "audiographer/general/interleaver.h" using namespace AudioGrapher; @@ -50,16 +51,13 @@ class InterleaverTest : public CppUnit::TestFixture ProcessContext c (random_data, frames + 1, 1); CPPUNIT_ASSERT_THROW (interleaver->input (0)->process (c), Exception); - c.frames() = frames; - interleaver->input (0)->process (c); - interleaver->input (1)->process (c); - c.frames() = frames -1; - CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception); + interleaver->input (0)->process (c.beginning (frames)); + interleaver->input (1)->process (c.beginning (frames)); + CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c.beginning (frames - 1)), Exception); - interleaver->input (0)->process (c); - interleaver->input (1)->process (c); - c.frames() = frames; - CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c), Exception); + interleaver->input (0)->process (c.beginning (frames - 1)); + interleaver->input (1)->process (c.beginning (frames - 1)); + CPPUNIT_ASSERT_THROW (interleaver->input (2)->process (c.beginning (frames)), Exception); } void testOutputSize() @@ -76,10 +74,9 @@ class InterleaverTest : public CppUnit::TestFixture CPPUNIT_ASSERT_EQUAL (expected_frames, generated_frames); nframes_t less_frames = frames / 2; - c.frames() = less_frames; - interleaver->input (0)->process (c); - interleaver->input (1)->process (c); - interleaver->input (2)->process (c); + interleaver->input (0)->process (c.beginning (less_frames)); + interleaver->input (1)->process (c.beginning (less_frames)); + interleaver->input (2)->process (c.beginning (less_frames)); expected_frames = less_frames * channels; generated_frames = sink->get_data().size(); @@ -91,15 +88,14 @@ class InterleaverTest : public CppUnit::TestFixture interleaver->add_output (sink); // input zero frames to all inputs - ProcessContext c (random_data, 0, 1); - interleaver->input (0)->process (c); - interleaver->input (1)->process (c); - interleaver->input (2)->process (c); + ProcessContext c (random_data, frames, 1); + interleaver->input (0)->process (c.beginning (0)); + interleaver->input (1)->process (c.beginning (0)); + interleaver->input (2)->process (c.beginning (0)); // NOTE zero input is allowed to be a NOP // ...now test regular input - c.frames() = frames; interleaver->input (0)->process (c); interleaver->input (1)->process (c); interleaver->input (2)->process (c); diff --git a/libs/audiographer/tests/normalizer_test.cc b/libs/audiographer/tests/general/normalizer_test.cc similarity index 91% rename from libs/audiographer/tests/normalizer_test.cc rename to libs/audiographer/tests/general/normalizer_test.cc index 711e0018ca..b08fd73a0e 100644 --- a/libs/audiographer/tests/normalizer_test.cc +++ b/libs/audiographer/tests/general/normalizer_test.cc @@ -1,7 +1,7 @@ -#include "utils.h" +#include "tests/utils.h" -#include "audiographer/normalizer.h" -#include "audiographer/peak_reader.h" +#include "audiographer/general/normalizer.h" +#include "audiographer/general/peak_reader.h" using namespace AudioGrapher; diff --git a/libs/audiographer/tests/peak_reader_test.cc b/libs/audiographer/tests/general/peak_reader_test.cc similarity index 93% rename from libs/audiographer/tests/peak_reader_test.cc rename to libs/audiographer/tests/general/peak_reader_test.cc index dce03b6caf..53b7a15174 100644 --- a/libs/audiographer/tests/peak_reader_test.cc +++ b/libs/audiographer/tests/general/peak_reader_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/peak_reader.h" +#include "tests/utils.h" + +#include "audiographer/general/peak_reader.h" using namespace AudioGrapher; diff --git a/libs/audiographer/tests/sample_format_converter_test.cc b/libs/audiographer/tests/general/sample_format_converter_test.cc similarity index 96% rename from libs/audiographer/tests/sample_format_converter_test.cc rename to libs/audiographer/tests/general/sample_format_converter_test.cc index f723f7af53..1456528ebf 100644 --- a/libs/audiographer/tests/sample_format_converter_test.cc +++ b/libs/audiographer/tests/general/sample_format_converter_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/sample_format_converter.h" +#include "tests/utils.h" + +#include "audiographer/general/sample_format_converter.h" using namespace AudioGrapher; @@ -192,10 +193,10 @@ class SampleFormatConverterTest : public CppUnit::TestFixture ProcessContext pc(random_data, 4, 1); CPPUNIT_ASSERT_THROW (converter->process (pc), Exception); - pc.frames() = frames - (frames % 3); - converter->process (pc); + nframes_t new_frame_count = frames - (frames % 3); + converter->process (ProcessContext (pc.data(), new_frame_count, 3)); frames_output = sink->get_data().size(); - CPPUNIT_ASSERT_EQUAL (pc.frames(), frames_output); + CPPUNIT_ASSERT_EQUAL (new_frame_count, frames_output); CPPUNIT_ASSERT (TestUtils::array_filled(sink->get_array(), pc.frames())); } diff --git a/libs/audiographer/tests/silence_trimmer_test.cc b/libs/audiographer/tests/general/silence_trimmer_test.cc similarity index 85% rename from libs/audiographer/tests/silence_trimmer_test.cc rename to libs/audiographer/tests/general/silence_trimmer_test.cc index 16234bec37..add24d026a 100644 --- a/libs/audiographer/tests/silence_trimmer_test.cc +++ b/libs/audiographer/tests/general/silence_trimmer_test.cc @@ -1,6 +1,6 @@ -#include "utils.h" +#include "tests/utils.h" -#include "audiographer/silence_trimmer.h" +#include "audiographer/general/silence_trimmer.h" using namespace AudioGrapher; @@ -29,7 +29,7 @@ class SilenceTrimmerTest : public CppUnit::TestFixture half_random_data = TestUtils::init_random_data(frames); memset(half_random_data, 0, (frames / 2) * sizeof(float)); - trimmer.reset (new SilenceTrimmer()); + trimmer.reset (new SilenceTrimmer (frames / 2)); sink.reset (new AppendingVectorSink()); trimmer->set_trim_beginning (true); @@ -41,14 +41,11 @@ class SilenceTrimmerTest : public CppUnit::TestFixture delete [] random_data; delete [] zero_data; delete [] half_random_data; - - AudioGrapher::Utils::free_resources(); } void testFullBuffers() { trimmer->add_output (sink); - AudioGrapher::Utils::init_zeros(frames / 2); { ProcessContext c (zero_data, frames, 1); @@ -93,7 +90,9 @@ class SilenceTrimmerTest : public CppUnit::TestFixture void testPartialBuffers() { trimmer->add_output (sink); - AudioGrapher::Utils::init_zeros(frames / 4); + trimmer->reset (frames / 4); + trimmer->set_trim_beginning (true); + trimmer->set_trim_end (true); { ProcessContext c (half_random_data, frames, 1); @@ -121,31 +120,14 @@ class SilenceTrimmerTest : public CppUnit::TestFixture void testExceptions() { - // TODO more tests here - - trimmer->add_output (sink); - { - ProcessContext c (random_data, frames, 1); - trimmer->process (c); - } - - { - ProcessContext c (zero_data, frames, 1); - trimmer->process (c); - } - - { - // Zeros not inited, so this should throw - ProcessContext c (random_data, frames, 1); - CPPUNIT_ASSERT_THROW (trimmer->process (c), Exception); + CPPUNIT_ASSERT_THROW (trimmer->reset (0), Exception); } } void testAddSilenceBeginning() { trimmer->add_output (sink); - AudioGrapher::Utils::init_zeros(frames / 2); nframes_t silence = frames / 2; trimmer->add_silence_to_beginning (silence); @@ -162,7 +144,6 @@ class SilenceTrimmerTest : public CppUnit::TestFixture void testAddSilenceEnd() { trimmer->add_output (sink); - AudioGrapher::Utils::init_zeros(frames / 2); nframes_t silence = frames / 3; trimmer->add_silence_to_end (silence); @@ -178,6 +159,9 @@ class SilenceTrimmerTest : public CppUnit::TestFixture trimmer->process (c); } + nframes_t frames_processed = sink->get_data().size(); + nframes_t total_frames = 2 * frames + silence; + CPPUNIT_ASSERT_EQUAL (total_frames, frames_processed); CPPUNIT_ASSERT (TestUtils::array_equals (sink->get_array(), random_data, frames)); CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames], random_data, frames)); CPPUNIT_ASSERT (TestUtils::array_equals (&sink->get_array()[frames * 2], zero_data, silence)); diff --git a/libs/audiographer/tests/sr_converter_test.cc b/libs/audiographer/tests/general/sr_converter_test.cc similarity index 98% rename from libs/audiographer/tests/sr_converter_test.cc rename to libs/audiographer/tests/general/sr_converter_test.cc index e7b49a1b71..68e21d3558 100644 --- a/libs/audiographer/tests/sr_converter_test.cc +++ b/libs/audiographer/tests/general/sr_converter_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/sr_converter.h" +#include "tests/utils.h" + +#include "audiographer/general/sr_converter.h" using namespace AudioGrapher; diff --git a/libs/audiographer/tests/threader_test.cc b/libs/audiographer/tests/general/threader_test.cc similarity index 98% rename from libs/audiographer/tests/threader_test.cc rename to libs/audiographer/tests/general/threader_test.cc index ac5588d79c..c599d87953 100644 --- a/libs/audiographer/tests/threader_test.cc +++ b/libs/audiographer/tests/general/threader_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/threader.h" +#include "tests/utils.h" + +#include "audiographer/general/threader.h" using namespace AudioGrapher; diff --git a/libs/audiographer/tests/sndfile/tmp_file_test.cc b/libs/audiographer/tests/sndfile/tmp_file_test.cc new file mode 100644 index 0000000000..d2d1b3581e --- /dev/null +++ b/libs/audiographer/tests/sndfile/tmp_file_test.cc @@ -0,0 +1,47 @@ +#include "tests/utils.h" +#include "audiographer/sndfile/tmp_file.h" + +using namespace AudioGrapher; + +class TmpFileTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (TmpFileTest); + CPPUNIT_TEST (testProcess); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp() + { + frames = 128; + random_data = TestUtils::init_random_data(frames); + } + + void tearDown() + { + delete [] random_data; + } + + void testProcess() + { + uint channels = 2; + file.reset (new TmpFile(SF_FORMAT_WAV | SF_FORMAT_FLOAT, channels, 44100)); + AllocatingProcessContext c (random_data, frames, channels); + c.set_flag (ProcessContext::EndOfInput); + file->process (c); + + TypeUtils::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 > file; + + float * random_data; + nframes_t frames; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (TmpFileTest); + diff --git a/libs/audiographer/tests/sndfile_writer_test.cc b/libs/audiographer/tests/sndfile_writer_test.cc deleted file mode 100644 index 359d456f15..0000000000 --- a/libs/audiographer/tests/sndfile_writer_test.cc +++ /dev/null @@ -1,42 +0,0 @@ -#include "utils.h" -#include "audiographer/sndfile_writer.h" - -using namespace AudioGrapher; - -class SndfileWriterTest : public CppUnit::TestFixture -{ - CPPUNIT_TEST_SUITE (SndfileWriterTest); - CPPUNIT_TEST (testProcess); - CPPUNIT_TEST_SUITE_END (); - - public: - void setUp() - { - frames = 128; - random_data = TestUtils::init_random_data(frames); - } - - void tearDown() - { - delete [] random_data; - } - - void testProcess() - { - uint channels = 2; - std::string filename ("test.wav"); - writer.reset (new SndfileWriter(channels, 44100, SF_FORMAT_WAV | SF_FORMAT_FLOAT, filename)); - ProcessContext c (random_data, frames, channels); - c.set_flag (ProcessContext::EndOfInput); - writer->process (c); - } - - private: - boost::shared_ptr > writer; - - float * random_data; - nframes_t frames; -}; - -CPPUNIT_TEST_SUITE_REGISTRATION (SndfileWriterTest); - diff --git a/libs/audiographer/tests/type_utils_test.cc b/libs/audiographer/tests/type_utils_test.cc new file mode 100644 index 0000000000..af9e923d1b --- /dev/null +++ b/libs/audiographer/tests/type_utils_test.cc @@ -0,0 +1,112 @@ +#include "tests/utils.h" + +#include "audiographer/type_utils.h" + +using namespace AudioGrapher; + +class TypeUtilsTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (TypeUtilsTest); + CPPUNIT_TEST (testZeroFillPod); + CPPUNIT_TEST (testZeroFillNonPod); + CPPUNIT_TEST (testCopy); + CPPUNIT_TEST (testMoveBackward); + CPPUNIT_TEST (testMoveForward); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp() + { + + } + + void tearDown() + { + + } + + void testZeroFillPod() + { + unsigned frames = 10; + float buf[frames]; + TypeUtils::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::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::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::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::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::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); + diff --git a/libs/audiographer/tests/identity_vertex_test.cc b/libs/audiographer/tests/utils/identity_vertex_test.cc similarity index 97% rename from libs/audiographer/tests/identity_vertex_test.cc rename to libs/audiographer/tests/utils/identity_vertex_test.cc index 5a3ae7c9f2..165af66a9d 100644 --- a/libs/audiographer/tests/identity_vertex_test.cc +++ b/libs/audiographer/tests/utils/identity_vertex_test.cc @@ -1,5 +1,6 @@ -#include "utils.h" -#include "audiographer/identity_vertex.h" +#include "tests/utils.h" + +#include "audiographer/utils/identity_vertex.h" using namespace AudioGrapher; diff --git a/libs/audiographer/wscript b/libs/audiographer/wscript index bb60a4c5e1..b9391df274 100644 --- a/libs/audiographer/wscript +++ b/libs/audiographer/wscript @@ -46,27 +46,24 @@ def build(bld): # Headers #bld.install_files('${INCLUDEDIR}/audiographer', 'audiographer/*.h') + #bld.install_files('${INCLUDEDIR}/audiographer/general', 'audiographer/general/*.h') + #bld.install_files('${INCLUDEDIR}/audiographer/sndfile', 'audiographer/sndfile/*.h') + #bld.install_files('${INCLUDEDIR}/audiographer/utils', 'audiographer/utils/*.h') + #bld.env['BUILD_TESTS'] = True bld.env['HAVE_ALL_GTHREAD'] = bld.env['HAVE_GLIB'] and bld.env['HAVE_GLIBMM'] and bld.env['HAVE_GTHREAD'] audiographer = bld.new_task_gen('cxx', 'shlib') audiographer.source = ''' - src/gdither/gdither.cc - src/sample_format_converter.cc + private/gdither/gdither.cc + src/general/sample_format_converter.cc src/routines.cc - src/utils.cc + src/debug_utils.cc ''' - if bld.env['HAVE_SNDFILE']: - audiographer.source += ''' - src/sndfile_base.cc - src/sndfile_writer.cc - src/sndfile_reader.cc - ''' - if bld.env['HAVE_SAMPLERATE']: audiographer.source += ''' - src/sr_converter.cc + src/general/sr_converter.cc ''' audiographer.name = 'libaudiographer' @@ -76,36 +73,38 @@ def build(bld): audiographer.uselib = 'GLIB GLIBMM GTHREAD SAMPLERATE SNDFILE' audiographer.vnum = AUDIOGRAPHER_LIB_VERSION audiographer.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3') + if bld.env['BUILD_TESTS'] and bld.env['HAVE_CPPUNIT']: # Unit tests obj = bld.new_task_gen('cxx', 'program') obj.source = ''' - tests/identity_vertex_test.cc - tests/interleaver_test.cc - tests/deinterleaver_test.cc - tests/interleaver_deinterleaver_test.cc - tests/chunker_test.cc - tests/sample_format_converter_test.cc tests/test_runner.cc - tests/peak_reader_test.cc - tests/normalizer_test.cc - tests/silence_trimmer_test.cc + tests/type_utils_test.cc + tests/utils/identity_vertex_test.cc + tests/general/interleaver_test.cc + tests/general/deinterleaver_test.cc + tests/general/interleaver_deinterleaver_test.cc + tests/general/chunker_test.cc + tests/general/sample_format_converter_test.cc + tests/general/peak_reader_test.cc + tests/general/normalizer_test.cc + tests/general/silence_trimmer_test.cc ''' if bld.env['HAVE_ALL_GTHREAD']: obj.source += ''' - tests/threader_test.cc + tests/general/threader_test.cc ''' if bld.env['HAVE_SNDFILE']: obj.source += ''' - tests/sndfile_writer_test.cc + tests/sndfile/tmp_file_test.cc ''' if bld.env['HAVE_SAMPLERATE']: obj.source += ''' - tests/sr_converter_test.cc + tests/general/sr_converter_test.cc ''' obj.uselib_local = 'libaudiographer'