From: Carl Hetherington Date: Tue, 19 Apr 2022 12:03:20 +0000 (+0200) Subject: Use std::vector in AudioBuffers (#2236). X-Git-Tag: v2.16.10~56 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=341ba1115b6285fec998901e50f9afb48bcaeeb6 Use std::vector in AudioBuffers (#2236). --- diff --git a/src/lib/audio_analyser.cc b/src/lib/audio_analyser.cc index 53d764a9b..755d6e9b0 100644 --- a/src/lib/audio_analyser.cc +++ b/src/lib/audio_analyser.cc @@ -134,7 +134,7 @@ AudioAnalyser::~AudioAnalyser () void -AudioAnalyser::analyse (shared_ptr b, DCPTime time) +AudioAnalyser::analyse (shared_ptr b, DCPTime time) { LOG_DEBUG_AUDIO_ANALYSIS("Received %1 frames at %2", b->frames(), to_string(time)); DCPOMATIC_ASSERT (time >= _start); @@ -150,7 +150,7 @@ AudioAnalyser::analyse (shared_ptr b, DCPTime time) vector interleaved(frames * channels); for (int j = 0; j < channels; ++j) { - float* data = b->data(j); + float const* data = b->data(j); for (int i = 0; i < frames; ++i) { float s = data[i]; diff --git a/src/lib/audio_analyser.h b/src/lib/audio_analyser.h index 14c744285..01eec36b1 100644 --- a/src/lib/audio_analyser.h +++ b/src/lib/audio_analyser.h @@ -45,7 +45,7 @@ public: AudioAnalyser (AudioAnalyser const&) = delete; AudioAnalyser& operator= (AudioAnalyser const&) = delete; - void analyse (std::shared_ptr, dcpomatic::DCPTime time); + void analyse (std::shared_ptr, dcpomatic::DCPTime time); dcpomatic::DCPTime start () const { return _start; diff --git a/src/lib/audio_buffers.cc b/src/lib/audio_buffers.cc index 51f6c6c33..119a499b4 100644 --- a/src/lib/audio_buffers.cc +++ b/src/lib/audio_buffers.cc @@ -22,22 +22,18 @@ #include "audio_buffers.h" #include "dcpomatic_assert.h" #include "maths_util.h" +#include "scope_guard.h" #include #include #include -#include -using std::bad_alloc; using std::shared_ptr; using std::make_shared; -/** Construct an AudioBuffers. Audio data is undefined after this constructor. - * @param channels Number of channels. - * @param frames Number of frames to reserve space for. - */ -AudioBuffers::AudioBuffers (int channels, int32_t frames) +/** Construct a silent AudioBuffers */ +AudioBuffers::AudioBuffers (int channels, int frames) { allocate (channels, frames); } @@ -48,21 +44,21 @@ AudioBuffers::AudioBuffers (int channels, int32_t frames) */ AudioBuffers::AudioBuffers (AudioBuffers const & other) { - allocate (other._channels, other._frames); - copy_from (&other, other._frames, 0, 0); + allocate (other.channels(), other.frames()); + copy_from (&other, other.frames(), 0, 0); } AudioBuffers::AudioBuffers (std::shared_ptr other) { - allocate (other->_channels, other->_frames); - copy_from (other.get(), other->_frames, 0, 0); + allocate (other->channels(), other->frames()); + copy_from (other.get(), other->frames(), 0, 0); } -AudioBuffers::AudioBuffers (std::shared_ptr other, int32_t frames_to_copy, int32_t read_offset) +AudioBuffers::AudioBuffers (std::shared_ptr other, int frames_to_copy, int read_offset) { - allocate (other->_channels, frames_to_copy); + allocate (other->channels(), frames_to_copy); copy_from (other.get(), frames_to_copy, read_offset, 0); } @@ -74,81 +70,55 @@ AudioBuffers::operator= (AudioBuffers const & other) return *this; } - deallocate (); - allocate (other._channels, other._frames); - copy_from (&other, other._frames, 0, 0); + allocate (other.channels(), other.frames()); + copy_from (&other, other.frames(), 0, 0); return *this; } -/** AudioBuffers destructor */ -AudioBuffers::~AudioBuffers () -{ - deallocate (); -} - - void -AudioBuffers::allocate (int channels, int32_t frames) +AudioBuffers::allocate (int channels, int frames) { DCPOMATIC_ASSERT (frames >= 0); - DCPOMATIC_ASSERT (channels >= 0); + DCPOMATIC_ASSERT (channels > 0); - _channels = channels; - _frames = frames; - _allocated_frames = frames; + ScopeGuard sg = [this]() { update_data_pointers(); }; - _data = static_cast (malloc(_channels * sizeof(float *))); - if (!_data) { - throw bad_alloc (); - } - - for (int i = 0; i < _channels; ++i) { - _data[i] = static_cast (malloc(frames * sizeof(float))); - if (!_data[i]) { - throw bad_alloc (); - } + _data.resize(channels); + for (int channel = 0; channel < channels; ++channel) { + _data[channel].resize(frames); } } -void -AudioBuffers::deallocate () +/** @param channel Channel index. + * @return Buffer for this channel. + */ +float* +AudioBuffers::data (int channel) { - for (int i = 0; i < _channels; ++i) { - free (_data[i]); - } - - free (_data); + DCPOMATIC_ASSERT (channel >= 0 && channel < channels()); + return _data[channel].data(); } /** @param channel Channel index. * @return Buffer for this channel. */ -float* +float const* AudioBuffers::data (int channel) const { - DCPOMATIC_ASSERT (channel >= 0 && channel < _channels); - return _data[channel]; + DCPOMATIC_ASSERT (channel >= 0 && channel < channels()); + return _data[channel].data(); } -/** Set the number of frames that these AudioBuffers will report themselves - * as having. If we reduce the number of frames, the `lost' frames will - * be silenced. - * @param f Frames; must be less than or equal to the number of allocated frames. - */ +/** Set the number of frames in these AudioBuffers */ void -AudioBuffers::set_frames (int32_t frames) +AudioBuffers::set_frames (int frames) { - DCPOMATIC_ASSERT (frames <= _allocated_frames); - - if (frames < _frames) { - make_silent (frames, _frames - frames); - } - _frames = frames; + allocate(_data.size(), frames); } @@ -156,7 +126,7 @@ AudioBuffers::set_frames (int32_t frames) void AudioBuffers::make_silent () { - for (int channel = 0; channel < _channels; ++channel) { + for (int channel = 0; channel < channels(); ++channel) { make_silent (channel); } } @@ -166,29 +136,28 @@ AudioBuffers::make_silent () void AudioBuffers::make_silent (int channel) { - DCPOMATIC_ASSERT (channel >= 0 && channel < _channels); + DCPOMATIC_ASSERT (channel >= 0 && channel < channels()); /* This isn't really allowed, as all-bits-0 is not guaranteed to mean a 0 float, but it seems that we can get away with it. */ - memset (_data[channel], 0, _frames * sizeof(float)); + memset (data(channel), 0, frames() * sizeof(float)); } -/** Make some frames. +/** Make some frames silent. * @param from Start frame. - * @param frames Number of frames to silence. */ void -AudioBuffers::make_silent (int32_t from, int32_t frames) +AudioBuffers::make_silent (int from, int frames_to_silence) { - DCPOMATIC_ASSERT ((from + frames) <= _allocated_frames); + DCPOMATIC_ASSERT ((from + frames_to_silence) <= frames()); - for (int channel = 0; channel < _channels; ++channel) { + for (int channel = 0; channel < channels(); ++channel) { /* This isn't really allowed, as all-bits-0 is not guaranteed to mean a 0 float, but it seems that we can get away with it. */ - memset (_data[channel] + from, 0, frames * sizeof(float)); + memset (data(channel) + from, 0, frames_to_silence * sizeof(float)); } } @@ -200,7 +169,7 @@ AudioBuffers::make_silent (int32_t from, int32_t frames) * @param write_offset Offset to write to in `to'. */ void -AudioBuffers::copy_from (AudioBuffers const * from, int32_t frames_to_copy, int32_t read_offset, int32_t write_offset) +AudioBuffers::copy_from (AudioBuffers const * from, int frames_to_copy, int read_offset, int write_offset) { if (frames_to_copy == 0) { /* Prevent the asserts from firing if there is nothing to do */ @@ -209,38 +178,38 @@ AudioBuffers::copy_from (AudioBuffers const * from, int32_t frames_to_copy, int3 DCPOMATIC_ASSERT (from); DCPOMATIC_ASSERT (from->channels() == channels()); - DCPOMATIC_ASSERT (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames); - DCPOMATIC_ASSERT (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames); + DCPOMATIC_ASSERT (read_offset >= 0 && (read_offset + frames_to_copy) <= from->frames()); + DCPOMATIC_ASSERT (write_offset >= 0 && (write_offset + frames_to_copy) <= frames()); - for (int i = 0; i < _channels; ++i) { - memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float)); + for (int channel = 0; channel < channels(); ++channel) { + memcpy (data(channel) + write_offset, from->data(channel) + read_offset, frames_to_copy * sizeof(float)); } } /** Move audio data around. + * @param frames_to_move Number of frames to move. * @param from Offset to move from. * @param to Offset to move to. - * @param frames Number of frames to move. */ void -AudioBuffers::move (int32_t frames, int32_t from, int32_t to) +AudioBuffers::move (int frames_to_move, int from, int to) { - if (frames == 0) { + if (frames_to_move == 0) { return; } DCPOMATIC_ASSERT (from >= 0); - DCPOMATIC_ASSERT (from < _frames); + DCPOMATIC_ASSERT (from < frames()); DCPOMATIC_ASSERT (to >= 0); - DCPOMATIC_ASSERT (to < _frames); - DCPOMATIC_ASSERT (frames > 0); - DCPOMATIC_ASSERT (frames <= _frames); - DCPOMATIC_ASSERT ((from + frames) <= _frames); - DCPOMATIC_ASSERT ((to + frames) <= _allocated_frames); - - for (int i = 0; i < _channels; ++i) { - memmove (_data[i] + to, _data[i] + from, frames * sizeof(float)); + DCPOMATIC_ASSERT (to < frames()); + DCPOMATIC_ASSERT (frames_to_move > 0); + DCPOMATIC_ASSERT (frames_to_move <= frames()); + DCPOMATIC_ASSERT ((from + frames_to_move) <= frames()); + DCPOMATIC_ASSERT ((to + frames_to_move) <= frames()); + + for (int channel = 0; channel < channels(); ++channel) { + memmove (data(channel) + to, data(channel) + from, frames_to_move * sizeof(float)); } } @@ -256,10 +225,10 @@ AudioBuffers::accumulate_channel (AudioBuffers const * from, int from_channel, i { int const N = frames (); DCPOMATIC_ASSERT (from->frames() == N); - DCPOMATIC_ASSERT (to_channel <= _channels); + DCPOMATIC_ASSERT (to_channel <= channels()); auto s = from->data (from_channel); - auto d = _data[to_channel]; + auto d = data(to_channel); for (int i = 0; i < N; ++i) { *d++ += (*s++) * gain; @@ -267,42 +236,6 @@ AudioBuffers::accumulate_channel (AudioBuffers const * from, int from_channel, i } -/** Ensure we have space for at least a certain number of frames. If we extend - * the buffers, fill the new space with silence. - */ -void -AudioBuffers::ensure_size (int32_t frames) -{ - if (_allocated_frames >= frames) { - return; - } - - /* Round up frames to the next power of 2 to reduce the number - of realloc()s that are necessary. - */ - frames--; - frames |= frames >> 1; - frames |= frames >> 2; - frames |= frames >> 4; - frames |= frames >> 8; - frames |= frames >> 16; - frames++; - - for (int i = 0; i < _channels; ++i) { - _data[i] = static_cast (realloc(_data[i], frames * sizeof(float))); - if (!_data[i]) { - throw bad_alloc (); - } - } - - auto const old_allocated = _allocated_frames; - _allocated_frames = frames; - if (old_allocated < _allocated_frames) { - make_silent (old_allocated, _allocated_frames - old_allocated); - } -} - - /** Mix some other buffers with these ones. The AudioBuffers must have the same number of channels. * @param from Audio buffers to get data from. * @param frames Number of frames to mix. @@ -310,14 +243,14 @@ AudioBuffers::ensure_size (int32_t frames) * @param write_offset Offset within this to mix into. */ void -AudioBuffers::accumulate_frames (AudioBuffers const * from, int32_t frames, int32_t read_offset, int32_t write_offset) +AudioBuffers::accumulate_frames (AudioBuffers const * from, int frames, int read_offset, int write_offset) { - DCPOMATIC_ASSERT (_channels == from->channels ()); + DCPOMATIC_ASSERT (channels() == from->channels()); DCPOMATIC_ASSERT (read_offset >= 0); DCPOMATIC_ASSERT (write_offset >= 0); auto from_data = from->data (); - for (int i = 0; i < _channels; ++i) { + for (int i = 0; i < channels(); ++i) { for (int j = 0; j < frames; ++j) { _data[i][j + write_offset] += from_data[i][j + read_offset]; } @@ -331,14 +264,15 @@ AudioBuffers::apply_gain (float dB) { auto const linear = db_to_linear (dB); - for (int i = 0; i < _channels; ++i) { - for (int j = 0; j < _frames; ++j) { + for (int i = 0; i < channels(); ++i) { + for (int j = 0; j < frames(); ++j) { _data[i][j] *= linear; } } } +/** @return AudioBuffers object containing only the given channel from this AudioBuffers */ shared_ptr AudioBuffers::channel (int channel) const { @@ -376,17 +310,28 @@ void AudioBuffers::append (shared_ptr other) { DCPOMATIC_ASSERT (channels() == other->channels()); - ensure_size (_frames + other->frames()); - copy_from (other.get(), other->frames(), 0, _frames); - _frames += other->frames(); + auto old_frames = frames(); + set_frames(old_frames + other->frames()); + copy_from (other.get(), other->frames(), 0, old_frames); } /** Remove some frames from the start of these AudioBuffers */ void -AudioBuffers::trim_start (int32_t frames) +AudioBuffers::trim_start (int frames_to_trim) { - DCPOMATIC_ASSERT (frames <= _frames); - move (_frames - frames, frames, 0); - set_frames (_frames - frames); + DCPOMATIC_ASSERT (frames_to_trim <= frames()); + move (frames() - frames_to_trim, frames_to_trim, 0); + set_frames (frames() - frames_to_trim); } + + +void +AudioBuffers::update_data_pointers () +{ + _data_pointers.resize (channels()); + for (int i = 0; i < channels(); ++i) { + _data_pointers[i] = _data[i].data(); + } +} + diff --git a/src/lib/audio_buffers.h b/src/lib/audio_buffers.h index 1f0f5f28a..4db0fa255 100644 --- a/src/lib/audio_buffers.h +++ b/src/lib/audio_buffers.h @@ -28,75 +28,65 @@ #define DCPOMATIC_AUDIO_BUFFERS_H -#include #include +#include /** @class AudioBuffers * @brief A class to hold multi-channel audio data in float format. - * - * The use of int32_t for frame counts in this class is due to the - * round-up to the next power-of-2 code in ensure_size(); if that - * were changed the frame count could use any integer type. */ class AudioBuffers { public: - AudioBuffers (int channels, int32_t frames); + AudioBuffers (int channels, int frames); AudioBuffers (AudioBuffers const &); explicit AudioBuffers (std::shared_ptr); - AudioBuffers (std::shared_ptr other, int32_t frames_to_copy, int32_t read_offset); - ~AudioBuffers (); + AudioBuffers (std::shared_ptr other, int frames_to_copy, int read_offset); AudioBuffers & operator= (AudioBuffers const &); std::shared_ptr clone () const; std::shared_ptr channel (int) const; - void ensure_size (int32_t); - - float** data () const { - return _data; + float* const* data () const { + return _data_pointers.data(); } - float* data (int) const; + float const* data (int) const; + float* data (int); int channels () const { - return _channels; + return _data.size(); } int frames () const { - return _frames; + return _data[0].size(); } - void set_frames (int32_t f); + void set_frames (int f); void make_silent (); void make_silent (int channel); - void make_silent (int32_t from, int32_t frames); + void make_silent (int from, int frames); void apply_gain (float); - void copy_from (AudioBuffers const * from, int32_t frames_to_copy, int32_t read_offset, int32_t write_offset); + void copy_from (AudioBuffers const * from, int frames_to_copy, int read_offset, int write_offset); void copy_channel_from (AudioBuffers const * from, int from_channel, int to_channel); - void move (int32_t frames, int32_t from, int32_t to); + void move (int frames, int from, int to); void accumulate_channel (AudioBuffers const * from, int from_channel, int to_channel, float gain = 1); - void accumulate_frames (AudioBuffers const * from, int32_t frames, int32_t read_offset, int32_t write_offset); + void accumulate_frames (AudioBuffers const * from, int frames, int read_offset, int write_offset); void append (std::shared_ptr other); - void trim_start (int32_t frames); + void trim_start (int frames); private: - void allocate (int channels, int32_t frames); - void deallocate (); - - /** Number of channels */ - int _channels; - /** Number of frames (where a frame is one sample across all channels) */ - int32_t _frames; - /** Number of frames that _data can hold */ - int32_t _allocated_frames; + void allocate (int channels, int frames); + void update_data_pointers (); + /** Audio data (so that, e.g. _data[2][6] is channel 2, sample 6) */ - float** _data; + std::vector> _data; + /** Pointers to the data of each vector in _data */ + std::vector _data_pointers; }; diff --git a/src/lib/audio_filter_graph.cc b/src/lib/audio_filter_graph.cc index 95f6730cf..d9e4e244f 100644 --- a/src/lib/audio_filter_graph.cc +++ b/src/lib/audio_filter_graph.cc @@ -106,7 +106,7 @@ AudioFilterGraph::sink_name () const } void -AudioFilterGraph::process (shared_ptr buffers) +AudioFilterGraph::process (shared_ptr buffers) { DCPOMATIC_ASSERT (buffers->frames() > 0); int const process_channels = av_get_channel_layout_nb_channels (_channel_layout); diff --git a/src/lib/audio_filter_graph.h b/src/lib/audio_filter_graph.h index af438a7e7..e749d0ec9 100644 --- a/src/lib/audio_filter_graph.h +++ b/src/lib/audio_filter_graph.h @@ -31,7 +31,7 @@ public: AudioFilterGraph (int sample_rate, int channels); ~AudioFilterGraph (); - void process (std::shared_ptr audio); + void process (std::shared_ptr audio); protected: std::string src_parameters () const override; diff --git a/src/lib/audio_ring_buffers.cc b/src/lib/audio_ring_buffers.cc index 1a216ab33..b55631a1c 100644 --- a/src/lib/audio_ring_buffers.cc +++ b/src/lib/audio_ring_buffers.cc @@ -85,7 +85,7 @@ AudioRingBuffers::get (float* out, int channels, int frames) } int const to_do = min (frames, front.first->frames() - _used_in_head); - float** p = front.first->data(); + float* const* p = front.first->data(); int const c = min (front.first->channels(), channels); for (int i = 0; i < to_do; ++i) { for (int j = 0; j < c; ++j) { diff --git a/src/lib/ffmpeg_file_encoder.cc b/src/lib/ffmpeg_file_encoder.cc index 57103abc7..62242d65c 100644 --- a/src/lib/ffmpeg_file_encoder.cc +++ b/src/lib/ffmpeg_file_encoder.cc @@ -127,7 +127,7 @@ public: return false; } - void write (int size, int channel_offset, int channels, float** data, int64_t sample_offset) + void write (int size, int channel_offset, int channels, float* const* data, int64_t sample_offset) { DCPOMATIC_ASSERT (size); diff --git a/src/lib/resampler.cc b/src/lib/resampler.cc index 056b2e1ee..4447ccf0d 100644 --- a/src/lib/resampler.cc +++ b/src/lib/resampler.cc @@ -131,7 +131,6 @@ Resampler::run (shared_ptr in) break; } - resampled->ensure_size (out_offset + data.output_frames_gen); resampled->set_frames (out_offset + data.output_frames_gen); { @@ -176,7 +175,7 @@ Resampler::flush () throw EncodeError (String::compose(N_("could not run sample-rate converter (%1)"), src_strerror(r))); } - out->ensure_size (out_offset + data.output_frames_gen); + out->set_frames (out_offset + data.output_frames_gen); auto p = data.data_out; auto q = out->data (); @@ -187,7 +186,6 @@ Resampler::flush () } out_offset += data.output_frames_gen; - out->set_frames (out_offset); return out; } diff --git a/test/audio_buffers_test.cc b/test/audio_buffers_test.cc index 921592f55..0abd73080 100644 --- a/test/audio_buffers_test.cc +++ b/test/audio_buffers_test.cc @@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE (audio_buffers_extend_test) random_fill (buffers); /* Extend */ - buffers.ensure_size (299); + buffers.set_frames (299); srand (1); random_check (buffers, 0, 150); @@ -303,3 +303,44 @@ BOOST_AUTO_TEST_CASE (audio_buffers_accumulate_frames) } } } + + +BOOST_AUTO_TEST_CASE (audio_buffers_data) +{ + AudioBuffers a (94, 512); + + for (int i = 0; i < 94; ++i) { + BOOST_CHECK_EQUAL (a.data()[i], a.data(i)); + } + + a.set_frames (2048); + + for (int i = 0; i < 94; ++i) { + BOOST_CHECK_EQUAL (a.data()[i], a.data(i)); + } +} + + +BOOST_AUTO_TEST_CASE (audio_buffers_trim_start) +{ + AudioBuffers a (13, 999); + + srand (55); + random_fill (a); + + a.trim_start (101); + + srand (55); + + /* Burn the first 101 numbers in the sequence */ + for (int i = 0; i < 101 * 13; ++i) { + random_float (); + } + + for (int i = 0; i < (999 - 101); ++i) { + for (int j = 0; j < 13; ++j) { + BOOST_CHECK_CLOSE (a.data(j)[i], random_float(), tolerance); + } + } +} +