Do the delay line in floats with an AudioBuffer.
authorCarl Hetherington <cth@carlh.net>
Sun, 4 Nov 2012 19:40:33 +0000 (19:40 +0000)
committerCarl Hetherington <cth@carlh.net>
Sun, 4 Nov 2012 19:40:33 +0000 (19:40 +0000)
.be/aff5ca2c-44ee-4ed6-800b-4abe9c3e794c/bugs/cc075bd0-4641-4b2c-83b1-4adb05433f71/values
src/lib/decoder.cc
src/lib/decoder.h
src/lib/delay_line.cc
src/lib/delay_line.h
src/lib/util.cc
src/lib/util.h
test/test.cc

index 0fd81c0cd669575d60ebd896d373e74b1de85d50..84f8f83bfc47e44af0e56b00e60f70b7079b782e 100644 (file)
@@ -7,7 +7,7 @@ reporter: Carl Hetherington <cth@carlh.net>
 severity: minor
 
 
-status: open
+status: fixed
 
 
 summary: Do the delay line in deinterleaved floats to simplify code.
index 3feaf43e9784cf19cffd3a840386fbafe26c75fe..110d2aa887d2f1365b2236e59814bd6cd02c9d60 100644 (file)
@@ -58,7 +58,7 @@ Decoder::Decoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o,
        , _ignore_length (ignore_length)
        , _video_frame_index (0)
        , _delay_line (0)
-       , _delay_in_bytes (0)
+       , _delay_in_frames (0)
        , _audio_frames_processed (0)
 {
        
@@ -73,9 +73,9 @@ Decoder::~Decoder ()
 void
 Decoder::process_begin ()
 {
-       _delay_in_bytes = _film->audio_delay() * audio_sample_rate() * audio_channels() * bytes_per_audio_sample() / 1000;
+       _delay_in_frames = _film->audio_delay() * audio_sample_rate() / 1000;
        delete _delay_line;
-       _delay_line = new DelayLine (_delay_in_bytes);
+       _delay_line = new DelayLine (audio_channels(), _delay_in_frames);
 
        _audio_frames_processed = 0;
 }
@@ -84,11 +84,10 @@ Decoder::process_begin ()
 void
 Decoder::process_end ()
 {
-       if (_delay_in_bytes < 0) {
-               uint8_t remainder[-_delay_in_bytes];
-               _delay_line->get_remaining (remainder);
-               _audio_frames_processed += _delay_in_bytes / (audio_channels() * bytes_per_audio_sample());
-               emit_audio (remainder, -_delay_in_bytes);
+       if (_delay_in_frames < 0 && _opt->decode_audio && audio_channels()) {
+               shared_ptr<AudioBuffers> b (new AudioBuffers (audio_channels(), -_delay_in_frames));
+               b->make_silent ();
+               emit_audio (b);
        }
 
        /* If we cut the decode off, the audio may be short; push some silence
@@ -105,26 +104,14 @@ Decoder::process_end ()
                                 _audio_frames_processed)
                );
        
-       if (audio_short_by_frames >= 0 && _opt->decode_audio) {
+       if (audio_short_by_frames > 0 && _opt->decode_audio && audio_channels()) {
 
                _film->log()->log (String::compose ("Source length is %1; %2 frames of audio processed.", video_frame_index(), _audio_frames_processed));
                _film->log()->log (String::compose ("Adding %1 frames of silence to the end.", audio_short_by_frames));
 
-               /* XXX: this is slightly questionable; does memset () give silence with all
-                  sample formats?
-               */
-
-               int64_t bytes = audio_short_by_frames * _film->audio_channels() * bytes_per_audio_sample();
-               
-               int64_t const silence_size = 16 * 1024 * _film->audio_channels() * bytes_per_audio_sample();
-               uint8_t silence[silence_size];
-               memset (silence, 0, silence_size);
-               
-               while (bytes) {
-                       int64_t const t = min (bytes, silence_size);
-                       emit_audio (silence, t);
-                       bytes -= t;
-               }
+               shared_ptr<AudioBuffers> b (new AudioBuffers (audio_channels(), audio_short_by_frames));
+               b->make_silent ();
+               emit_audio (b);
        }
 }
 
@@ -167,15 +154,6 @@ Decoder::pass ()
  */
 void
 Decoder::process_audio (uint8_t* data, int size)
-{
-       /* Push into the delay line */
-       size = _delay_line->feed (data, size);
-
-       emit_audio (data, size);
-}
-
-void
-Decoder::emit_audio (uint8_t* data, int size)
 {
        if (size == 0) {
                return;
@@ -250,10 +228,15 @@ Decoder::emit_audio (uint8_t* data, int size)
                }
        }
 
-       /* Update the number of audio frames we've pushed to the encoder */
-       _audio_frames_processed += audio->frames ();
+       _delay_line->feed (audio);
+       emit_audio (audio);
+}
 
+void
+Decoder::emit_audio (shared_ptr<AudioBuffers> audio)
+{
        Audio (audio);
+       _audio_frames_processed += audio->frames ();
 }
 
 /** Called by subclasses to tell the world that some video data is ready.
index 6cd7757b6562c05a36ee099f8b57b4a4769f5f39..9c7b2de3896b9e21a2476789c1ec584d60730977 100644 (file)
@@ -132,7 +132,7 @@ protected:
        bool _ignore_length;
 
 private:
-       void emit_audio (uint8_t* data, int size);
+       void emit_audio (boost::shared_ptr<AudioBuffers>);
        
        /** last video frame to be processed */
        int _video_frame_index;
@@ -140,7 +140,7 @@ private:
        std::list<boost::shared_ptr<FilterGraph> > _filter_graphs;
 
        DelayLine* _delay_line;
-       int _delay_in_bytes;
+       int _delay_in_frames;
 
        /* Number of audio frames that we have pushed to the encoder
           (at the DCP sample rate).
index c510fb4e3b3544f69994ccedeaf7433c1ed51dea..8aa43e293fee97a036c8df2c542c01493e23ffc1 100644 (file)
 #include <algorithm>
 #include <iostream>
 #include "delay_line.h"
+#include "util.h"
 
-using namespace std;
+using std::min;
+using boost::shared_ptr;
 
-/** Construct a DelayLine delaying by some number of bytes.
- *  @param d Number of bytes to delay by; +ve moves data later.
+/** @param channels Number of channels of audio.
+ *  @param frames Delay in frames, +ve to move audio later.
  */
-DelayLine::DelayLine (int d)
-       : _delay (d)
-       , _buffer (0)
-       , _negative_delay_remaining (0)
+DelayLine::DelayLine (int channels, int frames)
 {
-       if (d > 0) {
+       if (frames > 0) {
                /* We need a buffer to keep some data in */
-               _buffer = new uint8_t[d];
-               memset (_buffer, 0, d);
-       } else if (d < 0) {
+               _buffers.reset (new AudioBuffers (channels, frames));
+               _buffers->make_silent ();
+       } else if (frames < 0) {
                /* We can do -ve delays just by chopping off
                   the start, so no buffer needed.
                */
-               _negative_delay_remaining = -d;
+               _negative_delay_remaining = -frames;
        }
 }
 
 DelayLine::~DelayLine ()
 {
-       delete[] _buffer;
+
 }
 
-int
-DelayLine::feed (uint8_t* data, int size)
+void
+DelayLine::feed (shared_ptr<AudioBuffers> data)
 {
-       int available = size;
+       if (_buffers) {
+               /* We have some buffers, so we are moving the audio later */
 
-       if (_delay > 0) {
-               
                /* Copy the input data */
-               uint8_t input[size];
-               memcpy (input, data, size);
+               AudioBuffers input (*data.get ());
 
-               int to_do = size;
+               int to_do = data->frames ();
 
                /* Write some of our buffer to the output */
-               int const from_buffer = min (to_do, _delay);
-               memcpy (data, _buffer, from_buffer);
+               int const from_buffer = min (to_do, _buffers->frames());
+               data->copy_from (_buffers.get(), from_buffer, 0, 0);
                to_do -= from_buffer;
 
                /* Write some of the input to the output */
-               int const from_input = min (to_do, size);
-               memcpy (data + from_buffer, input, from_input);
+               int const from_input = to_do;
+               data->copy_from (&input, from_input, 0, from_buffer);
+
+               int const left_in_buffer = _buffers->frames() - from_buffer;
 
-               int const left_in_buffer = _delay - from_buffer;
-               
                /* Shuffle our buffer down */
-               memmove (_buffer, _buffer + from_buffer, left_in_buffer);
+               _buffers->move (from_buffer, 0, left_in_buffer);
 
                /* Copy remaining input data to our buffer */
-               memcpy (_buffer + left_in_buffer, input + from_input, size - from_input);
+               _buffers->copy_from (&input, input.frames() - from_input, from_input, left_in_buffer);
 
-       } else if (_delay < 0) {
+       } else {
 
                /* Chop the initial data off until _negative_delay_remaining
                   is zero, then just pass data.
                */
 
-               int const to_do = min (size, _negative_delay_remaining);
-               available = size - to_do;
-               memmove (data, data + to_do, available);
-               _negative_delay_remaining -= to_do;
-
+               int const to_do = min (data->frames(), _negative_delay_remaining);
+               if (to_do) {
+                       data->move (to_do, 0, data->frames() - to_do);
+                       data->set_frames (data->frames() - to_do);
+                       _negative_delay_remaining -= to_do;
+               }
        }
-
-       return available;
-}
-
-/** With -ve delays, the DelayLine will have data to give after
- *  all input data has been passed to ::feed().
- *  Call this method after passing all input data.
- *
- *  @param buffer Pointer to buffer of _delay bytes in length,
- *  which will be filled with remaining data.
- */
-void
-DelayLine::get_remaining (uint8_t* buffer)
-{
-       memset (buffer, 0, -_delay);
 }
index 377553de4035a1684711092b96f9e4272d97bb3b..e8d9560af599e5da366ed4735d17c6cf7ce6a235 100644 (file)
 
 */
 
-/** A class which can be fed a stream of bytes and which can
- *  delay them by a positive or negative amount.
- */
+#include <boost/shared_ptr.hpp>
+
+class AudioBuffers;
+
+/** A delay line for audio */
 class DelayLine
 {
 public:
-       DelayLine (int);
+       DelayLine (int channels, int frames);
        ~DelayLine ();
        
-       int feed (uint8_t *, int);
-       void get_remaining (uint8_t *);
+       void feed (boost::shared_ptr<AudioBuffers>);
 
 private:
-       int _delay; ///< delay in bytes, +ve to move data later
-       uint8_t* _buffer; ///< buffer for +ve delays, or 0
-       int _negative_delay_remaining; ///< number of bytes of negative delay that remain to emit
+       boost::shared_ptr<AudioBuffers> _buffers;
+       int _negative_delay_remaining; ///< number of frames of negative delay that remain to emit
 };
index d89ebd0d51cc9fc6cf916e66e924f961c548d292..26b2877f70f92544c38db219fd43304899a0be93 100644 (file)
@@ -726,6 +726,7 @@ get_optional_int (multimap<string, string> const & kv, string k)
 AudioBuffers::AudioBuffers (int channels, int frames)
        : _channels (channels)
        , _frames (frames)
+       , _allocated_frames (frames)
 {
        _data = new float*[_channels];
        for (int i = 0; i < _channels; ++i) {
@@ -733,6 +734,18 @@ AudioBuffers::AudioBuffers (int channels, int frames)
        }
 }
 
+AudioBuffers::AudioBuffers (AudioBuffers const & other)
+       : _channels (other._channels)
+       , _frames (other._frames)
+       , _allocated_frames (other._frames)
+{
+       _data = new float*[_channels];
+       for (int i = 0; i < _channels; ++i) {
+               _data[i] = new float[_frames];
+               memcpy (_data[i], other._data[i], _frames * sizeof (float));
+       }
+}
+
 AudioBuffers::~AudioBuffers ()
 {
        for (int i = 0; i < _channels; ++i) {
@@ -752,7 +765,7 @@ AudioBuffers::data (int c) const
 void
 AudioBuffers::set_frames (int f)
 {
-       assert (f <= _frames);
+       assert (f <= _allocated_frames);
        _frames = f;
 }
 
@@ -766,6 +779,24 @@ AudioBuffers::make_silent ()
        }
 }
 
+void
+AudioBuffers::copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset)
+{
+       assert (from->channels() == channels());
+
+       for (int i = 0; i < _channels; ++i) {
+               memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float));
+       }
+}
+
+void
+AudioBuffers::move (int from, int to, int frames)
+{
+       for (int i = 0; i < _channels; ++i) {
+               memmove (_data[i] + to, _data[i] + from, frames * sizeof(float));
+       }
+}
+
 void
 ensure_ui_thread ()
 {
index 26c6ed9faba6d940e32b279474435d4cd69d0aa3..f640122e2487bb46b5d2e50ebfa23dcf55b58d33 100644 (file)
@@ -218,6 +218,7 @@ class AudioBuffers
 {
 public:
        AudioBuffers (int channels, int frames);
+       AudioBuffers (AudioBuffers const &);
        ~AudioBuffers ();
 
        float** data () const {
@@ -226,6 +227,10 @@ public:
        
        float* data (int) const;
 
+       int channels () const {
+               return _channels;
+       }
+
        int frames () const {
                return _frames;
        }
@@ -234,12 +239,13 @@ public:
 
        void make_silent ();
 
+       void copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset);
+       void move (int from, int to, int frames);
+
 private:
-       /* no copy construction */
-       AudioBuffers (AudioBuffers const &);
-       
        int _channels;
        int _frames;
+       int _allocated_frames;
        float** _data;
 };
 
index af5c1f501902d1b0049b19e8efad3b3e425c35d0..2b6792a67571eb334c6a4696d5b85c953c920d49 100644 (file)
@@ -176,10 +176,10 @@ BOOST_AUTO_TEST_CASE (dvd_test)
 }
 
 void
-do_positive_delay_line_test (int delay_length, int block_length)
+do_positive_delay_line_test (int delay_length, int data_length)
 {
-       DelayLine d (delay_length);
-       uint8_t data[block_length];
+       DelayLine d (6, delay_length);
+       shared_ptr<AudioBuffers> data (new AudioBuffers (6, data_length));
 
        int in = 0;
        int out = 0;
@@ -187,64 +187,66 @@ do_positive_delay_line_test (int delay_length, int block_length)
        int zeros = 0;
        
        for (int i = 0; i < 64; ++i) {
-               for (int j = 0; j < block_length; ++j) {
-                       data[j] = in;
-                       ++in;
+               for (int j = 0; j < data_length; ++j) {
+                       for (int c = 0; c < 6; ++c ) {
+                               data->data(c)[j] = in;
+                               ++in;
+                       }
                }
 
-               int const a = d.feed (data, block_length);
-               returned += a;
+               d.feed (data);
+               returned += data->frames ();
 
-               for (int j = 0; j < a; ++j) {
+               for (int j = 0; j < data->frames(); ++j) {
                        if (zeros < delay_length) {
-                               BOOST_CHECK_EQUAL (data[j], 0);
+                               for (int c = 0; c < 6; ++c) {
+                                       BOOST_CHECK_EQUAL (data->data(c)[j], 0);
+                               }
                                ++zeros;
                        } else {
-                               BOOST_CHECK_EQUAL (data[j], out & 0xff);
-                               ++out;
+                               for (int c = 0; c < 6; ++c) {
+                                       BOOST_CHECK_EQUAL (data->data(c)[j], out);
+                                       ++out;
+                               }
                        }
                }
        }
 
-       BOOST_CHECK_EQUAL (returned, 64 * block_length);
+       BOOST_CHECK_EQUAL (returned, 64 * data_length);
 }
 
 void
-do_negative_delay_line_test (int delay_length, int block_length)
+do_negative_delay_line_test (int delay_length, int data_length)
 {
-       DelayLine d (delay_length);
-       uint8_t data[block_length];
+       DelayLine d (6, delay_length);
+       shared_ptr<AudioBuffers> data (new AudioBuffers (6, data_length));
 
        int in = 0;
-       int out = -delay_length;
+       int out = -delay_length * 6;
        int returned = 0;
        
        for (int i = 0; i < 256; ++i) {
-               for (int j = 0; j < block_length; ++j) {
-                       data[j] = in;
-                       ++in;
+               data->set_frames (data_length);
+               for (int j = 0; j < data_length; ++j) {
+                       for (int c = 0; c < 6; ++c) {
+                               data->data(c)[j] = in;
+                               ++in;
+                       }
                }
 
-               int const a = d.feed (data, block_length);
-               returned += a;
+               d.feed (data);
+               returned += data->frames ();
 
-               for (int j = 0; j < a; ++j) {
-                       BOOST_CHECK_EQUAL (data[j], out & 0xff);
-                       ++out;
+               for (int j = 0; j < data->frames(); ++j) {
+                       for (int c = 0; c < 6; ++c) {
+                               BOOST_CHECK_EQUAL (data->data(c)[j], out);
+                               ++out;
+                       }
                }
        }
 
-       uint8_t remainder[-delay_length];
-       d.get_remaining (remainder);
        returned += -delay_length;
-
-       for (int i = 0; i < -delay_length; ++i) {
-               BOOST_CHECK_EQUAL (remainder[i], 0);
-               ++out;
-       }
-
-       BOOST_CHECK_EQUAL (returned, 256 * block_length);
-       
+       BOOST_CHECK_EQUAL (returned, 256 * data_length);
 }
 
 BOOST_AUTO_TEST_CASE (delay_line_test)