diff options
Diffstat (limited to 'libs/audiographer/audiographer')
24 files changed, 1505 insertions, 0 deletions
diff --git a/libs/audiographer/audiographer/chunker.h b/libs/audiographer/audiographer/chunker.h new file mode 100644 index 0000000000..afce921cc2 --- /dev/null +++ b/libs/audiographer/audiographer/chunker.h @@ -0,0 +1,54 @@ +#ifndef AUDIOGRAPHER_CHUNKER_H +#define AUDIOGRAPHER_CHUNKER_H + +#include "listed_source.h" +#include "sink.h" +#include <cstring> + +namespace AudioGrapher +{ + +template<typename T> +class Chunker : public ListedSource<T>, public Sink<T> +{ + public: + Chunker (nframes_t chunk_size) + : chunk_size (chunk_size) + , position (0) + { + buffer = new T[chunk_size]; + } + + ~Chunker() + { + delete [] buffer; + } + + void process (ProcessContext<T> const & context) + { + if (position + context.frames() < chunk_size) { + memcpy (&buffer[position], (float const *)context.data(), context.frames() * sizeof(T)); + position += context.frames(); + } else { + nframes_t const frames_to_copy = chunk_size - position; + memcpy (&buffer[position], context.data(), frames_to_copy * sizeof(T)); + ProcessContext<T> c_out (context, buffer, chunk_size); + ListedSource<T>::output (c_out); + + memcpy (buffer, &context.data()[frames_to_copy], (context.frames() - frames_to_copy) * sizeof(T)); + position = context.frames() - frames_to_copy; + } + } + using Sink<T>::process; + + private: + nframes_t chunk_size; + nframes_t position; + T * buffer; + +}; + +} // namespace + +#endif // AUDIOGRAPHER_CHUNKER_H + diff --git a/libs/audiographer/audiographer/deinterleaver-inl.h b/libs/audiographer/audiographer/deinterleaver-inl.h new file mode 100644 index 0000000000..f93fdc53a4 --- /dev/null +++ b/libs/audiographer/audiographer/deinterleaver-inl.h @@ -0,0 +1,78 @@ +template<typename T> +DeInterleaver<T>::DeInterleaver() + : channels (0) + , max_frames (0) + , buffer (0) + {} + +template<typename T> +void +DeInterleaver<T>::init (unsigned int num_channels, nframes_t max_frames_per_channel) +{ + reset(); + channels = num_channels; + max_frames = max_frames_per_channel; + buffer = new T[max_frames]; + + for (unsigned int i = 0; i < channels; ++i) { + outputs.push_back (OutputPtr (new IdentityVertex<T>)); + } +} + +template<typename T> +typename DeInterleaver<T>::SourcePtr +DeInterleaver<T>::output (unsigned int channel) +{ + if (channel >= channels) { + throw Exception (*this, "channel out of range"); + } + + return boost::static_pointer_cast<Source<T> > (outputs[channel]); +} + +template<typename T> +void +DeInterleaver<T>::process (ProcessContext<T> const & c) +{ + nframes_t frames = c.frames(); + T const * data = c.data(); + + if (frames == 0) { return; } + + nframes_t const frames_per_channel = frames / channels; + + if (c.channels() != channels) { + throw Exception (*this, "wrong amount of channels given to process()"); + } + + if (frames % channels != 0) { + throw Exception (*this, "wrong amount of frames given to process()"); + } + + if (frames_per_channel > max_frames) { + throw Exception (*this, "too many frames given to process()"); + } + + unsigned int channel = 0; + for (typename std::vector<OutputPtr>::iterator it = outputs.begin(); it != outputs.end(); ++it, ++channel) { + if (!*it) { continue; } + + for (unsigned int i = 0; i < frames_per_channel; ++i) { + buffer[i] = data[channel + (channels * i)]; + } + + ProcessContext<T> c_out (c, buffer, frames_per_channel, 1); + (*it)->process (c_out); + } +} + +template<typename T> +void +DeInterleaver<T>::reset () +{ + outputs.clear(); + delete [] buffer; + buffer = 0; + channels = 0; + max_frames = 0; +} diff --git a/libs/audiographer/audiographer/deinterleaver.h b/libs/audiographer/audiographer/deinterleaver.h new file mode 100644 index 0000000000..3a4aa53c43 --- /dev/null +++ b/libs/audiographer/audiographer/deinterleaver.h @@ -0,0 +1,46 @@ +#ifndef AUDIOGRAPHER_DEINTERLEAVER_H +#define AUDIOGRAPHER_DEINTERLEAVER_H + +#include "types.h" +#include "source.h" +#include "sink.h" +#include "identity_vertex.h" +#include "exception.h" + +#include <vector> + +namespace AudioGrapher +{ + +template<typename T> +class DeInterleaver : public Sink<T> +{ + private: + typedef boost::shared_ptr<IdentityVertex<T> > OutputPtr; + + public: + DeInterleaver(); + ~DeInterleaver() { reset(); } + + typedef boost::shared_ptr<Source<T> > SourcePtr; + + void init (unsigned int num_channels, nframes_t max_frames_per_channel); + SourcePtr output (unsigned int channel); + void process (ProcessContext<T> const & c); + using Sink<T>::process; + + private: + + void reset (); + + std::vector<OutputPtr> outputs; + unsigned int channels; + nframes_t max_frames; + T * buffer; +}; + +#include "deinterleaver-inl.h" + +} // namespace + +#endif // AUDIOGRAPHER_DEINTERLEAVER_H diff --git a/libs/audiographer/audiographer/exception.h b/libs/audiographer/audiographer/exception.h new file mode 100644 index 0000000000..a179b30f91 --- /dev/null +++ b/libs/audiographer/audiographer/exception.h @@ -0,0 +1,52 @@ +#ifndef AUDIOGRAPHER_EXCEPTION_H +#define AUDIOGRAPHER_EXCEPTION_H + +#include <exception> +#include <string> +#include <cxxabi.h> + +#include <boost/format.hpp> + +namespace AudioGrapher +{ + +class Exception : public std::exception +{ + public: + template<typename T> + Exception (T const & thrower, std::string const & reason) + : reason (boost::str (boost::format ( + "Exception thrown by %1%: %2%") % name (thrower) % reason)) + {} + + virtual ~Exception () throw() { } + + const char* what() const throw() + { + return reason.c_str(); + } + + protected: + template<typename T> + std::string name (T const & obj) + { +#ifdef __GNUC__ + int status; + char * res = abi::__cxa_demangle (typeid(obj).name(), 0, 0, &status); + if (status == 0) { + std::string s(res); + free (res); + return s; + } +#endif + return typeid(obj).name(); + } + + private: + std::string const reason; + +}; + +} // namespace AudioGrapher + +#endif // AUDIOGRAPHER_EXCEPTION_H
\ No newline at end of file diff --git a/libs/audiographer/audiographer/identity_vertex.h b/libs/audiographer/audiographer/identity_vertex.h new file mode 100644 index 0000000000..b53bd96851 --- /dev/null +++ b/libs/audiographer/audiographer/identity_vertex.h @@ -0,0 +1,21 @@ +#ifndef AUDIOGRAPHER_IDENTITY_VERTEX_H +#define AUDIOGRAPHER_IDENTITY_VERTEX_H + +#include "listed_source.h" +#include "sink.h" + +namespace AudioGrapher +{ + +template<typename T> +class IdentityVertex : public ListedSource<T>, Sink<T> +{ + public: + void process (ProcessContext<T> const & c) { ListedSource<T>::output(c); } + void process (ProcessContext<T> & c) { ListedSource<T>::output(c); } +}; + + +} // namespace + +#endif // AUDIOGRAPHER_IDENTITY_VERTEX_H diff --git a/libs/audiographer/audiographer/interleaver-inl.h b/libs/audiographer/audiographer/interleaver-inl.h new file mode 100644 index 0000000000..07e93b2a85 --- /dev/null +++ b/libs/audiographer/audiographer/interleaver-inl.h @@ -0,0 +1,92 @@ +template<typename T> +Interleaver<T>::Interleaver() + : channels (0) + , max_frames (0) + , buffer (0) +{} + +template<typename T> +void +Interleaver<T>::init (unsigned int num_channels, nframes_t max_frames_per_channel) +{ + reset(); + channels = num_channels; + max_frames = max_frames_per_channel; + + buffer = new T[channels * max_frames]; + + for (unsigned int i = 0; i < channels; ++i) { + inputs.push_back (InputPtr (new Input (*this, i))); + } +} + +template<typename T> +typename Source<T>::SinkPtr +Interleaver<T>::input (unsigned int channel) +{ + if (channel >= channels) { + throw Exception (*this, "Channel out of range"); + } + + return boost::static_pointer_cast<Sink<T> > (inputs[channel]); +} + +template<typename T> +void +Interleaver<T>::reset_channels () +{ + for (unsigned int i = 0; i < channels; ++i) { + inputs[i]->reset(); + } + +} + +template<typename T> +void +Interleaver<T>::reset () +{ + inputs.clear(); + delete [] buffer; + buffer = 0; + channels = 0; + max_frames = 0; +} + +template<typename T> +void +Interleaver<T>::write_channel (ProcessContext<T> const & c, unsigned int channel) +{ + if (c.frames() > max_frames) { + reset_channels(); + throw Exception (*this, "Too many frames given to an input"); + } + + for (unsigned int i = 0; i < c.frames(); ++i) { + buffer[channel + (channels * i)] = c.data()[i]; + } + + nframes_t const ready_frames = ready_to_output(); + if (ready_frames) { + ProcessContext<T> c_out (c, buffer, ready_frames, channels); + ListedSource<T>::output (c_out); + reset_channels (); + } +} + +template<typename T> +nframes_t +Interleaver<T>::ready_to_output () +{ + nframes_t ready_frames = inputs[0]->frames(); + if (!ready_frames) { return 0; } + + for (unsigned int i = 1; i < channels; ++i) { + nframes_t const frames = inputs[i]->frames(); + if (!frames) { return 0; } + if (frames != ready_frames) { + init (channels, max_frames); + throw Exception (*this, "Frames count out of sync"); + } + } + return ready_frames * channels; +} diff --git a/libs/audiographer/audiographer/interleaver.h b/libs/audiographer/audiographer/interleaver.h new file mode 100644 index 0000000000..3d51fed5a5 --- /dev/null +++ b/libs/audiographer/audiographer/interleaver.h @@ -0,0 +1,71 @@ +#ifndef AUDIOGRAPHER_INTERLEAVER_H +#define AUDIOGRAPHER_INTERLEAVER_H + +#include "types.h" +#include "listed_source.h" +#include "sink.h" +#include "exception.h" + +#include <vector> +#include <cmath> + +namespace AudioGrapher +{ + +template<typename T> +class Interleaver : public ListedSource<T> +{ + public: + + Interleaver(); + ~Interleaver() { reset(); } + + void init (unsigned int num_channels, nframes_t max_frames_per_channel); + typename Source<T>::SinkPtr input (unsigned int channel); + + private: + + class Input : public Sink<T> + { + public: + Input (Interleaver & parent, unsigned int channel) + : frames_written (0), parent (parent), channel (channel) {} + + void process (ProcessContext<T> const & c) + { + if (c.channels() > 1) { throw Exception (*this, "Data input has more than on channel"); } + if (frames_written) { throw Exception (*this, "Input channels out of sync"); } + frames_written = c.frames(); + parent.write_channel (c, channel); + } + + using Sink<T>::process; + + nframes_t frames() { return frames_written; } + void reset() { frames_written = 0; } + + private: + nframes_t frames_written; + Interleaver & parent; + unsigned int channel; + }; + + void reset (); + void reset_channels (); + void write_channel (ProcessContext<T> const & c, unsigned int channel); + nframes_t ready_to_output(); + void output(); + + typedef boost::shared_ptr<Input> InputPtr; + std::vector<InputPtr> inputs; + + unsigned int channels; + nframes_t max_frames; + T * buffer; +}; + +#include "interleaver-inl.h" + +} // namespace + +#endif // AUDIOGRAPHER_INTERLEAVER_H diff --git a/libs/audiographer/audiographer/listed_source.h b/libs/audiographer/audiographer/listed_source.h new file mode 100644 index 0000000000..bc8f144d84 --- /dev/null +++ b/libs/audiographer/audiographer/listed_source.h @@ -0,0 +1,50 @@ +#ifndef AUDIOGRAPHER_LISTED_SOURCE_H +#define AUDIOGRAPHER_LISTED_SOURCE_H + +#include "types.h" +#include "source.h" + +#include <list> + +namespace AudioGrapher +{ + +template<typename T> +class ListedSource : public Source<T> +{ + public: + void add_output (typename Source<T>::SinkPtr output) { outputs.push_back(output); } + void clear_outputs () { outputs.clear(); } + void remove_output (typename Source<T>::SinkPtr output) { outputs.remove(output); } + + protected: + + typedef std::list<typename Source<T>::SinkPtr> SinkList; + + /// Helper for derived classes + void output (ProcessContext<T> const & c) + { + for (typename SinkList::iterator i = outputs.begin(); i != outputs.end(); ++i) { + (*i)->process (c); + } + } + + void output (ProcessContext<T> & c) + { + if (output_size_is_one()) { + // only one output, so we can keep this non-const + outputs.front()->process (c); + } else { + output (const_cast<ProcessContext<T> const &> (c)); + } + } + + inline bool output_size_is_one () { return (!outputs.empty() && ++outputs.begin() == outputs.end()); } + + SinkList outputs; +}; + +} // namespace + +#endif //AUDIOGRAPHER_LISTED_SOURCE_H + diff --git a/libs/audiographer/audiographer/normalizer.h b/libs/audiographer/audiographer/normalizer.h new file mode 100644 index 0000000000..dcaac75568 --- /dev/null +++ b/libs/audiographer/audiographer/normalizer.h @@ -0,0 +1,82 @@ +#ifndef AUDIOGRAPHER_NORMALIZER_H +#define AUDIOGRAPHER_NORMALIZER_H + +#include "listed_source.h" +#include "sink.h" +#include "routines.h" + +#include <cstring> + +namespace AudioGrapher +{ + +class Normalizer : public ListedSource<float>, Sink<float> +{ + public: + Normalizer (float target_dB) + : enabled (false) + , buffer (0) + , buffer_size (0) + { + target = pow (10.0f, target_dB * 0.05f); + } + + ~Normalizer() + { + delete [] buffer; + } + + void set_peak (float peak) + { + if (peak == 0.0f || peak == target) { + /* don't even try */ + enabled = false; + } else { + enabled = true; + gain = target / peak; + } + } + + void alloc_buffer(nframes_t frames) + { + delete [] buffer; + buffer = new float[frames]; + buffer_size = frames; + } + + void process (ProcessContext<float> const & c) + { + if (c.frames() > buffer_size) { + throw Exception (*this, "Too many frames given to process()"); + } + + if (enabled) { + memcpy (buffer, c.data(), c.frames() * sizeof(float)); + Routines::apply_gain_to_buffer (buffer, c.frames(), gain); + } + + ProcessContext<float> c_out (c, buffer); + ListedSource<float>::output (c_out); + } + + void process (ProcessContext<float> & c) + { + if (enabled) { + Routines::apply_gain_to_buffer (c.data(), c.frames(), gain); + } + ListedSource<float>::output(c); + } + + private: + bool enabled; + float target; + float gain; + + float * buffer; + nframes_t buffer_size; +}; + + +} // namespace + +#endif // AUDIOGRAPHER_NORMALIZER_H diff --git a/libs/audiographer/audiographer/peak_reader.h b/libs/audiographer/audiographer/peak_reader.h new file mode 100644 index 0000000000..e5aaf7081c --- /dev/null +++ b/libs/audiographer/audiographer/peak_reader.h @@ -0,0 +1,38 @@ +#ifndef AUDIOGRAPHER_PEAK_READER_H +#define AUDIOGRAPHER_PEAK_READER_H + +#include "listed_source.h" +#include "sink.h" +#include "routines.h" + +namespace AudioGrapher +{ + +class PeakReader : public ListedSource<float>, public Sink<float> +{ + public: + PeakReader() : peak (0.0) {} + + float get_peak() { return peak; } + void reset() { peak = 0.0; } + + void process (ProcessContext<float> const & c) + { + peak = Routines::compute_peak (c.data(), c.frames(), peak); + ListedSource<float>::output(c); + } + + void process (ProcessContext<float> & c) + { + peak = Routines::compute_peak (c.data(), c.frames(), peak); + ListedSource<float>::output(c); + } + + private: + float peak; +}; + + +} // namespace + +#endif // AUDIOGRAPHER_PEAK_READER_H diff --git a/libs/audiographer/audiographer/process_context.h b/libs/audiographer/audiographer/process_context.h new file mode 100644 index 0000000000..080e492944 --- /dev/null +++ b/libs/audiographer/audiographer/process_context.h @@ -0,0 +1,154 @@ +#ifndef AUDIOGRAPHER_PROCESS_CONTEXT_H +#define AUDIOGRAPHER_PROCESS_CONTEXT_H + +#include "types.h" + +#include <cstring> + +namespace AudioGrapher +{ + +/** + * Processing context. Constness only applies to data, not flags + */ + +template <typename T> +class ProcessContext { + +public: + + typedef FlagField::Flag Flag; + + enum Flags { + EndOfInput = 0 + }; + +public: + + /// Basic constructor with data, frame and channel count + ProcessContext (T * data, nframes_t frames, ChannelCount channels) + : _data (data), _frames (frames), _channels (channels) {} + + /// Normal copy constructor + ProcessContext (ProcessContext<T> const & other) + : _data (other._data), _frames (other._frames), _channels (other._channels), _flags (other._flags) {} + + /// "Copy constructor" with unique data, frame and channel count, but copies flags + template<typename Y> + ProcessContext (ProcessContext<Y> const & other, T * data, nframes_t frames, ChannelCount channels) + : _data (data), _frames (frames), _channels (channels), _flags (other.flags()) {} + + /// "Copy constructor" with unique data and frame count, but copies channel count and flags + template<typename Y> + ProcessContext (ProcessContext<Y> const & other, T * data, nframes_t frames) + : _data (data), _frames (frames), _channels (other.channels()), _flags (other.flags()) {} + + /// "Copy constructor" with unique data, but copies frame and channel count + flags + template<typename Y> + ProcessContext (ProcessContext<Y> const & other, T * data) + : _data (data), _frames (other.frames()), _channels (other.channels()), _flags (other.flags()) {} + + virtual ~ProcessContext () {} + + /// \a data points to the array of data to process + inline T const * data() const { return _data; } + inline T * data() { return _data; } + + /// \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; } + + /* Flags */ + + inline bool has_flag (Flag flag) const { return _flags.has (flag); } + inline void set_flag (Flag flag) const { _flags.set (flag); } + inline void remove_flag (Flag flag) const { _flags.remove (flag); } + inline FlagField const & flags () const { return _flags; } + +protected: + T * const _data; + nframes_t _frames; + ChannelCount _channels; + + mutable FlagField _flags; +}; + +/// A process context that allocates and owns it's data buffer +template <typename T> +struct AllocatingProcessContext : public ProcessContext<T> +{ + /// Allocates uninitialized memory + AllocatingProcessContext (nframes_t frames, ChannelCount channels) + : ProcessContext<T> (new T[frames], frames, channels) {} + + /// Copy constructor, copies data from other ProcessContext + AllocatingProcessContext (ProcessContext<T> const & other) + : ProcessContext<T> (other, new T[other._frames]) + { memcpy (ProcessContext<T>::_data, other._data, other._channels * other._frames * sizeof (T)); } + + /// "Copy constructor" with uninitialized data, unique frame and channel count, but copies flags + template<typename Y> + AllocatingProcessContext (ProcessContext<Y> const & other, nframes_t frames, ChannelCount channels) + : ProcessContext<T> (other, new T[frames], frames, channels) {} + + /// "Copy constructor" with uninitialized data, unique frame count, but copies channel count and flags + template<typename Y> + AllocatingProcessContext (ProcessContext<Y> const & other, nframes_t frames) + : ProcessContext<T> (other, new T[frames], frames, other.channels()) {} + + /// "Copy constructor" uninitialized data, that copies frame and channel count + flags + template<typename Y> + AllocatingProcessContext (ProcessContext<Y> const & other) + : ProcessContext<T> (other, new T[other._frames]) {} + + ~AllocatingProcessContext () { delete [] ProcessContext<T>::_data; } +}; + +/// A wrapper for a const ProcesContext which can be created from const data +template <typename T> +class ConstProcessContext +{ + public: + /// Basic constructor with data, frame and channel count + ConstProcessContext (T const * data, nframes_t frames, ChannelCount channels) + : context (const_cast<T *>(data), frames, channels) {} + + /// Copy constructor from const ProcessContext + ConstProcessContext (ProcessContext<T> const & other) + : context (const_cast<ProcessContext<T> &> (other)) {} + + /// "Copy constructor", with unique data, frame and channel count, but copies flags + template<typename ProcessContext> + ConstProcessContext (ProcessContext const & other, T const * data, nframes_t frames, ChannelCount channels) + : context (other, const_cast<T *>(data), frames, channels) {} + + /// "Copy constructor", with unique data and frame count, but copies channel count and flags + template<typename ProcessContext> + ConstProcessContext (ProcessContext const & other, T const * data, nframes_t frames) + : context (other, const_cast<T *>(data), frames) {} + + /// "Copy constructor", with unique data, but copies frame and channel count + flags + template<typename ProcessContext> + ConstProcessContext (ProcessContext const & other, T const * data) + : context (other, const_cast<T *>(data)) {} + + inline operator ProcessContext<T> const & () { return context; } + inline ProcessContext<T> const & operator() () { return context; } + inline ProcessContext<T> const * operator& () { return &context; } + + private: + ProcessContext<T> const context; +}; + +} // namespace + +#endif // AUDIOGRAPHER_PROCESS_CONTEXT_H diff --git a/libs/audiographer/audiographer/routines.h b/libs/audiographer/audiographer/routines.h new file mode 100644 index 0000000000..9ae6b7a255 --- /dev/null +++ b/libs/audiographer/audiographer/routines.h @@ -0,0 +1,53 @@ +#ifndef AUDIOGRAPHER_ROUTINES_H +#define AUDIOGRAPHER_ROUTINES_H + +#include "types.h" + +#include <cmath> + +namespace AudioGrapher +{ + +class Routines +{ + public: + typedef float (*compute_peak_t) (float const *, nframes_t, float); + typedef void (*apply_gain_to_buffer_t) (float *, nframes_t, 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, nframes_t frames, float current_peak) + { + return (*_compute_peak) (data, frames, current_peak); + } + + static inline void apply_gain_to_buffer (float * data, nframes_t frames, float gain) + { + (*_apply_gain_to_buffer) (data, frames, gain); + } + + private: + static inline float default_compute_peak (float const * data, nframes_t frames, float current_peak) + { + for (nframes_t 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, nframes_t frames, float gain) + { + for (nframes_t i = 0; i < frames; ++i) { + data[i] *= gain; + } + } + + static compute_peak_t _compute_peak; + static apply_gain_to_buffer_t _apply_gain_to_buffer; +}; + +} // namespace + +#endif // AUDIOGRAPHER_ROUTINES_H diff --git a/libs/audiographer/audiographer/sample_format_converter.h b/libs/audiographer/audiographer/sample_format_converter.h new file mode 100644 index 0000000000..12976ffdd2 --- /dev/null +++ b/libs/audiographer/audiographer/sample_format_converter.h @@ -0,0 +1,67 @@ +#ifndef AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H +#define AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H + +#include "listed_source.h" +#include "sink.h" +#include "gdither/gdither_types.h" + +namespace AudioGrapher +{ + +/// Dither types from the gdither library +enum DitherType +{ + D_None = GDitherNone, ///< No didtering + D_Rect = GDitherRect, ///< Rectangular dithering, i.e. white noise + D_Tri = GDitherTri, ///< Triangular dithering + D_Shaped = GDitherShaped ///< Actually noise shaping, only works for 46kHzish signals +}; + +/** Sample format converter that does dithering. + * This class can only convert floats to either \a float, \a int32_t, \a int16_t, or \a uint8_t + */ +template <typename TOut> +class SampleFormatConverter : public Sink<float>, public ListedSource<TOut> +{ + public: + /** Constructor + * \param channels number of channels in stream + */ + SampleFormatConverter (uint32_t channels); + ~SampleFormatConverter (); + + /** Initialize and allocate buffers for processing. + * \param max_frames maximum number of frames that is allowed to be used in calls to \a process() + * \param type dither type from \a DitherType + * \param data_width data with in bits + * \note If the non-const version of process() is used with floats, + * there is no need to call this function. + */ + void init (nframes_t max_frames, int type, int data_width); + + /// Set whether or not clipping to [-1.0, 1.0] should occur when TOut = float. Clipping is off by default + void set_clip_floats (bool yn) { clip_floats = yn; } + + /// Processes data without modifying it + void process (ProcessContext<float> const & c_in); + + /// This version is only different in the case when \a TOut = float, and float clipping is on. + void process (ProcessContext<float> & c_in); + + private: + void reset(); + void init_common(nframes_t max_frames); // not-template-specialized part of init + void check_frame_count(nframes_t frames); + + uint32_t channels; + GDither dither; + nframes_t data_out_size; + TOut * data_out; + + bool clip_floats; + +}; + +} // namespace + +#endif // AUDIOGRAPHER_SAMPLE_FORMAT_CONVERTER_H diff --git a/libs/audiographer/audiographer/silence_trimmer.h b/libs/audiographer/audiographer/silence_trimmer.h new file mode 100644 index 0000000000..46190cfeae --- /dev/null +++ b/libs/audiographer/audiographer/silence_trimmer.h @@ -0,0 +1,191 @@ +#ifndef AUDIOGRAPHER_SILENCE_TRIMMER_H +#define AUDIOGRAPHER_SILENCE_TRIMMER_H + +#include "listed_source.h" +#include "sink.h" +#include "exception.h" +#include "utils.h" + +#include <cstring> + +namespace AudioGrapher { + +template<typename T> +class SilenceTrimmer : public ListedSource<T>, public Sink<T> +{ + public: + + SilenceTrimmer() + { + reset (); + } + + void reset() + { + in_beginning = true; + in_end = false; + trim_beginning = false; + trim_end = false; + silence_frames = 0; + max_output_frames = 0; + add_to_beginning = 0; + add_to_end = 0; + } + + void add_silence_to_beginning (nframes_t frames_per_channel) + { + if (!in_beginning) { + throw Exception(*this, "Tried to add silence to beginning after already outputting data"); + } + add_to_beginning = frames_per_channel; + } + + void add_silence_to_end (nframes_t frames_per_channel) + { + if (in_end) { + throw Exception(*this, "Tried to add silence to end after already reaching end"); + } + add_to_end = frames_per_channel; + } + + void set_trim_beginning (bool yn) + { + if (!in_beginning) { + throw Exception(*this, "Tried to set beginning trim after already outputting data"); + } + trim_beginning = yn; + } + + void set_trim_end (bool yn) + { + if (in_end) { + throw Exception(*this, "Tried to set end trim after already reaching end"); + } + trim_end = yn; + } + + void limit_output_size (nframes_t max_frames) + { + max_output_frames = max_frames; + } + + void process (ProcessContext<T> const & c) + { + if (in_end) { throw Exception(*this, "process() after reacing end of input"); } + in_end = c.has_flag (ProcessContext<T>::EndOfInput); + + nframes_t frame_index = 0; + + if (in_beginning) { + + bool has_data = true; + + // only check silence if doing either of these + // This will set both has_data and frame_index + if (add_to_beginning || trim_beginning) { + has_data = find_first_non_zero_sample (c, frame_index); + } + + // Added silence if there is silence to add + if (add_to_beginning) { + ConstProcessContext<T> c_copy (c); + if (has_data) { // There will be more output, so remove flag + c_copy().remove_flag (ProcessContext<T>::EndOfInput); + } + add_to_beginning *= c.channels(); + output_silence_frames (c_copy, add_to_beginning); + } + + // If we are not trimming the beginning, output everything + // Then has_data = true and frame_index = 0 + // Otherwise these reflect the silence state + if (has_data) { + in_beginning = false; + ConstProcessContext<T> c_out (c, &c.data()[frame_index], c.frames() - frame_index); + ListedSource<T>::output (c_out); + } + + } else if (trim_end) { // Only check zero samples if trimming end + + if (find_first_non_zero_sample (c, frame_index)) { + // context contains non-zero data + output_silence_frames (c, silence_frames); // flush intermediate silence + ListedSource<T>::output (c); // output rest of data + } else { // whole context is zero + silence_frames += c.frames(); + } + + } else { // no need to do anything special + + ListedSource<T>::output (c); + } + + // Finally if in end, add silence to end + if (in_end && add_to_end) { + add_to_end *= c.channels(); + output_silence_frames (c, add_to_end, true); + } + } + + using Sink<T>::process; + + private: + + bool find_first_non_zero_sample (ProcessContext<T> const & c, nframes_t & result_frame) + { + for (nframes_t i = 0; i < c.frames(); ++i) { + if (c.data()[i] != static_cast<T>(0.0)) { + result_frame = i; + // Round down to nearest interleaved "frame" beginning + result_frame -= result_frame % c.channels(); + return true; + } + } + return false; + } + + void output_silence_frames (ProcessContext<T> const & c, nframes_t & total_frames, bool adding_to_end = false) + { + nframes_t silence_buffer_size = Utils::get_zero_buffer_size<T>(); + if (silence_buffer_size == 0) { throw Exception (*this, "Utils::init_zeros has not been called!"); } + + bool end_of_input = c.has_flag (ProcessContext<T>::EndOfInput); + c.remove_flag (ProcessContext<T>::EndOfInput); + + while (total_frames > 0) { + nframes_t frames = std::min (silence_buffer_size, total_frames); + if (max_output_frames) { + frames = std::min (frames, max_output_frames); + } + frames -= frames % c.channels(); + + total_frames -= frames; + ConstProcessContext<T> c_out (c, Utils::get_zeros<T>(frames), frames); + + // boolean commentation :) + bool const no_more_silence_will_be_added = adding_to_end || (add_to_end == 0); + bool const is_last_frame_output_in_this_function = (total_frames == 0); + if (end_of_input && no_more_silence_will_be_added && is_last_frame_output_in_this_function) { + c_out().set_flag (ProcessContext<T>::EndOfInput); + } + ListedSource<T>::output (c_out); + } + } + + + bool in_beginning; + bool in_end; + + bool trim_beginning; + bool trim_end; + + nframes_t silence_frames; + nframes_t max_output_frames; + + nframes_t add_to_beginning; + nframes_t add_to_end; +}; + +} // namespace + +#endif // AUDIOGRAPHER_SILENCE_TRIMMER_H diff --git a/libs/audiographer/audiographer/sink.h b/libs/audiographer/audiographer/sink.h new file mode 100644 index 0000000000..486fccfd13 --- /dev/null +++ b/libs/audiographer/audiographer/sink.h @@ -0,0 +1,42 @@ +#ifndef AUDIOGRAPHER_SINK_H +#define AUDIOGRAPHER_SINK_H + +#include <boost/shared_ptr.hpp> + +#include "process_context.h" + +namespace AudioGrapher +{ + +template <typename T> +class Sink { + public: + virtual ~Sink () {} + + /** Process given data. + * The data can not be modified, so in-place processing is not allowed. + * At least this function must be implemented by deriving classes + */ + virtual void process (ProcessContext<T> const & context) = 0; + + /** Process given data + * Data may be modified, so in place processing is allowed. + * The default implementation calls the non-modifying version, + * so this function does not need to be overriden. + * However, if the sink can do in-place processing, + * overriding this is highly recommended. + * + * If this is not overridden adding "using Sink<T>::process;" + * to the deriving class declaration is suggested to avoid + * warnings about hidden virtual functions. + */ + inline virtual void process (ProcessContext<T> & context) + { + this->process (static_cast<ProcessContext<T> const &> (context)); + } +}; + +} // namespace + +#endif // AUDIOGRAPHER_SINK_H + diff --git a/libs/audiographer/audiographer/sndfile_base.h b/libs/audiographer/audiographer/sndfile_base.h new file mode 100644 index 0000000000..fd6c5f3552 --- /dev/null +++ b/libs/audiographer/audiographer/sndfile_base.h @@ -0,0 +1,30 @@ +#ifndef AUDIOGRAPHER_SNDFILE_BASE_H +#define AUDIOGRAPHER_SNDFILE_BASE_H + +#include <string> +#include <sndfile.h> +#include <sigc++/signal.h> + +#include "types.h" + +namespace AudioGrapher { + +/// Common interface for templated libsndfile readers/writers +class SndfileBase +{ + public: + + sigc::signal<void, std::string> FileWritten; + + protected: + SndfileBase (ChannelCount channels, nframes_t samplerate, int format, std::string const & path); + virtual ~SndfileBase (); + + std::string path; + SF_INFO sf_info; + SNDFILE * sndfile; +}; + +} // namespace + +#endif // AUDIOGRAPHER_SNDFILE_BASE_H
\ No newline at end of file diff --git a/libs/audiographer/audiographer/sndfile_reader.h b/libs/audiographer/audiographer/sndfile_reader.h new file mode 100644 index 0000000000..9e47da56b2 --- /dev/null +++ b/libs/audiographer/audiographer/sndfile_reader.h @@ -0,0 +1,40 @@ +#ifndef AUDIOGRAPHER_SNDFILE_READER_H +#define AUDIOGRAPHER_SNDFILE_READER_H + +#include "sndfile_base.h" +#include "listed_source.h" +#include "process_context.h" + +namespace AudioGrapher +{ + +/** Reader for audio files using libsndfile. + * Once again only short, int and float are valid template parameters + */ +template<typename T> +class SndfileReader : public virtual SndfileBase, public ListedSource<T> +{ + public: + + enum SeekType { + SeekBeginning = SEEK_SET, //< Seek from beginning of file + SeekCurrent = SEEK_CUR, //< Seek from current position + SeekEnd = SEEK_END //< Seek from end + }; + + public: + + SndfileReader (ChannelCount channels, nframes_t samplerate, int format, std::string path); + + nframes_t seek (nframes_t frames, SeekType whence); + nframes_t read (ProcessContext<T> & context); + + private: + + void init(); // init read function + sf_count_t (*read_func)(SNDFILE *, T *, sf_count_t); +}; + +} // namespace + +#endif // AUDIOGRAPHER_SNDFILE_READER_H
\ No newline at end of file diff --git a/libs/audiographer/audiographer/sndfile_writer.h b/libs/audiographer/audiographer/sndfile_writer.h new file mode 100644 index 0000000000..a92da982c1 --- /dev/null +++ b/libs/audiographer/audiographer/sndfile_writer.h @@ -0,0 +1,29 @@ +#ifndef AUDIOGRAPHER_SNDFILE_WRITER_H +#define AUDIOGRAPHER_SNDFILE_WRITER_H + +#include "sndfile_base.h" +#include "types.h" +#include "sink.h" + +namespace AudioGrapher +{ + +/// Template parameter specific parts of sndfile writer +template <typename T> +class SndfileWriter : public virtual SndfileBase, public Sink<T> +{ + public: + SndfileWriter (ChannelCount channels, nframes_t samplerate, int format, std::string const & path); + + void process (ProcessContext<T> const & c); + using Sink<T>::process; + + private: + + void init (); // Inits write function + sf_count_t (*write_func)(SNDFILE *, const T *, sf_count_t); +}; + +} // namespace + +#endif // AUDIOGRAPHER_SNDFILE_WRITER_H
\ No newline at end of file diff --git a/libs/audiographer/audiographer/source.h b/libs/audiographer/audiographer/source.h new file mode 100644 index 0000000000..8ffad204ba --- /dev/null +++ b/libs/audiographer/audiographer/source.h @@ -0,0 +1,28 @@ +#ifndef AUDIOGRAPHER_SOURCE_H +#define AUDIOGRAPHER_SOURCE_H + +#include "types.h" +#include "sink.h" + +#include <boost/shared_ptr.hpp> + +namespace AudioGrapher +{ + +template<typename T> +class Source +{ + public: + virtual ~Source () { } + + typedef boost::shared_ptr<Sink<T> > SinkPtr; + + virtual void add_output (SinkPtr output) = 0; + virtual void clear_outputs () = 0; + virtual void remove_output (SinkPtr output) = 0; +}; + +} // namespace + +#endif //AUDIOGRAPHER_SOURCE_H + diff --git a/libs/audiographer/audiographer/sr_converter.h b/libs/audiographer/audiographer/sr_converter.h new file mode 100644 index 0000000000..073fdc8247 --- /dev/null +++ b/libs/audiographer/audiographer/sr_converter.h @@ -0,0 +1,51 @@ +#ifndef AUDIOGRAPHER_SR_CONVERTER_H +#define AUDIOGRAPHER_SR_CONVERTER_H + +#include <samplerate.h> + +#include "types.h" +#include "listed_source.h" +#include "sink.h" + +namespace AudioGrapher +{ + +class SampleRateConverter : public ListedSource<float>, public Sink<float> +{ + public: + SampleRateConverter (uint32_t channels); + ~SampleRateConverter (); + + // not RT safe + void init (nframes_t in_rate, nframes_t out_rate, int quality = 0); + + // returns max amount of frames that will be output + nframes_t allocate_buffers (nframes_t max_frames); + + // could be RT safe (check libsamplerate to be sure) + void process (ProcessContext<float> const & c); + using Sink<float>::process; + + private: + + void set_end_of_input (ProcessContext<float> const & c); + void reset (); + + bool active; + uint32_t channels; + nframes_t max_frames_in; + + float * leftover_data; + nframes_t leftover_frames; + nframes_t max_leftover_frames; + + float * data_out; + nframes_t data_out_size; + + SRC_DATA src_data; + SRC_STATE* src_state; +}; + +} // namespace + +#endif // AUDIOGRAPHER_SR_CONVERTER_H diff --git a/libs/audiographer/audiographer/threader.h b/libs/audiographer/audiographer/threader.h new file mode 100644 index 0000000000..e6c3aa97bf --- /dev/null +++ b/libs/audiographer/audiographer/threader.h @@ -0,0 +1,120 @@ +#ifndef AUDIOGRAPHER_THREADER_H +#define AUDIOGRAPHER_THREADER_H + +#include <glibmm/threadpool.h> +#include <sigc++/slot.h> +#include <boost/format.hpp> + +#include <glib.h> +#include <vector> +#include <algorithm> + +#include "source.h" +#include "sink.h" +#include "exception.h" + +namespace AudioGrapher +{ + +class ThreaderException : public Exception +{ + public: + template<typename T> + ThreaderException (T const & thrower, std::exception const & e) + : Exception (thrower, + boost::str ( boost::format + ("\n\t- Dynamic type: %1%\n\t- what(): %2%") % name (e) % e.what() )) + { } +}; + +template <typename T> +class Threader : public Source<T>, public Sink<T> +{ + private: + typedef std::vector<typename Source<T>::SinkPtr> OutputVec; + + public: + + Threader (Glib::ThreadPool & thread_pool, long wait_timeout_milliseconds = 1000) + : thread_pool (thread_pool) + , readers (0) + , wait_timeout (wait_timeout_milliseconds) + { } + + virtual ~Threader () {} + + void add_output (typename Source<T>::SinkPtr output) { outputs.push_back (output); } + void clear_outputs () { outputs.clear (); } + void remove_output (typename Source<T>::SinkPtr output) { + typename OutputVec::iterator new_end = std::remove(outputs.begin(), outputs.end(), output); + outputs.erase (new_end, outputs.end()); + } + + /* The context has to be const, because this is working concurrently */ + void process (ProcessContext<T> const & c) + { + wait_mutex.lock(); + + exception.reset(); + + unsigned int outs = outputs.size(); + g_atomic_int_add (&readers, outs); + for (unsigned int i = 0; i < outs; ++i) { + thread_pool.push (sigc::bind (sigc::mem_fun (this, &Threader::process_output), c, i)); + } + + wait(); + } + + using Sink<T>::process; + + private: + + void wait() + { + Glib::TimeVal wait_time; + wait_time.assign_current_time(); + wait_time.add_milliseconds(wait_timeout); + + wait_cond.timed_wait(wait_mutex, wait_time); + bool timed_out = (g_atomic_int_get (&readers) != 0); + wait_mutex.unlock(); + if (timed_out) { throw Exception (*this, "wait timed out"); } + + if (exception) { + throw *exception; + } + } + + void process_output(ProcessContext<T> const & c, unsigned int output) + { + try { + outputs[output]->process (c); + } catch (std::exception const & e) { + // Only first exception will be passed on + exception_mutex.lock(); + if(!exception) { exception.reset (new ThreaderException (*this, e)); } + exception_mutex.unlock(); + } + + if (g_atomic_int_dec_and_test (&readers)) { + wait_cond.signal(); + } + } + + OutputVec outputs; + + Glib::ThreadPool & thread_pool; + Glib::Mutex wait_mutex; + Glib::Cond wait_cond; + gint readers; + long wait_timeout; + + Glib::Mutex exception_mutex; + boost::shared_ptr<ThreaderException> exception; + +}; + +} // namespace + +#endif //AUDIOGRAPHER_THREADER_H
\ No newline at end of file diff --git a/libs/audiographer/audiographer/tmp_file.h b/libs/audiographer/audiographer/tmp_file.h new file mode 100644 index 0000000000..a5537e3a69 --- /dev/null +++ b/libs/audiographer/audiographer/tmp_file.h @@ -0,0 +1,25 @@ +#ifndef AUDIOGRAPHER_TMP_FILE_H +#define AUDIOGRAPHER_TMP_FILE_H + +#include "sndfile_writer.h" +#include "sndfile_reader.h" + +namespace AudioGrapher +{ + +template<typename T> +class TmpFile : public SndfileWriter<T>, public SndfileReader<T> +{ + public: + + TmpFile (ChannelCount channels, nframes_t samplerate, int format) + : SndfileBase (channels, samplerate, format, "temp") + , SndfileWriter<T> (channels, samplerate, format, "temp") + , SndfileReader<T> (channels, samplerate, format, "temp") + {} + +}; + +} // namespace + +#endif // AUDIOGRAPHER_TMP_FILE_H
\ No newline at end of file diff --git a/libs/audiographer/audiographer/types.h b/libs/audiographer/audiographer/types.h new file mode 100644 index 0000000000..f48f8f807a --- /dev/null +++ b/libs/audiographer/audiographer/types.h @@ -0,0 +1,32 @@ +#ifndef AUDIOGRAPHER_TYPES_H +#define AUDIOGRAPHER_TYPES_H + +#include <stdint.h> + +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 void set (Flag flag) { _flags |= (1 << flag); } + inline void remove (Flag flag) { _flags &= ~(1 << flag); } + inline storage_type flags () const { return _flags; } + + private: + storage_type _flags; +}; + +} // namespace + +#endif // __audiographer_types_h__
\ No newline at end of file diff --git a/libs/audiographer/audiographer/utils.h b/libs/audiographer/audiographer/utils.h new file mode 100644 index 0000000000..2f9c51918b --- /dev/null +++ b/libs/audiographer/audiographer/utils.h @@ -0,0 +1,59 @@ +#ifndef AUDIOGRAPHER_UTILS_H +#define AUDIOGRAPHER_UTILS_H + +#include "types.h" +#include "exception.h" + +#include <cstring> + +namespace AudioGrapher +{ + +class Utils +{ + public: + + static void free_resources(); + + /// Initialize zero buffer, if buffer is != 0, it will be used as the zero buffer + template <typename T> + static void init_zeros (nframes_t frames, T const * buffer = 0) + { + if (frames == 0) { + throw Exception (Utils(), "init_zeros must be called with an argument greater than zero."); + } + unsigned long n_zeros = frames * sizeof (T); + if (n_zeros <= num_zeros) { return; } + delete [] zeros; + if (buffer) { + zeros = reinterpret_cast<char const *>(buffer); + } else { + zeros = new char[n_zeros]; + memset (const_cast<char *>(zeros), 0, n_zeros); + } + num_zeros = n_zeros; + } + + template <typename T> + static T const * get_zeros (nframes_t frames) + { + if (frames * sizeof (T) > num_zeros) { + throw Exception (Utils(), "init_zeros has not been called with a large enough frame count"); + } + return reinterpret_cast<T const *> (zeros); + } + + template <typename T> + static nframes_t get_zero_buffer_size () + { + return num_zeros / sizeof (T); + } + + private: + static char const * zeros; + static unsigned long num_zeros; +}; + +} // namespace + +#endif // AUDIOGRAPHER_ROUTINES_H |