summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorSakari Bergen <sakari.bergen@beatwaves.net>2009-12-27 22:09:40 +0000
committerSakari Bergen <sakari.bergen@beatwaves.net>2009-12-27 22:09:40 +0000
commit8da27200d18fe4c471a759dde8e10d85ff29d277 (patch)
tree8a68123da7cb8539a9818704363e3fd98da4d385 /libs
parentdde0848a984e06cbc1d4117d9cffa75c191f3b39 (diff)
- Fix process callbakc handling during export
- Fix filename handling when exporting multiple files - Some updates to audiographer git-svn-id: svn://localhost/ardour2/branches/3.0@6402 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs')
-rw-r--r--libs/ardour/ardour/export_graph_builder.h7
-rw-r--r--libs/ardour/ardour/export_handler.h6
-rw-r--r--libs/ardour/ardour/session.h3
-rw-r--r--libs/ardour/export_graph_builder.cc34
-rw-r--r--libs/ardour/export_handler.cc38
-rw-r--r--libs/ardour/session_export.cc63
-rw-r--r--libs/ardour/session_state.cc1
-rw-r--r--libs/audiographer/audiographer/debug_utils.h34
-rw-r--r--libs/audiographer/audiographer/debuggable.h46
-rw-r--r--libs/audiographer/audiographer/exception.h24
-rw-r--r--libs/audiographer/audiographer/process_context.h10
-rw-r--r--libs/audiographer/audiographer/sndfile_base.h3
-rw-r--r--libs/audiographer/audiographer/sr_converter.h10
-rw-r--r--libs/audiographer/audiographer/threader.h3
-rw-r--r--libs/audiographer/audiographer/throwing.h32
-rw-r--r--libs/audiographer/audiographer/type_utils.h59
-rw-r--r--libs/audiographer/audiographer/types.h13
-rw-r--r--libs/audiographer/src/sndfile_reader.cc1
-rw-r--r--libs/audiographer/src/sndfile_writer.cc5
-rw-r--r--libs/audiographer/src/sr_converter.cc65
-rw-r--r--libs/audiographer/tests/sr_converter_test.cc28
-rw-r--r--libs/audiographer/tests/utils.h16
22 files changed, 373 insertions, 128 deletions
diff --git a/libs/ardour/ardour/export_graph_builder.h b/libs/ardour/ardour/export_graph_builder.h
index 1244afd647..2f57aaf583 100644
--- a/libs/ardour/ardour/export_graph_builder.h
+++ b/libs/ardour/ardour/export_graph_builder.h
@@ -62,6 +62,7 @@ class ExportGraphBuilder
~ExportGraphBuilder ();
int process (nframes_t frames, bool last_cycle);
+ bool process_normalize (); // returns true when finished
void reset ();
void add_config (FileSpec const & config);
@@ -124,6 +125,9 @@ class ExportGraphBuilder
void add_child (FileSpec const & new_config);
bool operator== (FileSpec const & other_config) const;
+ /// Returns true when finished
+ bool process ();
+
private:
typedef boost::shared_ptr<AudioGrapher::PeakReader> PeakReaderPtr;
typedef boost::shared_ptr<AudioGrapher::Normalizer> NormalizerPtr;
@@ -132,7 +136,6 @@ class ExportGraphBuilder
typedef boost::shared_ptr<AudioGrapher::AllocatingProcessContext<Sample> > BufferPtr;
void start_post_processing();
- void do_post_processing();
ExportGraphBuilder & parent;
@@ -217,6 +220,8 @@ class ExportGraphBuilder
Sample * process_buffer;
nframes_t process_buffer_frames;
+ std::list<Normalizer *> normalizers;
+
Glib::ThreadPool thread_pool;
};
diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h
index f7f3b863b9..971c0c4940 100644
--- a/libs/ardour/ardour/export_handler.h
+++ b/libs/ardour/ardour/export_handler.h
@@ -112,17 +112,21 @@ class ExportHandler : public ExportElementFactory
private:
+ int process (nframes_t frames);
+
Session & session;
GraphBuilderPtr graph_builder;
StatusPtr export_status;
ConfigMap config_map;
bool realtime;
+ bool normalizing;
/* Timespan management */
void start_timespan ();
int process_timespan (nframes_t frames);
+ int process_normalize ();
void finish_timespan ();
typedef std::pair<ConfigMap::iterator, ConfigMap::iterator> TimespanBounds;
@@ -131,8 +135,6 @@ class ExportHandler : public ExportElementFactory
PBD::ScopedConnection process_connection;
sframes_t process_position;
-
- PBD::ScopedConnection export_read_finished_connection;
/* CD Marker stuff */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index ae9c1f58a5..b72822bd1c 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -530,7 +530,6 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
int start_audio_export (nframes_t position, bool realtime);
PBD::Signal1<int,nframes_t> ProcessExport;
- PBD::Signal0<void> ExportReadFinished;
static PBD::Signal2<void,std::string, std::string> Exported;
void add_source (boost::shared_ptr<Source>);
@@ -934,7 +933,7 @@ class Session : public PBD::StatefulDestructible, public PBD::ScopedConnectionLi
nframes_t post_export_position;
bool _exporting;
- bool _exporting_realtime;
+ bool _export_rolling;
boost::shared_ptr<ExportHandler> export_handler;
boost::shared_ptr<ExportStatus> export_status;
diff --git a/libs/ardour/export_graph_builder.cc b/libs/ardour/export_graph_builder.cc
index 5b5ec16b36..3b1d1e3838 100644
--- a/libs/ardour/export_graph_builder.cc
+++ b/libs/ardour/export_graph_builder.cc
@@ -56,11 +56,26 @@ ExportGraphBuilder::process (nframes_t frames, bool last_cycle)
return 0;
}
+bool
+ExportGraphBuilder::process_normalize ()
+{
+ for (std::list<Normalizer *>::iterator it = normalizers.begin(); it != normalizers.end(); /* ++ in loop */) {
+ if ((*it)->process()) {
+ it = normalizers.erase (it);
+ } else {
+ ++it;
+ }
+ }
+
+ return normalizers.empty();
+}
+
void
ExportGraphBuilder::reset ()
{
channel_configs.clear ();
channels.clear ();
+ normalizers.clear ();
}
void
@@ -252,20 +267,21 @@ ExportGraphBuilder::Normalizer::operator== (FileSpec const & other_config) const
config.format->normalize_target() == other_config.format->normalize_target();
}
-void
-ExportGraphBuilder::Normalizer::start_post_processing()
+bool
+ExportGraphBuilder::Normalizer::process()
{
- normalizer->set_peak (peak_reader->get_peak());
- tmp_file->seek (0, SndfileReader<Sample>::SeekBeginning);
- parent.thread_pool.push (sigc::mem_fun (*this, &Normalizer::do_post_processing));
+ ProcessContext<Sample> buffer_copy (*buffer);
+ tmp_file->read (buffer_copy);
+ normalizer->process (buffer_copy);
+ return buffer_copy.frames() != buffer->frames();
}
void
-ExportGraphBuilder::Normalizer::do_post_processing()
+ExportGraphBuilder::Normalizer::start_post_processing()
{
- while (tmp_file->read (*buffer) == buffer->frames()) {
- normalizer->process (*buffer);
- }
+ normalizer->set_peak (peak_reader->get_peak());
+ tmp_file->seek (0, SndfileReader<Sample>::SeekBeginning);
+ parent.normalizers.push_back (this);
}
/* SRC */
diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc
index 3ce07cf44d..a9335add37 100644
--- a/libs/ardour/export_handler.cc
+++ b/libs/ardour/export_handler.cc
@@ -119,7 +119,7 @@ ExportHandler::add_export_config (TimespanPtr timespan, ChannelConfigPtr channel
FileSpec spec (channel_config, format, filename);
ConfigPair pair (timespan, spec);
config_map.insert (pair);
-
+
return true;
}
@@ -138,8 +138,6 @@ ExportHandler::do_export (bool rt)
/* Start export */
realtime = rt;
-
- session.ExportReadFinished.connect_same_thread (export_read_finished_connection, boost::bind (&ExportHandler::finish_timespan, this));
start_timespan ();
}
@@ -149,6 +147,7 @@ ExportHandler::start_timespan ()
export_status->timespan++;
if (config_map.empty()) {
+ // freewheeling has to be stopped from outside the process cycle
export_status->running = false;
return;
}
@@ -160,17 +159,33 @@ ExportHandler::start_timespan ()
timespan_bounds = config_map.equal_range (current_timespan);
graph_builder->reset ();
for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) {
- graph_builder->add_config (it->second);
+ // Filenames can be shared across timespans
+ FileSpec & spec = it->second;
+ spec.filename->set_timespan (it->first);
+ graph_builder->add_config (spec);
}
/* start export */
- session.ProcessExport.connect_same_thread (process_connection, boost::bind (&ExportHandler::process_timespan, this, _1));
+ normalizing = false;
+ session.ProcessExport.connect_same_thread (process_connection, boost::bind (&ExportHandler::process, this, _1));
process_position = current_timespan->get_start();
session.start_audio_export (process_position, realtime);
}
int
+ExportHandler::process (nframes_t frames)
+{
+ if (!export_status->running) {
+ return 0;
+ } else if (normalizing) {
+ return process_normalize ();
+ } else {
+ return process_timespan (frames);
+ }
+}
+
+int
ExportHandler::process_timespan (nframes_t frames)
{
/* update position */
@@ -184,6 +199,7 @@ ExportHandler::process_timespan (nframes_t frames)
if (last_cycle) {
frames_to_read = end - process_position;
export_status->stop = true;
+ normalizing = true;
} else {
frames_to_read = frames;
}
@@ -196,11 +212,19 @@ ExportHandler::process_timespan (nframes_t frames)
return graph_builder->process (frames_to_read, last_cycle);
}
+int
+ExportHandler::process_normalize ()
+{
+ if (graph_builder->process_normalize ()) {
+ finish_timespan ();
+ }
+
+ return 0;
+}
+
void
ExportHandler::finish_timespan ()
{
- process_connection.disconnect ();
-
while (config_map.begin() != timespan_bounds.second) {
config_map.erase (config_map.begin());
}
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
index 86a6250ffd..9a277f5655 100644
--- a/libs/ardour/session_export.cc
+++ b/libs/ardour/session_export.cc
@@ -123,8 +123,6 @@ Session::start_audio_export (nframes_t position, bool realtime)
*/
_transport_frame = position;
-
- _exporting_realtime = realtime;
export_status->stop = false;
/* get transport ready. note how this is calling butler functions
@@ -144,39 +142,30 @@ Session::start_audio_export (nframes_t position, bool realtime)
return -1;
}
- if (realtime) {
- last_process_function = process_function;
- process_function = &Session::process_export;
- } else {
- _engine.Freewheel.connect_same_thread (export_freewheel_connection, boost::bind (&Session::process_export_fw, this, _1));
- return _engine.freewheel (true);
- }
-
- return 0;
+ _engine.Freewheel.connect_same_thread (export_freewheel_connection, boost::bind (&Session::process_export_fw, this, _1));
+ _export_rolling = true;
+ return _engine.freewheel (true);
}
void
Session::process_export (nframes_t nframes)
{
- try {
-
- if (export_status->stop) {
- stop_audio_export ();
- return;
- }
-
- if (!_exporting_realtime) {
- /* make sure we've caught up with disk i/o, since
- we're running faster than realtime c/o JACK.
- */
+ if (_export_rolling && export_status->stop) {
+ stop_audio_export ();
+ }
- _butler->wait_until_finished ();
- }
+ if (_export_rolling) {
+ /* make sure we've caught up with disk i/o, since
+ we're running faster than realtime c/o JACK.
+ */
+ _butler->wait_until_finished ();
/* do the usual stuff */
process_without_events (nframes);
-
+ }
+
+ try {
/* handle export - XXX what about error handling? */
ProcessExport (nframes);
@@ -197,23 +186,16 @@ Session::process_export_fw (nframes_t nframes)
int
Session::stop_audio_export ()
{
- if (_exporting_realtime) {
- process_function = last_process_function;
- } else {
- export_freewheel_connection.disconnect();
- }
-
/* can't use stop_transport() here because we need
an immediate halt and don't require all the declick
stuff that stop_transport() implements.
*/
realtime_stop (true, true);
+ _export_rolling = false;
_butler->schedule_transport_work ();
- if (!export_status->aborted()) {
- ExportReadFinished ();
- } else {
+ if (export_status->aborted()) {
finalize_audio_export ();
}
@@ -225,20 +207,11 @@ void
Session::finalize_audio_export ()
{
_exporting = false;
- export_status->running = false;
-
- if (!_exporting_realtime) {
- _engine.freewheel (false);
- _exporting_realtime = false;
- }
+ _export_rolling = false;
/* Clean up */
- /* BOOST SIGNAL are these necessary?
- ProcessExport.clear();
- ExportReadFinished.clear();
- */
-
+ _engine.freewheel (false);
export_freewheel_connection.disconnect();
export_handler.reset();
export_status.reset();
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 13b9112866..9394cd7bec 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -209,7 +209,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
g_atomic_int_set (&_capture_load_min, 100);
_play_range = false;
_exporting = false;
- _exporting_realtime = false;
_gain_automation_buffer = 0;
_pan_automation_buffer = 0;
_npan_buffers = 0;
diff --git a/libs/audiographer/audiographer/debug_utils.h b/libs/audiographer/audiographer/debug_utils.h
new file mode 100644
index 0000000000..8b45f0d65c
--- /dev/null
+++ b/libs/audiographer/audiographer/debug_utils.h
@@ -0,0 +1,34 @@
+#ifndef AUDIOGRAPHER_DEBUG_UTILS_H
+#define AUDIOGRAPHER_DEBUG_UTILS_H
+
+#include <string>
+
+#ifdef __GNUC__
+#include <cxxabi.h>
+#endif
+
+namespace AudioGrapher
+{
+
+struct DebugUtils
+{
+ template<typename T>
+ static std::string demangled_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();
+ }
+
+};
+
+} // namespace
+
+#endif // AUDIOGRAPHER_DEBUG_UTILS_H
diff --git a/libs/audiographer/audiographer/debuggable.h b/libs/audiographer/audiographer/debuggable.h
new file mode 100644
index 0000000000..4126327b86
--- /dev/null
+++ b/libs/audiographer/audiographer/debuggable.h
@@ -0,0 +1,46 @@
+#ifndef AUDIOGRAPHER_DEBUGGABLE_H
+#define AUDIOGRAPHER_DEBUGGABLE_H
+
+#ifndef DEFAULT_DEBUG_LEVEL
+#define DEFAULT_DEBUG_LEVEL DebugNone
+#endif
+
+#include <iostream>
+
+namespace AudioGrapher
+{
+
+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
+};
+
+/// Class that allows optimizing out debugging code during compile time
+template<DebugLevel L = DEFAULT_DEBUG_LEVEL>
+class Debuggable
+{
+ protected:
+ Debuggable(std::ostream & debug_stream = std::cerr)
+ : stream (debug_stream) {}
+
+ bool debug_level (DebugLevel level) {
+ #ifdef NDEBUG
+ return false;
+ #else
+ return L >= level;
+ #endif
+ }
+ std::ostream & debug_stream() { return stream; }
+
+ private:
+ std::ostream & stream;
+};
+
+
+} // namespace
+
+#endif // AUDIOGRAPHER_DEBUGGABLE_H
diff --git a/libs/audiographer/audiographer/exception.h b/libs/audiographer/audiographer/exception.h
index a179b30f91..43891db619 100644
--- a/libs/audiographer/audiographer/exception.h
+++ b/libs/audiographer/audiographer/exception.h
@@ -3,10 +3,11 @@
#include <exception>
#include <string>
-#include <cxxabi.h>
#include <boost/format.hpp>
+#include "audiographer/debug_utils.h"
+
namespace AudioGrapher
{
@@ -15,8 +16,9 @@ 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))
+ : reason (boost::str (boost::format
+ ("Exception thrown by %1%: %2%")
+ % DebugUtils::demangled_name (thrower) % reason))
{}
virtual ~Exception () throw() { }
@@ -25,22 +27,6 @@ class Exception : public std::exception
{
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;
diff --git a/libs/audiographer/audiographer/process_context.h b/libs/audiographer/audiographer/process_context.h
index 080e492944..654da2565a 100644
--- a/libs/audiographer/audiographer/process_context.h
+++ b/libs/audiographer/audiographer/process_context.h
@@ -1,9 +1,11 @@
#ifndef AUDIOGRAPHER_PROCESS_CONTEXT_H
#define AUDIOGRAPHER_PROCESS_CONTEXT_H
-#include "types.h"
+#include <boost/static_assert.hpp>
+#include <boost/type_traits.hpp>
-#include <cstring>
+#include "types.h"
+#include "type_utils.h"
namespace AudioGrapher
{
@@ -14,7 +16,9 @@ namespace AudioGrapher
template <typename T>
class ProcessContext {
-
+
+ BOOST_STATIC_ASSERT (boost::has_trivial_destructor<T>::value);
+
public:
typedef FlagField::Flag Flag;
diff --git a/libs/audiographer/audiographer/sndfile_base.h b/libs/audiographer/audiographer/sndfile_base.h
index fd6c5f3552..7f8da752e8 100644
--- a/libs/audiographer/audiographer/sndfile_base.h
+++ b/libs/audiographer/audiographer/sndfile_base.h
@@ -6,11 +6,12 @@
#include <sigc++/signal.h>
#include "types.h"
+#include "debuggable.h"
namespace AudioGrapher {
/// Common interface for templated libsndfile readers/writers
-class SndfileBase
+class SndfileBase : public Debuggable<>
{
public:
diff --git a/libs/audiographer/audiographer/sr_converter.h b/libs/audiographer/audiographer/sr_converter.h
index 073fdc8247..a9bfc7c4c3 100644
--- a/libs/audiographer/audiographer/sr_converter.h
+++ b/libs/audiographer/audiographer/sr_converter.h
@@ -3,14 +3,20 @@
#include <samplerate.h>
-#include "types.h"
+#include "debuggable.h"
#include "listed_source.h"
#include "sink.h"
+#include "throwing.h"
+#include "types.h"
namespace AudioGrapher
{
-class SampleRateConverter : public ListedSource<float>, public Sink<float>
+class SampleRateConverter
+ : public ListedSource<float>
+ , public Sink<float>
+ , public Debuggable<>
+ , public Throwing<>
{
public:
SampleRateConverter (uint32_t channels);
diff --git a/libs/audiographer/audiographer/threader.h b/libs/audiographer/audiographer/threader.h
index e6c3aa97bf..ad6a542126 100644
--- a/libs/audiographer/audiographer/threader.h
+++ b/libs/audiographer/audiographer/threader.h
@@ -23,7 +23,8 @@ class ThreaderException : public Exception
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() ))
+ ("\n\t- Dynamic type: %1%\n\t- what(): %2%")
+ % DebugUtils::demangled_name (e) % e.what() ))
{ }
};
diff --git a/libs/audiographer/audiographer/throwing.h b/libs/audiographer/audiographer/throwing.h
new file mode 100644
index 0000000000..05a056d5e9
--- /dev/null
+++ b/libs/audiographer/audiographer/throwing.h
@@ -0,0 +1,32 @@
+#ifndef AUDIOGRAPHER_THROWING_H
+#define AUDIOGRAPHER_THROWING_H
+
+#ifndef DEFAULT_THROW_LEVEL
+#define DEFAULT_THROW_LEVEL ThrowStrict
+#endif
+
+namespace AudioGrapher
+{
+
+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
+};
+
+/// Class that allows optimizing out error checking during compile time
+template<ThrowLevel L = DEFAULT_THROW_LEVEL>
+class Throwing
+{
+ protected:
+ Throwing() {}
+ bool throw_level (ThrowLevel level) { return L >= level; }
+};
+
+
+} // namespace
+
+#endif // AUDIOGRAPHER_THROWING_H
diff --git a/libs/audiographer/audiographer/type_utils.h b/libs/audiographer/audiographer/type_utils.h
new file mode 100644
index 0000000000..84a28f3d07
--- /dev/null
+++ b/libs/audiographer/audiographer/type_utils.h
@@ -0,0 +1,59 @@
+#ifndef AUDIOGRAPHER_TYPE_UTILS_H
+#define AUDIOGRAPHER_TYPE_UTILS_H
+
+#include "types.h"
+#include <boost/static_assert.hpp>
+#include <boost/type_traits.hpp>
+#include <memory>
+#include <algorithm>
+
+namespace AudioGrapher
+{
+
+class TypeUtilsBase
+{
+ protected:
+
+ template<typename T, bool b>
+ static void do_fill(T * buffer, nframes_t frames, const boost::integral_constant<bool, b>&)
+ { std::uninitialized_fill_n (buffer, frames, T()); }
+
+ template<typename T>
+ static void do_fill(T * buffer, nframes_t frames, const boost::true_type&)
+ { memset (buffer, frames * sizeof(T), 0); }
+
+ private:
+};
+
+template<typename T>
+class TypeUtils : private TypeUtilsBase
+{
+ BOOST_STATIC_ASSERT (boost::has_trivial_destructor<T>::value);
+
+ typedef boost::integral_constant<bool,
+ boost::is_floating_point<T>::value ||
+ boost::is_signed<T>::value> zero_fillable;
+ public:
+ 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)
+ { std::uninitialized_copy (source, &source[frames], destination); }
+
+ inline static void move (T* source, T* destination, nframes_t frames)
+ {
+ if (destination < source) {
+ std::copy (source, &source[frames], destination);
+ } else {
+ std::copy_backward (source, &source[frames], destination);
+ }
+ }
+
+ private:
+
+};
+
+
+} // namespace
+
+#endif // AUDIOGRAPHER_TYPE_UTILS_H
diff --git a/libs/audiographer/audiographer/types.h b/libs/audiographer/audiographer/types.h
index f48f8f807a..5d31899748 100644
--- a/libs/audiographer/audiographer/types.h
+++ b/libs/audiographer/audiographer/types.h
@@ -18,10 +18,15 @@ class FlagField {
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; }
+ 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;
diff --git a/libs/audiographer/src/sndfile_reader.cc b/libs/audiographer/src/sndfile_reader.cc
index 0508110314..8297844721 100644
--- a/libs/audiographer/src/sndfile_reader.cc
+++ b/libs/audiographer/src/sndfile_reader.cc
@@ -33,6 +33,7 @@ SndfileReader<T>::read (ProcessContext<T> & context)
nframes_t frames_read = (*read_func) (sndfile, context.data(), context.frames());
if (frames_read < context.frames()) {
+ context.frames() = frames_read;
context.set_flag (ProcessContext<T>::EndOfInput);
}
output (context);
diff --git a/libs/audiographer/src/sndfile_writer.cc b/libs/audiographer/src/sndfile_writer.cc
index d12d6b943b..f8c9ea63b5 100644
--- a/libs/audiographer/src/sndfile_writer.cc
+++ b/libs/audiographer/src/sndfile_writer.cc
@@ -60,9 +60,10 @@ SndfileWriter<T>::process (ProcessContext<T> const & c)
if (c.has_flag(ProcessContext<T>::EndOfInput)) {
sf_write_sync (sndfile);
- //#ifdef HAVE_SIGCPP
FileWritten (path);
- //#endif
+ if (debug_level (DebugProcess)) {
+ debug_stream() << str ( format("Finished writing file %1%") % path) << std::endl;
+ }
}
}
diff --git a/libs/audiographer/src/sr_converter.cc b/libs/audiographer/src/sr_converter.cc
index c61b3d0728..c62fd719e8 100644
--- a/libs/audiographer/src/sr_converter.cc
+++ b/libs/audiographer/src/sr_converter.cc
@@ -1,19 +1,10 @@
#include "audiographer/sr_converter.h"
#include "audiographer/exception.h"
+#include "audiographer/type_utils.h"
#include <cmath>
-#include <cstring>
#include <boost/format.hpp>
-#define ENABLE_DEBUG 0
-
-#if ENABLE_DEBUG
- #include <iostream>
- #define DEBUG(str) std::cout << str << std::endl;
-#else
- #define DEBUG(str)
-#endif
-
namespace AudioGrapher
{
using boost::format;
@@ -44,8 +35,11 @@ SampleRateConverter::init (nframes_t in_rate, nframes_t out_rate, int quality)
active = true;
int err;
- if ((src_state = src_new (quality, channels, &err)) == 0) {
- throw Exception (*this, str (format ("Cannot initialize sample rate converter: %1%") % src_strerror (err)));
+ src_state = src_new (quality, channels, &err);
+ if (throw_level (ThrowObject) && !src_state) {
+ throw Exception (*this, str (format
+ ("Cannot initialize sample rate converter: %1%")
+ % src_strerror (err)));
}
src_data.src_ratio = (double) out_rate / (double) in_rate;
@@ -70,7 +64,7 @@ SampleRateConverter::allocate_buffers (nframes_t max_frames)
max_leftover_frames = 4 * max_frames;
leftover_data = (float *) realloc (leftover_data, max_leftover_frames * sizeof (float));
- if (!leftover_data) {
+ if (throw_level (ThrowObject) && !leftover_data) {
throw Exception (*this, "A memory allocation error occured");
}
@@ -92,13 +86,13 @@ SampleRateConverter::process (ProcessContext<float> const & c)
nframes_t frames = c.frames();
float * in = const_cast<float *> (c.data()); // TODO check if this is safe!
- if (frames > max_frames_in) {
+ if (throw_level (ThrowStrict) && frames > max_frames_in) {
throw Exception (*this, str (format (
"process() called with too many frames, %1% instead of %2%")
% frames % max_frames_in));
}
- if (frames % channels != 0) {
+ 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));
@@ -121,7 +115,7 @@ SampleRateConverter::process (ProcessContext<float> const & c)
/* first time, append new data from data_in into the leftover_data buffer */
- memcpy (&leftover_data [leftover_frames * channels], in, frames * sizeof(float));
+ TypeUtils<float>::copy (&leftover_data [leftover_frames * channels], in, frames);
src_data.input_frames = frames + leftover_frames;
} else {
@@ -140,23 +134,28 @@ SampleRateConverter::process (ProcessContext<float> const & c)
first_time = false;
- DEBUG ("data_in: " << src_data.data_in);
- DEBUG ("input_frames: " << src_data.input_frames);
- DEBUG ("data_out: " << src_data.data_out);
- DEBUG ("output_frames: " << src_data.output_frames);
+ if (debug_level (DebugVerbose)) {
+ debug_stream() << "data_in: " << src_data.data_in <<
+ ", input_frames: " << src_data.input_frames <<
+ ", data_out: " << src_data.data_out <<
+ ", output_frames: " << src_data.output_frames << std::endl;
+ }
- if ((err = src_process (src_state, &src_data)) != 0) {
- throw Exception (*this, str (format ("An error occured during sample rate conversion: %1%") % src_strerror (err)));
+ err = src_process (src_state, &src_data);
+ if (throw_level (ThrowProcess) && err) {
+ throw Exception (*this, str (format
+ ("An error occured during sample rate conversion: %1%")
+ % src_strerror (err)));
}
leftover_frames = src_data.input_frames - src_data.input_frames_used;
if (leftover_frames > 0) {
- if (leftover_frames > max_leftover_frames) {
+ if (throw_level (ThrowProcess) && leftover_frames > max_leftover_frames) {
throw Exception(*this, "leftover frames overflowed");
}
- memmove (leftover_data, (char *) &src_data.data_in[src_data.input_frames_used * channels],
- leftover_frames * channels * sizeof(float));
+ TypeUtils<float>::move (&src_data.data_in[src_data.input_frames_used * channels],
+ leftover_data, leftover_frames * channels);
}
ProcessContext<float> c_out (c, data_out, src_data.output_frames_gen * channels);
@@ -165,11 +164,17 @@ SampleRateConverter::process (ProcessContext<float> const & c)
}
output (c_out);
- DEBUG ("src_data.output_frames_gen: " << src_data.output_frames_gen << ", leftover_frames: " << leftover_frames);
+ if (debug_level (DebugProcess)) {
+ debug_stream() <<
+ "src_data.output_frames_gen: " << src_data.output_frames_gen <<
+ ", leftover_frames: " << leftover_frames << std::endl;
+ }
- if (src_data.output_frames_gen == 0 && leftover_frames) { throw Exception (*this, boost::str (boost::format (
- "No output frames genereated with %1% leftover frames")
- % leftover_frames)); }
+ if (throw_level (ThrowProcess) && src_data.output_frames_gen == 0 && leftover_frames) {
+ throw Exception (*this, boost::str (boost::format
+ ("No output frames genereated with %1% leftover frames")
+ % leftover_frames));
+ }
} while (leftover_frames > frames);
@@ -189,7 +194,9 @@ void SampleRateConverter::set_end_of_input (ProcessContext<float> const & c)
/* No idea why this has to be done twice for all data to be written,
* but that just seems to be the way it is...
*/
+ dummy.remove_flag (ProcessContext<float>::EndOfInput);
process (dummy);
+ dummy.set_flag (ProcessContext<float>::EndOfInput);
process (dummy);
}
diff --git a/libs/audiographer/tests/sr_converter_test.cc b/libs/audiographer/tests/sr_converter_test.cc
index 59c05806c6..e7b49a1b71 100644
--- a/libs/audiographer/tests/sr_converter_test.cc
+++ b/libs/audiographer/tests/sr_converter_test.cc
@@ -9,6 +9,7 @@ class SampleRateConverterTest : public CppUnit::TestFixture
CPPUNIT_TEST (testNoConversion);
CPPUNIT_TEST (testUpsampleLength);
CPPUNIT_TEST (testDownsampleLength);
+ CPPUNIT_TEST (testRespectsEndOfInput);
CPPUNIT_TEST_SUITE_END ();
public:
@@ -17,6 +18,7 @@ class SampleRateConverterTest : public CppUnit::TestFixture
frames = 128;
random_data = TestUtils::init_random_data(frames);
sink.reset (new AppendingVectorSink<float>());
+ grabber.reset (new ProcessContextGrabber<float>());
converter.reset (new SampleRateConverter (1));
}
@@ -88,10 +90,36 @@ class SampleRateConverterTest : public CppUnit::TestFixture
CPPUNIT_ASSERT (half_frames - tolerance < frames_output && frames_output < half_frames + tolerance);
}
+ void testRespectsEndOfInput()
+ {
+ assert (frames % 2 == 0);
+ nframes_t const half_frames = frames / 2;
+
+ converter->init (44100, 48000);
+ converter->allocate_buffers (half_frames);
+ converter->add_output (grabber);
+
+ ProcessContext<float> c (random_data, half_frames, 1);
+ converter->process (c);
+ ProcessContext<float> c2 (&random_data[half_frames], half_frames / 2, 1);
+ c2.set_flag (ProcessContext<float>::EndOfInput);
+ converter->process (c2);
+
+ for (std::list<ProcessContext<float> >::iterator it = grabber->contexts.begin(); it != grabber->contexts.end(); ++it) {
+ std::list<ProcessContext<float> >::iterator next = it; ++next;
+ if (next == grabber->contexts.end()) {
+ CPPUNIT_ASSERT (it->has_flag (ProcessContext<float>::EndOfInput));
+ } else {
+ CPPUNIT_ASSERT (!it->has_flag (ProcessContext<float>::EndOfInput));
+ }
+ }
+ }
+
private:
boost::shared_ptr<SampleRateConverter > converter;
boost::shared_ptr<AppendingVectorSink<float> > sink;
+ boost::shared_ptr<ProcessContextGrabber<float> > grabber;
float * random_data;
nframes_t frames;
diff --git a/libs/audiographer/tests/utils.h b/libs/audiographer/tests/utils.h
index 6b6a1fea0f..b13a7b7f07 100644
--- a/libs/audiographer/tests/utils.h
+++ b/libs/audiographer/tests/utils.h
@@ -12,6 +12,7 @@
#include "audiographer/exception.h"
#include <vector>
+#include <list>
#include <cstring>
#include <cstdlib>
#include <ctime>
@@ -116,4 +117,19 @@ class ThrowingSink : public AudioGrapher::Sink<T>
using AudioGrapher::Sink<T>::process;
};
+template<typename T>
+class ProcessContextGrabber : public AudioGrapher::Sink<T>
+{
+ public:
+ void process (AudioGrapher::ProcessContext<T> const & c)
+ {
+ contexts.push_back (c);
+ }
+ using AudioGrapher::Sink<T>::process;
+
+ typedef std::list<AudioGrapher::ProcessContext<T> > ContextList;
+ ContextList contexts;
+
+};
+
#endif // AUDIOGRAPHER_TESTS_UTILS_H