*/
+#ifndef DVDOMATIC_AUDIO_BUFFERS_H
+#define DVDOMATIC_AUDIO_BUFFERS_H
+
#include <boost/shared_ptr.hpp>
/** @class AudioBuffers
/** Audio data (so that, e.g. _data[2][6] is channel 2, sample 6) */
float** _data;
};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "audio_buffers.h"
+
+template <class T, class F>
+class AudioMerger
+{
+public:
+ AudioMerger (int channels, boost::function<F (T)> t_to_f, boost::function<T (F)> f_to_t)
+ : _buffers (new AudioBuffers (channels, 0))
+ , _next_emission (0)
+ , _t_to_f (t_to_f)
+ , _f_to_t (f_to_t)
+ {}
+
+ void push (boost::shared_ptr<const AudioBuffers> audio, T time)
+ {
+ if (time > _next_emission) {
+ /* We can emit some audio from our buffer; this is how many frames
+ we are going to emit.
+ */
+ F const to_emit = _t_to_f (time - _next_emission);
+ boost::shared_ptr<AudioBuffers> emit (new AudioBuffers (_buffers->channels(), to_emit));
+
+ /* And this is how many we will get from our buffer */
+ F const to_emit_from_buffers = min (to_emit, _buffers->frames ());
+
+ /* Copy the data that we have to the back end of `emit' */
+ emit->copy_from (_buffers.get(), to_emit_from_buffers, 0, to_emit - to_emit_from_buffers);
+
+ /* Silence any gap at the start */
+ emit->make_silent (0, to_emit - to_emit_from_buffers);
+
+ /* Emit that */
+ Audio (emit, _next_emission);
+
+ _next_emission += _f_to_t (to_emit);
+
+ /* And remove the data we've emitted from our buffers */
+ if (_buffers->frames() > to_emit_from_buffers) {
+ _buffers->move (to_emit_from_buffers, 0, _buffers->frames() - to_emit_from_buffers);
+ }
+ _buffers->set_frames (_buffers->frames() - to_emit_from_buffers);
+ }
+
+ /* Now accumulate the new audio into our buffers */
+ F frame = _t_to_f (time);
+ F after = max (_buffers->frames(), frame + audio->frames() - _t_to_f (_next_emission));
+ _buffers->ensure_size (after);
+ _buffers->accumulate_frames (audio.get(), 0, frame - _t_to_f (_next_emission), audio->frames ());
+ _buffers->set_frames (after);
+ }
+
+ F min (F a, int b)
+ {
+ if (a < b) {
+ return a;
+ }
+
+ return b;
+ }
+
+ F max (int a, F b)
+ {
+ if (a > b) {
+ return a;
+ }
+
+ return b;
+ }
+
+ void flush ()
+ {
+ if (_buffers->frames() > 0) {
+ Audio (_buffers, _next_emission);
+ }
+ }
+
+ boost::signals2::signal<void (boost::shared_ptr<const AudioBuffers>, T)> Audio;
+
+private:
+ boost::shared_ptr<AudioBuffers> _buffers;
+ T _next_emission;
+ boost::function<F (T)> _t_to_f;
+ boost::function<T (F)> _f_to_t;
+};
, _have_valid_pieces (false)
, _video_position (0)
, _audio_position (0)
- , _audio_buffers (f->audio_channels(), 0)
+ , _audio_merger (f->audio_channels(), bind (&Film::time_to_audio_frames, f.get(), _1), bind (&Film::audio_frames_to_time, f.get(), _1))
{
_playlist->Changed.connect (bind (&Player::playlist_changed, this));
_playlist->ContentChanged.connect (bind (&Player::content_changed, this, _1, _2, _3));
_film->Changed.connect (bind (&Player::film_changed, this, _1));
+ _audio_merger.Audio.connect (bind (&Player::merger_process_audio, this, _1, _2));
set_video_container_size (_film->container()->size (_film->full_frame ()));
}
time = 0;
}
- /* The time of this audio may indicate that some of our buffered audio is not going to
- be added to any more, so it can be emitted.
- */
-
- if (time > _audio_position) {
- /* We can emit some audio from our buffers; this is how many frames */
- OutputAudioFrame const N = _film->time_to_audio_frames (time - _audio_position);
- if (N > _audio_buffers.frames()) {
- /* We need some extra silence before whatever is in the buffers */
- _audio_buffers.ensure_size (N);
- _audio_buffers.move (0, N - _audio_buffers.frames(), _audio_buffers.frames ());
- _audio_buffers.make_silent (0, _audio_buffers.frames());
- _audio_buffers.set_frames (N);
- }
- assert (N <= _audio_buffers.frames());
-
- /* XXX: not convinced that a copy is necessary here */
- shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), N));
- emit->copy_from (&_audio_buffers, N, 0, 0);
- Audio (emit, _audio_position);
-
- _audio_position = piece->audio_position = _audio_position + _film->audio_frames_to_time (N);
-
- /* And remove it from our buffers */
- if (_audio_buffers.frames() > N) {
- _audio_buffers.move (N, 0, _audio_buffers.frames() - N);
- }
- _audio_buffers.set_frames (_audio_buffers.frames() - N);
- }
+ _audio_merger.push (audio, time);
+}
- /* Now accumulate the new audio into our buffers */
- _audio_buffers.ensure_size (_audio_buffers.frames() + audio->frames());
- _audio_buffers.accumulate_frames (audio.get(), 0, 0, audio->frames ());
- _audio_buffers.set_frames (_audio_buffers.frames() + audio->frames());
+void
+Player::merger_process_audio (shared_ptr<const AudioBuffers> audio, Time time)
+{
+ Audio (audio, time);
+ _audio_position += _film->audio_frames_to_time (audio->frames ());
}
void
Player::flush ()
{
- if (_audio_buffers.frames() > 0) {
- shared_ptr<AudioBuffers> emit (new AudioBuffers (_audio_buffers.channels(), _audio_buffers.frames()));
- emit->copy_from (&_audio_buffers, _audio_buffers.frames(), 0, 0);
- Audio (emit, _audio_position);
- _audio_position += _film->audio_frames_to_time (_audio_buffers.frames ());
- _audio_buffers.set_frames (0);
- }
+ _audio_merger.flush ();
while (_video_position < _audio_position) {
emit_black ();
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "playlist.h"
-#include "audio_buffers.h"
#include "content.h"
#include "film.h"
#include "rect.h"
+#include "audio_merger.h"
+#include "audio_content.h"
class Job;
class Film;
boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>, bool);
void film_changed (Film::Property);
void update_subtitle ();
+ void merger_process_audio (boost::shared_ptr<const AudioBuffers>, Time);
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
/** The time after the last audio that we emitted */
Time _audio_position;
- AudioBuffers _audio_buffers;
+ AudioMerger<Time, AudioContent::Frame> _audio_merger;
libdcp::Size _video_container_size;
boost::shared_ptr<Image> _black_frame;
--- /dev/null
+/*
+ Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/test/unit_test.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/signals2.hpp>
+#include "lib/audio_merger.h"
+#include "lib/audio_buffers.h"
+
+using boost::shared_ptr;
+using boost::bind;
+
+static shared_ptr<const AudioBuffers> last_audio;
+static int last_time;
+
+static int
+pass_through (int x)
+{
+ return x;
+}
+
+static void
+process_audio (shared_ptr<const AudioBuffers> audio, int time)
+{
+ last_audio = audio;
+ last_time = time;
+}
+
+static void
+reset ()
+{
+ last_audio.reset ();
+ last_time = 0;
+}
+
+BOOST_AUTO_TEST_CASE (audio_merger_test1)
+{
+ AudioMerger<int, int> merger (1, bind (&pass_through, _1), boost::bind (&pass_through, _1));
+ merger.Audio.connect (bind (&process_audio, _1, _2));
+
+ reset ();
+
+ /* Push 64 samples, 0 -> 63 at time 0 */
+ shared_ptr<AudioBuffers> buffers (new AudioBuffers (1, 64));
+ for (int i = 0; i < 64; ++i) {
+ buffers->data()[0][i] = i;
+ }
+ merger.push (buffers, 0);
+
+ /* That should not have caused an emission */
+ BOOST_CHECK_EQUAL (last_audio, shared_ptr<const AudioBuffers> ());
+ BOOST_CHECK_EQUAL (last_time, 0);
+
+ /* Push 64 samples, 0 -> 63 at time 22 */
+ merger.push (buffers, 22);
+
+ /* That should have caused an emission of 22 samples at 0 */
+ BOOST_CHECK (last_audio != shared_ptr<const AudioBuffers> ());
+ BOOST_CHECK_EQUAL (last_audio->frames(), 22);
+ BOOST_CHECK_EQUAL (last_time, 0);
+
+ /* And they should be a staircase */
+ for (int i = 0; i < 22; ++i) {
+ BOOST_CHECK_EQUAL (last_audio->data()[0][i], i);
+ }
+
+ reset ();
+ merger.flush ();
+
+ /* That flush should give us 64 samples at 22 */
+ BOOST_CHECK_EQUAL (last_audio->frames(), 64);
+ BOOST_CHECK_EQUAL (last_time, 22);
+
+ /* Check the sample values */
+ for (int i = 0; i < 64; ++i) {
+ int correct = i;
+ if (i < (64 - 22)) {
+ correct += i + 22;
+ }
+ BOOST_CHECK_EQUAL (last_audio->data()[0][i], correct);
+ }
+}
+
+BOOST_AUTO_TEST_CASE (audio_merger_test2)
+{
+ AudioMerger<int, int> merger (1, bind (&pass_through, _1), boost::bind (&pass_through, _1));
+ merger.Audio.connect (bind (&process_audio, _1, _2));
+
+ reset ();
+
+ /* Push 64 samples, 0 -> 63 at time 9 */
+ shared_ptr<AudioBuffers> buffers (new AudioBuffers (1, 64));
+ for (int i = 0; i < 64; ++i) {
+ buffers->data()[0][i] = i;
+ }
+ merger.push (buffers, 9);
+
+ /* That flush should give us 9 samples at 0 */
+ BOOST_CHECK_EQUAL (last_audio->frames(), 9);
+ BOOST_CHECK_EQUAL (last_time, 0);
+
+ for (int i = 0; i < 9; ++i) {
+ BOOST_CHECK_EQUAL (last_audio->data()[0][i], 0);
+ }
+
+ reset ();
+ merger.flush ();
+
+ /* That flush should give us 64 samples at 9 */
+ BOOST_CHECK_EQUAL (last_audio->frames(), 64);
+ BOOST_CHECK_EQUAL (last_time, 9);
+
+ /* Check the sample values */
+ for (int i = 0; i < 64; ++i) {
+ BOOST_CHECK_EQUAL (last_audio->data()[0][i], i);
+ }
+}
obj.use = 'libdcpomatic'
obj.source = """
test.cc
+ silence_padding_test.cc
+ audio_merger_test.cc
resampler_test.cc
ffmpeg_audio_test.cc
threed_test.cc
play_test.cc
frame_rate_test.cc
- silence_padding_test.cc
audio_delay_test.cc
ffmpeg_pts_offset.cc
ffmpeg_examiner_test.cc