-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
shared_ptr<Piece> earliest;
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
- cout << "check " << (*i)->content->file() << " start=" << (*i)->content->start() << ", next=" << (*i)->decoder->next() << ", end=" << (*i)->content->end() << "\n";
+// cout << "check " << (*i)->content->file() << " start=" << (*i)->content->start() << ", next=" << (*i)->decoder->next() << ", end=" << (*i)->content->end() << "\n";
if (((*i)->decoder->next() + (*i)->content->start()) >= (*i)->content->end()) {
continue;
}
- if (!_audio && dynamic_pointer_cast<SndfileContent> ((*i)->content)) {
+ if (!_audio && dynamic_pointer_cast<AudioDecoder> ((*i)->decoder) && !dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) {
continue;
}
Time const t = (*i)->content->start() + (*i)->decoder->next();
if (t < earliest_t) {
- cout << "\t candidate; " << t << " " << (t / TIME_HZ) << ".\n";
+// cout << "\t candidate; " << t << " " << (t / TIME_HZ) << ".\n";
earliest_t = t;
earliest = *i;
}
void
Player::process_video (weak_ptr<Content> weak_content, shared_ptr<const Image> image, bool same, shared_ptr<Subtitle> sub, Time time)
{
- cout << "[V]\n";
-
shared_ptr<Content> content = weak_content.lock ();
if (!content) {
return;
return;
}
- cout << "seek to " << t << " " << (t / TIME_HZ) << "\n";
+// cout << "seek to " << t << " " << (t / TIME_HZ) << "\n";
for (list<shared_ptr<Piece> >::iterator i = _pieces.begin(); i != _pieces.end(); ++i) {
Time s = t - (*i)->content->start ();
s = max (static_cast<Time> (0), s);
s = min ((*i)->content->length(), s);
- cout << "seek [" << (*i)->content->file() << "," << (*i)->content->start() << "," << (*i)->content->end() << "] to " << s << "\n";
+// cout << "seek [" << (*i)->content->file() << "," << (*i)->content->start() << "," << (*i)->content->end() << "] to " << s << "\n";
(*i)->decoder->seek (s);
}
}
+void
+Player::add_black_piece (Time s, Time len)
+{
+ shared_ptr<NullContent> nc (new NullContent (_film, s, len));
+ shared_ptr<BlackDecoder> bd (new BlackDecoder (_film, nc));
+ bd->Video.connect (bind (&Player::process_video, this, nc, _1, _2, _3, _4));
+ _pieces.push_back (shared_ptr<Piece> (new Piece (nc, bd)));
+ cout << "\tblack @ " << s << " -- " << (s + len) << "\n";
+}
+
+void
+Player::add_silent_piece (Time s, Time len)
+{
+ shared_ptr<NullContent> nc (new NullContent (_film, s, len));
+ shared_ptr<SilenceDecoder> sd (new SilenceDecoder (_film, nc));
+ sd->Audio.connect (bind (&Player::process_audio, this, nc, _1, _2));
+ _pieces.push_back (shared_ptr<Piece> (new Piece (nc, sd)));
+ cout << "\tsilence @ " << s << " -- " << (s + len) << "\n";
+}
+
+
struct ContentSorter
{
bool operator() (shared_ptr<Content> a, shared_ptr<Content> b)
void
Player::setup_pieces ()
{
-// cout << "----- Player SETUP PIECES.\n";
+ cout << "----- Player SETUP PIECES.\n";
list<shared_ptr<Piece> > old_pieces = _pieces;
fd->Audio.connect (bind (&Player::process_audio, this, *i, _1, _2));
decoder = fd;
-// cout << "\tFFmpeg @ " << fc->start() << " -- " << fc->end() << "\n";
+ cout << "\tFFmpeg @ " << fc->start() << " -- " << fc->end() << "\n";
}
shared_ptr<const ImageMagickContent> ic = dynamic_pointer_cast<const ImageMagickContent> (*i);
}
decoder = id;
-// cout << "\tImageMagick @ " << ic->start() << " -- " << ic->end() << "\n";
+ cout << "\tImageMagick @ " << ic->start() << " -- " << ic->end() << "\n";
}
shared_ptr<const SndfileContent> sc = dynamic_pointer_cast<const SndfileContent> (*i);
sd->Audio.connect (bind (&Player::process_audio, this, *i, _1, _2));
decoder = sd;
-// cout << "\tSndfile @ " << sc->start() << " -- " << sc->end() << "\n";
+ cout << "\tSndfile @ " << sc->start() << " -- " << sc->end() << "\n";
}
_pieces.push_back (shared_ptr<Piece> (new Piece (*i, decoder)));
if (dynamic_pointer_cast<VideoContent> ((*i)->content)) {
Time const diff = (*i)->content->start() - video_pos;
if (diff > 0) {
- shared_ptr<NullContent> nc (new NullContent (_film, video_pos, diff));
- shared_ptr<BlackDecoder> bd (new BlackDecoder (_film, nc));
- bd->Video.connect (bind (&Player::process_video, this, nc, _1, _2, _3, _4));
- _pieces.push_back (shared_ptr<Piece> (new Piece (nc, bd)));
-// cout << "\tblack @ " << video_pos << " -- " << (video_pos + diff) << "\n";
+ add_black_piece (video_pos, diff);
}
video_pos = (*i)->content->end();
} else {
Time const diff = (*i)->content->start() - audio_pos;
if (diff > 0) {
- shared_ptr<NullContent> nc (new NullContent (_film, audio_pos, diff));
- shared_ptr<SilenceDecoder> sd (new SilenceDecoder (_film, nc));
- sd->Audio.connect (bind (&Player::process_audio, this, nc, _1, _2));
- _pieces.push_back (shared_ptr<Piece> (new Piece (nc, sd)));
-// cout << "\tsilence @ " << audio_pos << " -- " << (audio_pos + diff) << "\n";
+ add_silent_piece (video_pos, diff);
}
audio_pos = (*i)->content->end();
}
}
+
+ if (video_pos < audio_pos) {
+ add_black_piece (video_pos, audio_pos - video_pos);
+ } else if (audio_pos < video_pos) {
+ add_silent_piece (audio_pos, video_pos - audio_pos);
+ }
}
void
void playlist_changed ();
void content_changed (boost::weak_ptr<Content>, int);
void do_seek (Time, bool);
+ void add_black_piece (Time, Time);
+ void add_silent_piece (Time, Time);
boost::shared_ptr<const Film> _film;
boost::shared_ptr<const Playlist> _playlist;
/** Our pieces are ready to go; if this is false the pieces must be (re-)created before they are used */
bool _have_valid_pieces;
std::list<boost::shared_ptr<Piece> > _pieces;
-
- /** Time of the earliest thing not yet to have been emitted */
Time _position;
AudioBuffers _audio_buffers;
Time _next_audio;
{
node->add_child("Type")->add_child_text ("Sndfile");
Content::as_xml (node);
+ AudioContent::as_xml (node);
node->add_child("AudioChannels")->add_child_text (lexical_cast<string> (_audio_channels));
node->add_child("AudioLength")->add_child_text (lexical_cast<string> (_audio_length));
node->add_child("AudioFrameRate")->add_child_text (lexical_cast<string> (_audio_frame_rate));
-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
/*
Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
#include "timeline_dialog.h"
#include "audio_mapping_view.h"
#include "container.h"
+#include "timecode.h"
using std::string;
using std::cout;
FilmEditor::make_video_panel ()
{
_video_panel = new wxPanel (_content_notebook);
- _video_sizer = new wxBoxSizer (wxVERTICAL);
- _video_panel->SetSizer (_video_sizer);
+ wxBoxSizer* video_sizer = new wxBoxSizer (wxVERTICAL);
+ _video_panel->SetSizer (video_sizer);
wxGridBagSizer* grid = new wxGridBagSizer (4, 4);
- _video_sizer->Add (grid, 0, wxALL, 8);
+ video_sizer->Add (grid, 0, wxALL, 8);
int r = 0;
add_label_to_grid_bag_sizer (grid, _video_panel, _("Left crop"), wxGBPosition (r, 0));
_content_notebook = new wxNotebook (_content_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_LEFT);
_content_sizer->Add (_content_notebook, 1, wxEXPAND | wxTOP, 6);
-
+
make_video_panel ();
_content_notebook->AddPage (_video_panel, _("Video"), false);
make_audio_panel ();
_content_notebook->AddPage (_audio_panel, _("Audio"), false);
make_subtitle_panel ();
_content_notebook->AddPage (_subtitle_panel, _("Subtitles"), false);
+ make_timing_panel ();
+ _content_notebook->AddPage (_timing_panel, _("Timing"), false);
_loop_count->SetRange (2, 1024);
}
FilmEditor::make_audio_panel ()
{
_audio_panel = new wxPanel (_content_notebook);
- _audio_sizer = new wxBoxSizer (wxVERTICAL);
- _audio_panel->SetSizer (_audio_sizer);
+ wxBoxSizer* audio_sizer = new wxBoxSizer (wxVERTICAL);
+ _audio_panel->SetSizer (audio_sizer);
wxFlexGridSizer* grid = new wxFlexGridSizer (3, 4, 4);
- _audio_sizer->Add (grid, 0, wxALL, 8);
+ audio_sizer->Add (grid, 0, wxALL, 8);
_show_audio = new wxButton (_audio_panel, wxID_ANY, _("Show Audio..."));
grid->Add (_show_audio, 1);
grid->AddSpacer (0);
_audio_mapping = new AudioMappingView (_audio_panel);
- _audio_sizer->Add (_audio_mapping, 1, wxEXPAND | wxALL, 6);
+ audio_sizer->Add (_audio_mapping, 1, wxEXPAND | wxALL, 6);
_audio_gain->SetRange (-60, 60);
_audio_delay->SetRange (-1000, 1000);
FilmEditor::make_subtitle_panel ()
{
_subtitle_panel = new wxPanel (_content_notebook);
- _subtitle_sizer = new wxBoxSizer (wxVERTICAL);
- _subtitle_panel->SetSizer (_subtitle_sizer);
+ wxBoxSizer* subtitle_sizer = new wxBoxSizer (wxVERTICAL);
+ _subtitle_panel->SetSizer (subtitle_sizer);
wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
- _subtitle_sizer->Add (grid, 0, wxALL, 8);
+ subtitle_sizer->Add (grid, 0, wxALL, 8);
_with_subtitles = new wxCheckBox (_subtitle_panel, wxID_ANY, _("With Subtitles"));
grid->Add (_with_subtitles, 1);
_subtitle_scale->SetRange (1, 1000);
}
+void
+FilmEditor::make_timing_panel ()
+{
+ _timing_panel = new wxPanel (_content_notebook);
+ wxBoxSizer* timing_sizer = new wxBoxSizer (wxVERTICAL);
+ _timing_panel->SetSizer (timing_sizer);
+ wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
+ timing_sizer->Add (grid, 0, wxALL, 8);
+
+ add_label_to_sizer (grid, _timing_panel, _("Start time"));
+ _start = new Timecode (_timing_panel);
+ grid->Add (_start);
+ add_label_to_sizer (grid, _timing_panel, _("Length"));
+ _length = new Timecode (_timing_panel);
+ grid->Add (_length);
+}
+
+
/** Called when the left crop widget has been changed */
void
FilmEditor::left_crop_changed (wxCommandEvent &)
ffmpeg_content = dynamic_pointer_cast<FFmpegContent> (content);
}
- if (property == VideoContentProperty::VIDEO_CROP) {
+ if (property == ContentProperty::START) {
+ if (content) {
+ _start->set (content->start (), _film->dcp_video_frame_rate ());
+ } else {
+ _start->set (0, 24);
+ }
+ } else if (property == VideoContentProperty::VIDEO_CROP) {
checked_set (_left_crop, video_content ? video_content->crop().left : 0);
checked_set (_right_crop, video_content ? video_content->crop().right : 0);
checked_set (_top_crop, video_content ? video_content->crop().top : 0);
film_changed (Film::DCI_METADATA);
film_changed (Film::DCP_VIDEO_FRAME_RATE);
+ film_content_changed (boost::shared_ptr<Content> (), ContentProperty::START);
film_content_changed (boost::shared_ptr<Content> (), VideoContentProperty::VIDEO_CROP);
film_content_changed (boost::shared_ptr<Content> (), AudioContentProperty::AUDIO_GAIN);
film_content_changed (boost::shared_ptr<Content> (), AudioContentProperty::AUDIO_DELAY);
class TimelineDialog;
class AudioMappingView;
class Format;
+class Timecode;
/** @class FilmEditor
* @brief A wx widget to edit a film's metadata, and perform various functions.
void make_video_panel ();
void make_audio_panel ();
void make_subtitle_panel ();
+ void make_timing_panel ();
void connect_to_widgets ();
/* Handle changes to the view */
wxPanel* _content_panel;
wxSizer* _content_sizer;
wxPanel* _video_panel;
- wxSizer* _video_sizer;
wxPanel* _audio_panel;
- wxSizer* _audio_sizer;
wxPanel* _subtitle_panel;
- wxSizer* _subtitle_sizer;
+ wxPanel* _timing_panel;
/** The film we are editing */
boost::shared_ptr<Film> _film;
wxStaticText* _audio_description;
wxChoice* _subtitle_stream;
AudioMappingView* _audio_mapping;
+ Timecode* _start;
+ Timecode* _length;
std::vector<Format const *> _formats;
--- /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/lexical_cast.hpp>
+#include "timecode.h"
+#include "wx_util.h"
+
+using std::string;
+using boost::lexical_cast;
+
+Timecode::Timecode (wxWindow* parent)
+ : wxPanel (parent)
+{
+ wxClientDC dc (parent);
+ wxSize size = dc.GetTextExtent (wxT ("9999"));
+ size.SetHeight (-1);
+
+ wxTextValidator validator (wxFILTER_INCLUDE_CHAR_LIST);
+ wxArrayString list;
+
+ string n = "0123456789";
+ for (size_t i = 0; i < n.length(); ++i) {
+ list.Add (n[i]);
+ }
+
+ validator.SetIncludes (list);
+
+ wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL);
+ _hours = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size, 0, validator);
+ _hours->SetMaxLength (2);
+ sizer->Add (_hours);
+ add_label_to_sizer (sizer, this, ":");
+ _minutes = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size);
+ _minutes->SetMaxLength (2);
+ sizer->Add (_minutes);
+ add_label_to_sizer (sizer, this, ":");
+ _seconds = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size);
+ _seconds->SetMaxLength (2);
+ sizer->Add (_seconds);
+ add_label_to_sizer (sizer, this, ".");
+ _frames = new wxTextCtrl (this, wxID_ANY, wxT(""), wxDefaultPosition, size);
+ _frames->SetMaxLength (2);
+ sizer->Add (_frames);
+
+ SetSizerAndFit (sizer);
+}
+
+void
+Timecode::set (Time t, int fps)
+{
+ int const h = t / (3600 * TIME_HZ);
+ t -= h * 3600 * TIME_HZ;
+ int const m = t / (60 * TIME_HZ);
+ t -= m * 60 * TIME_HZ;
+ int const s = t / TIME_HZ;
+ t -= s * TIME_HZ;
+ int const f = t * fps / TIME_HZ;
+
+ _hours->SetValue (wxString::Format ("%02d", h));
+ _minutes->SetValue (wxString::Format ("%02d", m));
+ _seconds->SetValue (wxString::Format ("%02d", s));
+ _frames->SetValue (wxString::Format ("%02d", f));
+}
+
+Time
+Timecode::get (int fps) const
+{
+ Time t = 0;
+ t += lexical_cast<int> (wx_to_std (_hours->GetValue())) * 3600 * TIME_HZ;
+ t += lexical_cast<int> (wx_to_std (_minutes->GetValue())) * 60 * TIME_HZ;
+ t += lexical_cast<int> (wx_to_std (_seconds->GetValue())) * TIME_HZ;
+ t += lexical_cast<int> (wx_to_std (_frames->GetValue())) * TIME_HZ / fps;
+ return t;
+}
--- /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 <wx/wx.h>
+#include "lib/types.h"
+
+class Timecode : public wxPanel
+{
+public:
+ Timecode (wxWindow *);
+
+ void set (Time, int);
+ Time get (int) const;
+
+private:
+ wxTextCtrl* _hours;
+ wxTextCtrl* _minutes;
+ wxTextCtrl* _seconds;
+ wxTextCtrl* _frames;
+};
-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
, _track (t)
, _selected (false)
{
- c->Changed.connect (bind (&ContentView::content_changed, this, _2));
+ _content_connection = c->Changed.connect (bind (&ContentView::content_changed, this, _2));
}
Rect bbox () const
#endif
wxGraphicsPath path = gc->CreatePath ();
- path.MoveToPoint (time_x (start), y_pos (_track));
- path.AddLineToPoint (time_x (start + len), y_pos (_track));
- path.AddLineToPoint (time_x (start + len), y_pos (_track + 1));
- path.AddLineToPoint (time_x (start), y_pos (_track + 1));
- path.AddLineToPoint (time_x (start), y_pos (_track));
+ path.MoveToPoint (time_x (start), y_pos (_track) + 4);
+ path.AddLineToPoint (time_x (start + len), y_pos (_track) + 4);
+ path.AddLineToPoint (time_x (start + len), y_pos (_track + 1) - 4);
+ path.AddLineToPoint (time_x (start), y_pos (_track + 1) - 4);
+ path.AddLineToPoint (time_x (start), y_pos (_track) + 4);
gc->StrokePath (path);
gc->FillPath (path);
boost::weak_ptr<Content> _content;
int _track;
bool _selected;
+
+ boost::signals2::scoped_connection _content_connection;
};
class AudioContentView : public ContentView
playlist_changed ();
- film->playlist()->Changed.connect (bind (&Timeline::playlist_changed, this));
+ _playlist_connection = film->playlist()->Changed.connect (bind (&Timeline::playlist_changed, this));
}
void
if (_down_view) {
shared_ptr<Content> c = _down_view->content().lock();
if (c) {
- c->set_start (_down_view_start + time_diff);
+ c->set_start (max (static_cast<Time> (0), _down_view_start + time_diff));
}
}
}
-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
/*
Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
+#include <boost/signals2.hpp>
#include <wx/wx.h>
#include "util.h"
boost::shared_ptr<ContentView> _down_view;
Time _down_view_start;
bool _first_move;
+
+ boost::signals2::scoped_connection _playlist_connection;
};
new_film_dialog.cc
properties_dialog.cc
server_dialog.cc
+ timecode.cc
timeline.cc
timeline_dialog.cc
wx_util.cc