*/
void
-AudioBuffers::move (int32_t from, int32_t to, int32_t frames)
+AudioBuffers::move (int32_t frames, int32_t from, int32_t to)
{
if (frames == 0) {
return;
}
void
-AudioBuffers::accumulate_frames (AudioBuffers const * from, int32_t read_offset, int32_t write_offset, int32_t frames)
+AudioBuffers::accumulate_frames (AudioBuffers const * from, int32_t frames, int32_t read_offset, int32_t write_offset)
{
DCPOMATIC_ASSERT (_channels == from->channels ());
DCPOMATIC_ASSERT (read_offset >= 0);
b->copy_from (this, frames (), 0, 0);
return b;
}
+
+void
+AudioBuffers::append (shared_ptr<const AudioBuffers> other)
+{
+ ensure_size (_frames + other->frames());
+ copy_from (other.get(), other->frames(), 0, _frames);
+ _frames += other->frames();
+}
+
+void
+AudioBuffers::trim_start (int32_t frames)
+{
+ DCPOMATIC_ASSERT (frames <= _frames);
+ move (_frames - frames, frames, 0);
+ set_frames (_frames - frames);
+}
void copy_from (AudioBuffers const * from, int32_t frames_to_copy, int32_t read_offset, int32_t write_offset);
void copy_channel_from (AudioBuffers const * from, int from_channel, int to_channel);
- void move (int32_t from, int32_t to, int32_t frames);
+ void move (int32_t frames, int32_t from, int32_t to);
void accumulate_channel (AudioBuffers const * from, int from_channel, int to_channel, float gain = 1);
- void accumulate_frames (AudioBuffers const *, int32_t read_offset, int32_t write_offset, int32_t frames);
+ void accumulate_frames (AudioBuffers const * from, int32_t frames, int32_t read_offset, int32_t write_offset);
+ void append (boost::shared_ptr<const AudioBuffers> other);
+ void trim_start (int32_t frames);
private:
void allocate (int channels, int32_t frames);
}
/* Shuffle the tail down */
- _tail->move (out->frames(), 0, _tail->frames() - out->frames());
+ _tail->move (_tail->frames() - out->frames(), out->frames(), 0);
/* Copy input into the tail */
_tail->copy_from (in.get(), in->frames(), 0, _tail->frames() - in->frames());
int const amount = min (in->frames(), _tail->frames());
if (amount < _tail->frames ()) {
- _tail->move (amount, 0, _tail->frames() - amount);
+ _tail->move (_tail->frames() - amount, amount, 0);
}
_tail->copy_from (in.get(), amount, in->frames() - amount, _tail->frames () - amount);
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 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
using std::pair;
using std::min;
using std::max;
+using std::list;
+using std::cout;
using std::make_pair;
using boost::shared_ptr;
+using boost::optional;
-AudioMerger::AudioMerger (int channels, int frame_rate)
- : _buffers (new AudioBuffers (channels, 0))
- , _last_pull (0)
+AudioMerger::AudioMerger (int frame_rate)
+ : _last_pull (0)
, _frame_rate (frame_rate)
{
/** Pull audio up to a given time; after this call, no more data can be pushed
* before the specified time.
*/
-pair<shared_ptr<AudioBuffers>, DCPTime>
+list<pair<shared_ptr<AudioBuffers>, DCPTime> >
AudioMerger::pull (DCPTime time)
{
- /* Number of frames to return */
- Frame const to_return = time.frames_floor (_frame_rate) - _last_pull.frames_floor (_frame_rate);
- shared_ptr<AudioBuffers> out (new AudioBuffers (_buffers->channels(), to_return));
-
- /* And this is how many we will get from our buffer */
- Frame const to_return_from_buffers = min (to_return, Frame (_buffers->frames()));
-
- /* Copy the data that we have to the back end of the return buffer */
- out->copy_from (_buffers.get(), to_return_from_buffers, 0, to_return - to_return_from_buffers);
- /* Silence any gap at the start */
- out->make_silent (0, to_return - to_return_from_buffers);
-
- DCPTime out_time = _last_pull;
- _last_pull = time;
-
- /* And remove the data we're returning from our buffers */
- if (_buffers->frames() > to_return_from_buffers) {
- _buffers->move (to_return_from_buffers, 0, _buffers->frames() - to_return_from_buffers);
+ list<pair<shared_ptr<AudioBuffers>, DCPTime> > out;
+
+ DCPTimePeriod period (_last_pull, time);
+ _buffers.sort (AudioMerger::BufferComparator());
+
+ list<Buffer> new_buffers;
+
+ BOOST_FOREACH (Buffer i, _buffers) {
+ if (i.period().to < time) {
+ /* Completely within the pull period */
+ out.push_back (make_pair (i.audio, i.time));
+ } else if (i.time < time) {
+ /* Overlaps the end of the pull period */
+ shared_ptr<AudioBuffers> audio (new AudioBuffers (i.audio->channels(), DCPTime(time - i.time).frames_floor(_frame_rate)));
+ audio->copy_from (i.audio.get(), audio->frames(), 0, 0);
+ out.push_back (make_pair (audio, i.time));
+ i.audio->trim_start (audio->frames ());
+ i.time += DCPTime::from_frames(audio->frames(), _frame_rate);
+ new_buffers.push_back (i);
+ } else {
+ /* Not involved */
+ new_buffers.push_back (i);
+ }
}
- _buffers->set_frames (_buffers->frames() - to_return_from_buffers);
- return make_pair (out, out_time);
+ _buffers = new_buffers;
+ return out;
}
void
{
DCPOMATIC_ASSERT (time >= _last_pull);
- Frame const frame = time.frames_floor (_frame_rate);
- Frame after = max (Frame (_buffers->frames()), frame + audio->frames() - _last_pull.frames_floor (_frame_rate));
- _buffers->ensure_size (after);
- _buffers->accumulate_frames (audio.get(), 0, frame - _last_pull.frames_floor (_frame_rate), audio->frames ());
- _buffers->set_frames (after);
+ DCPTimePeriod period (time, time + DCPTime::from_frames (audio->frames(), _frame_rate));
+
+ /* Mix any parts of this new block with existing ones */
+ BOOST_FOREACH (Buffer i, _buffers) {
+ optional<DCPTimePeriod> overlap = i.period().overlap (period);
+ if (overlap) {
+ int32_t const offset = DCPTime(overlap->from - i.time).frames_floor(_frame_rate);
+ int32_t const frames = overlap->duration().frames_floor(_frame_rate);
+ if (i.time < time) {
+ i.audio->accumulate_frames(audio.get(), frames, 0, offset);
+ } else {
+ i.audio->accumulate_frames(audio.get(), frames, offset, 0);
+ }
+ }
+ }
+
+ list<DCPTimePeriod> periods;
+ BOOST_FOREACH (Buffer i, _buffers) {
+ periods.push_back (i.period ());
+ }
+
+ /* Add the non-overlapping parts */
+ BOOST_FOREACH (DCPTimePeriod i, subtract (period, periods)) {
+ list<Buffer>::iterator before = _buffers.end();
+ list<Buffer>::iterator after = _buffers.end();
+ for (list<Buffer>::iterator j = _buffers.begin(); j != _buffers.end(); ++j) {
+ if (j->period().to == i.from) {
+ before = j;
+ }
+ if (j->period().from == i.to) {
+ after = j;
+ }
+ }
+
+ /* Get the part of audio that we want to use */
+ shared_ptr<AudioBuffers> part (new AudioBuffers (audio->channels(), i.to.frames_floor(_frame_rate) - i.from.frames_floor(_frame_rate)));
+ part->copy_from (audio.get(), part->frames(), DCPTime(i.from - time).frames_floor(_frame_rate), 0);
+
+ if (before == _buffers.end() && after == _buffers.end()) {
+ /* New buffer */
+ _buffers.push_back (Buffer (part, time, _frame_rate));
+ } else if (before != _buffers.end() && after == _buffers.end()) {
+ /* We have an existing buffer before this one; append new data to it */
+ before->audio->append (part);
+ } else if (before ==_buffers.end() && after != _buffers.end()) {
+ /* We have an existing buffer after this one; append it to the new data and replace */
+ part->append (after->audio);
+ after->audio = part;
+ after->time = time;
+ } else {
+ /* We have existing buffers both before and after; coalesce them all */
+ before->audio->append (part);
+ before->audio->append (after->audio);
+ _buffers.erase (after);
+ }
+ }
}
/*
- Copyright (C) 2013-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2017 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
class AudioMerger
{
public:
- AudioMerger (int channels, int frame_rate);
+ AudioMerger (int frame_rate);
/** Pull audio up to a given time; after this call, no more data can be pushed
* before the specified time.
*/
- std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> pull (DCPTime time);
+ std::list<std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> > pull (DCPTime time);
void push (boost::shared_ptr<const AudioBuffers> audio, DCPTime time);
- DCPTime last_pull () const {
- return _last_pull;
- }
private:
- boost::shared_ptr<AudioBuffers> _buffers;
+ class Buffer
+ {
+ public:
+ /** @param c Channels
+ * @param f Frames
+ * @param t Time
+ * @param r Frame rate.
+ */
+ Buffer (int c, int32_t f, DCPTime t, int r)
+ : audio (new AudioBuffers (c, f))
+ , time (t)
+ , frame_rate (r)
+ {}
+
+ Buffer (boost::shared_ptr<AudioBuffers> a, DCPTime t, int r)
+ : audio (a)
+ , time (t)
+ , frame_rate (r)
+ {}
+
+ boost::shared_ptr<AudioBuffers> audio;
+ DCPTime time;
+ int frame_rate;
+
+ DCPTimePeriod period () const {
+ return DCPTimePeriod (time, time + DCPTime::from_frames (audio->frames(), frame_rate));
+ }
+ };
+
+ class BufferComparator
+ {
+ public:
+ bool operator() (AudioMerger::Buffer const & a, AudioMerger::Buffer const & b)
+ {
+ return a.time < b.time;
+ }
+ };
+
+ std::list<Buffer> _buffers;
DCPTime _last_pull;
int _frame_rate;
};
/*
- Copyright (C) 2014-2016 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "frame_rate_change.h"
#include "dcpomatic_assert.h"
#include <boost/optional.hpp>
+#include <boost/foreach.hpp>
#include <stdint.h>
#include <cmath>
#include <ostream>
}
};
+/** @param B Periods to subtract from `A', must be in ascending order of start time and must not overlap */
+template <class T>
+std::list<TimePeriod<T> > subtract (TimePeriod<T> A, std::list<TimePeriod<T> > const & B)
+{
+ std::list<TimePeriod<T> > result;
+ result.push_back (A);
+
+ BOOST_FOREACH (TimePeriod<T> i, B) {
+ std::list<TimePeriod<T> > new_result;
+ BOOST_FOREACH (TimePeriod<T> j, result) {
+ boost::optional<TimePeriod<T> > ov = i.overlap (j);
+ if (ov) {
+ if (*ov == i) {
+ /* A contains all of B */
+ if (i.from != j.from) {
+ new_result.push_back (TimePeriod<T> (j.from, i.from));
+ }
+ if (i.to != j.to) {
+ new_result.push_back (TimePeriod<T> (i.to, j.to));
+ }
+ } else if (*ov == j) {
+ /* B contains all of A */
+ } else if (i.from < j.from) {
+ /* B overlaps start of A */
+ new_result.push_back (TimePeriod<T> (i.to, j.to));
+ } else if (i.to > j.to) {
+ /* B overlaps end of A */
+ new_result.push_back (TimePeriod<T> (j.from, i.from));
+ }
+ } else {
+ new_result.push_back (j);
+ }
+ }
+ result = new_result;
+ }
+
+ return result;
+}
+
typedef TimePeriod<ContentTime> ContentTimePeriod;
typedef TimePeriod<DCPTime> DCPTimePeriod;
}
void
-Decoder::seek (ContentTime time, bool accurate)
+Decoder::seek (ContentTime, bool)
{
if (audio) {
audio->seek ();
virtual bool pass () = 0;
virtual void seek (ContentTime time, bool accurate);
- ContentTime position () const;
+ virtual ContentTime position () const;
};
#endif
if (ct < ContentTime ()) {
/* Discard audio data that comes before time 0 */
Frame const remove = min (int64_t (data->frames()), (-ct).frames_ceil(double((*stream)->frame_rate ())));
- data->move (remove, 0, data->frames() - remove);
+ data->move (data->frames() - remove, remove, 0);
data->set_frames (data->frames() - remove);
ct += ContentTime::from_frames (remove, (*stream)->frame_rate ());
}
, _always_burn_subtitles (false)
, _fast (false)
, _play_referenced (false)
- , _audio_merger (_film->audio_channels(), _film->audio_frame_rate())
+ , _audio_merger (_film->audio_frame_rate())
{
_film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1));
_playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this));
}
}
+ if (!_play_referenced) {
+ BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
+ shared_ptr<DCPContent> dc = dynamic_pointer_cast<DCPContent> (i->content);
+ if (dc) {
+ if (dc->reference_video()) {
+ _no_video.push_back (DCPTimePeriod (dc->position(), dc->end()));
+ }
+ if (dc->reference_audio()) {
+ _no_audio.push_back (DCPTimePeriod (dc->position(), dc->end()));
+ }
+ }
+ }
+ }
+
_have_valid_pieces = true;
}
}
if (!earliest) {
- /* No more content; fill up to the length of our playlist with silent black */
-
- DCPTime const length = _playlist->length ();
-
- DCPTime const frame = DCPTime::from_frames (1, _film->video_frame_rate());
- DCPTime from;
+ /* No more content; fill up with silent black */
+ DCPTimePeriod remaining_video (DCPTime(), _playlist->length());
if (_last_time) {
- from = _last_time.get() + frame;
- }
- for (DCPTime i = from; i < length; i += frame) {
- Video (black_player_video_frame (), i);
- }
-
- DCPTime t = _last_audio_time;
- while (t < length) {
- DCPTime block = min (DCPTime::from_seconds (0.5), length - t);
- Frame const samples = block.frames_round(_film->audio_frame_rate());
- if (samples) {
- shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), samples));
- silence->make_silent ();
- Audio (silence, t);
- }
- t += block;
+ remaining_video.from = _last_time.get() + one_video_frame();
}
-
+ fill_video (remaining_video);
+ fill_audio (DCPTimePeriod (_last_audio_time, _playlist->length()));
return true;
}
}
}
-// cout << "PULL " << to_string(pull_from) << "\n";
- pair<shared_ptr<AudioBuffers>, DCPTime> audio = _audio_merger.pull (pull_from);
- if (audio.first->frames() > 0) {
- DCPOMATIC_ASSERT (audio.second >= _last_audio_time);
- DCPTime t = _last_audio_time;
- while (t < audio.second) {
- /* Silence up to the time of this new audio */
- DCPTime block = min (DCPTime::from_seconds (0.5), audio.second - t);
- shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), block.frames_round(_film->audio_frame_rate())));
- silence->make_silent ();
- Audio (silence, t);
- t += block;
- }
-
- Audio (audio.first, audio.second);
- _last_audio_time = audio.second + DCPTime::from_frames(audio.first->frames(), _film->audio_frame_rate());
+ list<pair<shared_ptr<AudioBuffers>, DCPTime> > audio = _audio_merger.pull (pull_from);
+ for (list<pair<shared_ptr<AudioBuffers>, DCPTime> >::iterator i = audio.begin(); i != audio.end(); ++i) {
+ DCPOMATIC_ASSERT (i->second >= _last_audio_time);
+ fill_audio (DCPTimePeriod (_last_audio_time, i->second));
+ Audio (i->first, i->second);
+ _last_audio_time = i->second + DCPTime::from_frames(i->first->frames(), _film->audio_frame_rate());
}
return false;
/* Time and period of the frame we will emit */
DCPTime const time = content_video_to_dcp (piece, video.frame);
- DCPTimePeriod const period (time, time + DCPTime::from_frames (1, _film->video_frame_rate()));
+ DCPTimePeriod const period (time, time + one_video_frame());
/* Discard if it's outside the content's period */
if (time < piece->content->position() || time >= piece->content->end()) {
/* Fill gaps */
if (_last_time) {
- /* XXX: this may not work for 3D */
- DCPTime const frame = DCPTime::from_frames (1, _film->video_frame_rate());
- for (DCPTime i = _last_time.get() + frame; i < time; i += frame) {
- if (_playlist->video_content_at(i) && _last_video) {
- Video (shared_ptr<PlayerVideo> (new PlayerVideo (*_last_video)), i);
- } else {
- Video (black_player_video_frame (), i);
- }
- }
+ fill_video (DCPTimePeriod (_last_time.get() + one_video_frame(), time));
}
_last_video.reset (
content_audio.audio = _audio_processor->run (content_audio.audio, _film->audio_channels ());
}
-// cout << "PUSH " << content_audio.audio->frames() << " @ " << to_string(time) << "\n";
_audio_merger.push (content_audio.audio, time);
DCPOMATIC_ASSERT (_stream_states.find (stream) != _stream_states.end ());
}
if (accurate) {
- _last_time = time - DCPTime::from_frames (1, _film->video_frame_rate ());
+ _last_time = time - one_video_frame ();
} else {
_last_time = optional<DCPTime> ();
}
_resamplers[make_pair(content, stream)] = r;
return r;
}
+
+void
+Player::fill_video (DCPTimePeriod period)
+{
+ /* XXX: this may not work for 3D */
+ BOOST_FOREACH (DCPTimePeriod i, subtract(period, _no_video)) {
+ for (DCPTime j = i.from; j < i.to; j += one_video_frame()) {
+ if (_playlist->video_content_at(j) && _last_video) {
+ Video (shared_ptr<PlayerVideo> (new PlayerVideo (*_last_video)), j);
+ } else {
+ Video (black_player_video_frame(), j);
+ }
+ }
+ }
+}
+
+void
+Player::fill_audio (DCPTimePeriod period)
+{
+ BOOST_FOREACH (DCPTimePeriod i, subtract(period, _no_audio)) {
+ DCPTime t = i.from;
+ while (t < i.to) {
+ DCPTime block = min (DCPTime::from_seconds (0.5), i.to - t);
+ Frame const samples = block.frames_round(_film->audio_frame_rate());
+ if (samples) {
+ shared_ptr<AudioBuffers> silence (new AudioBuffers (_film->audio_channels(), samples));
+ silence->make_silent ();
+ Audio (silence, t);
+ }
+ t += block;
+ }
+ }
+}
+
+DCPTime
+Player::one_video_frame () const
+{
+ return DCPTime::from_frames (1, _film->video_frame_rate ());
+}
void image_subtitle (boost::weak_ptr<Piece>, ContentImageSubtitle);
void text_subtitle (boost::weak_ptr<Piece>, ContentTextSubtitle);
boost::shared_ptr<Resampler> resampler (boost::shared_ptr<const AudioContent> content, AudioStreamPtr stream, bool create);
+ DCPTime one_video_frame () const;
+ void fill_video (DCPTimePeriod period);
+ void fill_audio (DCPTimePeriod period);
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
};
std::map<AudioStreamPtr, StreamState> _stream_states;
+ std::list<DCPTimePeriod> _no_video;
+ std::list<DCPTimePeriod> _no_audio;
+
std::list<std::pair<PlayerSubtitles, DCPTimePeriod> > _subtitles;
boost::shared_ptr<AudioProcessor> _audio_processor;
/* Mix of L and R; -6dB down in amplitude (3dB in terms of power) */
shared_ptr<AudioBuffers> in_LR = in_L->clone ();
- in_LR->accumulate_frames (in_R.get(), 0, 0, in_R->frames ());
+ in_LR->accumulate_frames (in_R.get(), in_R->frames(), 0, 0);
in_LR->apply_gain (-6);
/* Run filters */
/* L + R minus 6dB (in terms of amplitude) */
shared_ptr<AudioBuffers> in_LR = in->channel(0);
- in_LR->accumulate_frames (in->channel(1).get(), 0, 0, in->frames());
+ in_LR->accumulate_frames (in->channel(1).get(), in->frames(), 0, 0);
in_LR->apply_gain (-6);
if (channels > 0) {
int const to = 666;
int const frames = 444;
- buffers.move (from, to, frames);
+ buffers.move (frames, from, to);
/* Re-seed and check the un-moved parts */
srand (84);
AudioBuffers b (3, 256);
random_fill (b);
- a.accumulate_frames (&b, 91, 44, 129);
+ a.accumulate_frames (&b, 129, 91, 44);
srand (38);
for (int i = 0; i < 256; ++i) {
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/signals2.hpp>
+#include <iostream>
using std::pair;
+using std::list;
+using std::cout;
using boost::shared_ptr;
using boost::bind;
int const sampling_rate = 48000;
-BOOST_AUTO_TEST_CASE (audio_merger_test1)
+static void
+push (AudioMerger& merger, int from, int to, int at)
{
- AudioMerger merger (1, sampling_rate);
-
- /* 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;
+ shared_ptr<AudioBuffers> buffers (new AudioBuffers (1, to - from));
+ for (int i = 0; i < (to - from); ++i) {
+ buffers->data()[0][i] = from + i;
}
- merger.push (buffers, DCPTime());
+ merger.push (buffers, DCPTime(at, sampling_rate));
+}
- /* Push 64 samples, 0 -> 63 at time 22 */
- merger.push (buffers, DCPTime::from_frames (22, sampling_rate));
+/* Basic mixing, 2 overlapping pushes */
+BOOST_AUTO_TEST_CASE (audio_merger_test1)
+{
+ AudioMerger merger (sampling_rate);
- pair<shared_ptr<AudioBuffers>, DCPTime> tb = merger.pull (DCPTime::from_frames (22, sampling_rate));
- BOOST_CHECK (tb.first != shared_ptr<const AudioBuffers> ());
- BOOST_CHECK_EQUAL (tb.first->frames(), 22);
- BOOST_CHECK_EQUAL (tb.second.get(), 0);
+ push (merger, 0, 64, 0);
+ push (merger, 0, 64, 22);
+
+ list<pair<shared_ptr<AudioBuffers>, DCPTime> > tb = merger.pull (DCPTime::from_frames (22, sampling_rate));
+ BOOST_REQUIRE (tb.size() == 1);
+ BOOST_CHECK (tb.front().first != shared_ptr<const AudioBuffers> ());
+ BOOST_CHECK_EQUAL (tb.front().first->frames(), 22);
+ BOOST_CHECK_EQUAL (tb.front().second.get(), 0);
/* And they should be a staircase */
for (int i = 0; i < 22; ++i) {
- BOOST_CHECK_EQUAL (tb.first->data()[0][i], i);
+ BOOST_CHECK_EQUAL (tb.front().first->data()[0][i], i);
}
tb = merger.pull (DCPTime::from_frames (22 + 64, sampling_rate));
-
- BOOST_CHECK_EQUAL (tb.first->frames(), 64);
- BOOST_CHECK_EQUAL (tb.second.get(), DCPTime::from_frames(22, sampling_rate).get());
+ BOOST_REQUIRE (tb.size() == 1);
+ BOOST_CHECK_EQUAL (tb.front().first->frames(), 64);
+ BOOST_CHECK_EQUAL (tb.front().second.get(), DCPTime::from_frames(22, sampling_rate).get());
/* Check the sample values */
for (int i = 0; i < 64; ++i) {
if (i < (64 - 22)) {
correct += i + 22;
}
- BOOST_CHECK_EQUAL (tb.first->data()[0][i], correct);
+ BOOST_CHECK_EQUAL (tb.front().first->data()[0][i], correct);
}
}
+/* Push at non-zero time */
BOOST_AUTO_TEST_CASE (audio_merger_test2)
{
- AudioMerger merger (1, sampling_rate);
+ AudioMerger merger (sampling_rate);
+
+ push (merger, 0, 64, 9);
- /* Push 64 samples, 0 -> 63 at time 9 */
- shared_ptr<AudioBuffers> buffers (new AudioBuffers (1, 64));
+ /* There's nothing from 0 to 9 */
+ list<pair<shared_ptr<AudioBuffers>, DCPTime> > tb = merger.pull (DCPTime::from_frames (9, sampling_rate));
+ BOOST_CHECK_EQUAL (tb.size(), 0);
+
+ /* Then there's our data at 9 */
+ tb = merger.pull (DCPTime::from_frames (9 + 64, sampling_rate));
+
+ BOOST_CHECK_EQUAL (tb.front().first->frames(), 64);
+ BOOST_CHECK_EQUAL (tb.front().second.get(), DCPTime::from_frames(9, sampling_rate).get());
+
+ /* Check the sample values */
for (int i = 0; i < 64; ++i) {
- buffers->data()[0][i] = i;
+ BOOST_CHECK_EQUAL (tb.front().first->data()[0][i], i);
}
- merger.push (buffers, DCPTime::from_frames (9, sampling_rate));
+}
- pair<shared_ptr<AudioBuffers>, DCPTime> tb = merger.pull (DCPTime::from_frames (9, sampling_rate));
- BOOST_CHECK_EQUAL (tb.first->frames(), 9);
- BOOST_CHECK_EQUAL (tb.second.get(), 0);
+/* Push two non contiguous blocks */
+BOOST_AUTO_TEST_CASE (audio_merger_test3)
+{
+ AudioMerger merger (sampling_rate);
- for (int i = 0; i < 9; ++i) {
- BOOST_CHECK_EQUAL (tb.first->data()[0][i], 0);
- }
+ push (merger, 0, 64, 17);
+ push (merger, 0, 64, 114);
- tb = merger.pull (DCPTime::from_frames (9 + 64, sampling_rate));
+ /* Get them back */
- BOOST_CHECK_EQUAL (tb.first->frames(), 64);
- BOOST_CHECK_EQUAL (tb.second.get(), DCPTime::from_frames(9, sampling_rate).get());
+ list<pair<shared_ptr<AudioBuffers>, DCPTime> > tb = merger.pull (DCPTime::from_frames (100, sampling_rate));
+ BOOST_REQUIRE (tb.size() == 1);
+ BOOST_CHECK_EQUAL (tb.front().first->frames(), 64);
+ BOOST_CHECK_EQUAL (tb.front().second.get(), DCPTime::from_frames(17, sampling_rate).get());
+ for (int i = 0; i < 64; ++i) {
+ BOOST_CHECK_EQUAL (tb.front().first->data()[0][i], i);
+ }
- /* Check the sample values */
+ tb = merger.pull (DCPTime::from_frames (200, sampling_rate));
+ BOOST_REQUIRE (tb.size() == 1);
+ BOOST_CHECK_EQUAL (tb.front().first->frames(), 64);
+ BOOST_CHECK_EQUAL (tb.front().second.get(), DCPTime::from_frames(114, sampling_rate).get());
for (int i = 0; i < 64; ++i) {
- BOOST_CHECK_EQUAL (tb.first->data()[0][i], i);
+ BOOST_CHECK_EQUAL (tb.front().first->data()[0][i], i);
}
}
/*
- Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2015-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
*/
-#include <boost/test/unit_test.hpp>
#include "lib/dcpomatic_time.h"
+#include <boost/test/unit_test.hpp>
+#include <list>
+
+using std::list;
BOOST_AUTO_TEST_CASE (dcpomatic_time_test)
{
BOOST_CHECK (a.overlap(b));
BOOST_CHECK (a.overlap(b).get() == DCPTimePeriod(DCPTime(1), DCPTime(9)));
}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test1)
+{
+ DCPTimePeriod A (DCPTime (0), DCPTime (106));
+ list<DCPTimePeriod> B;
+ B.push_back (DCPTimePeriod (DCPTime (0), DCPTime (42)));
+ B.push_back (DCPTimePeriod (DCPTime (52), DCPTime (91)));
+ B.push_back (DCPTimePeriod (DCPTime (94), DCPTime (106)));
+ list<DCPTimePeriod> r = subtract (A, B);
+ list<DCPTimePeriod>::const_iterator i = r.begin ();
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (42));
+ BOOST_CHECK (i->to == DCPTime (52));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (91));
+ BOOST_CHECK (i->to == DCPTime (94));
+ ++i;
+ BOOST_REQUIRE (i == r.end ());
+}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test2)
+{
+ DCPTimePeriod A (DCPTime (0), DCPTime (106));
+ list<DCPTimePeriod> B;
+ B.push_back (DCPTimePeriod (DCPTime (14), DCPTime (42)));
+ B.push_back (DCPTimePeriod (DCPTime (52), DCPTime (91)));
+ B.push_back (DCPTimePeriod (DCPTime (94), DCPTime (106)));
+ list<DCPTimePeriod> r = subtract (A, B);
+ list<DCPTimePeriod>::const_iterator i = r.begin ();
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (0));
+ BOOST_CHECK (i->to == DCPTime (14));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (42));
+ BOOST_CHECK (i->to == DCPTime (52));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (91));
+ BOOST_CHECK (i->to == DCPTime (94));
+ ++i;
+ BOOST_REQUIRE (i == r.end ());
+}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test3)
+{
+ DCPTimePeriod A (DCPTime (0), DCPTime (106));
+ list<DCPTimePeriod> B;
+ B.push_back (DCPTimePeriod (DCPTime (14), DCPTime (42)));
+ B.push_back (DCPTimePeriod (DCPTime (52), DCPTime (91)));
+ B.push_back (DCPTimePeriod (DCPTime (94), DCPTime (99)));
+ list<DCPTimePeriod> r = subtract (A, B);
+ list<DCPTimePeriod>::const_iterator i = r.begin ();
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (0));
+ BOOST_CHECK (i->to == DCPTime (14));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (42));
+ BOOST_CHECK (i->to == DCPTime (52));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (91));
+ BOOST_CHECK (i->to == DCPTime (94));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (99));
+ BOOST_CHECK (i->to == DCPTime (106));
+ ++i;
+ BOOST_REQUIRE (i == r.end ());
+}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test4)
+{
+ DCPTimePeriod A (DCPTime (0), DCPTime (106));
+ list<DCPTimePeriod> B;
+ list<DCPTimePeriod> r = subtract (A, B);
+ list<DCPTimePeriod>::const_iterator i = r.begin ();
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (0));
+ BOOST_CHECK (i->to == DCPTime (106));
+ ++i;
+ BOOST_REQUIRE (i == r.end ());
+}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test5)
+{
+ DCPTimePeriod A (DCPTime (0), DCPTime (106));
+ list<DCPTimePeriod> B;
+ B.push_back (DCPTimePeriod (DCPTime (14), DCPTime (42)));
+ B.push_back (DCPTimePeriod (DCPTime (42), DCPTime (91)));
+ B.push_back (DCPTimePeriod (DCPTime (94), DCPTime (99)));
+ list<DCPTimePeriod> r = subtract (A, B);
+ list<DCPTimePeriod>::const_iterator i = r.begin ();
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (0));
+ BOOST_CHECK (i->to == DCPTime (14));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from ==DCPTime (91));
+ BOOST_CHECK (i->to == DCPTime (94));
+ ++i;
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (99));
+ BOOST_CHECK (i->to == DCPTime (106));
+ ++i;
+ BOOST_REQUIRE (i == r.end ());
+}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test6)
+{
+ DCPTimePeriod A (DCPTime (0), DCPTime (106));
+ list<DCPTimePeriod> B;
+ B.push_back (DCPTimePeriod (DCPTime (0), DCPTime (42)));
+ B.push_back (DCPTimePeriod (DCPTime (42), DCPTime (91)));
+ B.push_back (DCPTimePeriod (DCPTime (91), DCPTime (106)));
+ list<DCPTimePeriod> r = subtract (A, B);
+ BOOST_CHECK (r.empty());
+}
+
+BOOST_AUTO_TEST_CASE (dcpomatic_time_period_subtract_test7)
+{
+ DCPTimePeriod A (DCPTime (228), DCPTime (356));
+ list<DCPTimePeriod> B;
+ B.push_back (DCPTimePeriod (DCPTime (34), DCPTime (162)));
+ list<DCPTimePeriod> r = subtract (A, B);
+ list<DCPTimePeriod>::const_iterator i = r.begin ();
+ BOOST_REQUIRE (i != r.end ());
+ BOOST_CHECK (i->from == DCPTime (228));
+ BOOST_CHECK (i->to == DCPTime (356));
+ ++i;
+ BOOST_REQUIRE (i == r.end ());
+}
ov->make_dcp ();
wait_for_jobs ();
+ std::cout << "incoming vf.\n";
+
/* Make the VF */
shared_ptr<Film> vf = new_test_film ("vf_test2_vf");
vf->set_name ("vf_test2_vf");
vf->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
vf->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
- shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (content_factory (vf, ov->dir (ov->dcp_name ())).front());
+ shared_ptr<DCPContent> dcp (new DCPContent (vf, ov->dir (ov->dcp_name ())));
BOOST_REQUIRE (dcp);
vf->examine_and_add_content (dcp);
wait_for_jobs ();
vf->set_name ("vf_test3_vf");
vf->set_dcp_content_type (DCPContentType::from_isdcf_name ("TST"));
vf->set_reel_type (REELTYPE_BY_VIDEO_CONTENT);
- shared_ptr<DCPContent> dcp = dynamic_pointer_cast<DCPContent> (content_factory(vf, ov->dir (ov->dcp_name ())).front());
+ shared_ptr<DCPContent> dcp (new DCPContent (vf, ov->dir (ov->dcp_name ())));
BOOST_REQUIRE (dcp);
dcp->set_trim_start (ContentTime::from_seconds (1));
dcp->set_trim_end (ContentTime::from_seconds (1));