Various work on audio channel mapping.
authorCarl Hetherington <cth@carlh.net>
Sat, 6 Apr 2013 22:57:46 +0000 (23:57 +0100)
committerCarl Hetherington <cth@carlh.net>
Sat, 6 Apr 2013 22:57:46 +0000 (23:57 +0100)
21 files changed:
src/lib/analyse_audio_job.cc
src/lib/audio_mapping.cc
src/lib/audio_mapping.h
src/lib/encoder.cc
src/lib/ffmpeg_content.cc
src/lib/film.cc
src/lib/film.h
src/lib/imagemagick_content.cc
src/lib/playlist.cc
src/lib/playlist.h
src/lib/sndfile_content.cc
src/lib/writer.cc
src/wx/audio_dialog.cc
src/wx/audio_dialog.h
src/wx/audio_mapping_view.cc [new file with mode: 0644]
src/wx/audio_mapping_view.h [new file with mode: 0644]
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/sndfile_content_dialog.cc [deleted file]
src/wx/sndfile_content_dialog.h [deleted file]
src/wx/wscript

index 5ef6515dd4dbd3f011f4107aab6c5a780c84a218..50096d7c125f183d694d4534febbadb4f20f4739 100644 (file)
@@ -57,8 +57,8 @@ AnalyseAudioJob::run ()
 
        _samples_per_point = max (int64_t (1), _film->audio_length() / _num_points);
 
-       _current.resize (_film->audio_channels ());
-       _analysis.reset (new AudioAnalysis (_film->audio_channels()));
+       _current.resize (MAX_AUDIO_CHANNELS);
+       _analysis.reset (new AudioAnalysis (MAX_AUDIO_CHANNELS));
                         
        while (!player->pass()) {
                set_progress (float (_done) / _film->audio_length ());
index 3fc423e101a891074eada7261bff77d622311cba..48cc23307504457f1e94a7724635e852ba0d052d 100644 (file)
 
 #include "audio_mapping.h"
 
-using std::map;
-using boost::optional;
-
-AutomaticAudioMapping::AutomaticAudioMapping (int c)
-       : _source_channels (c)
+using std::list;
+using std::cout;
+using std::make_pair;
+using std::pair;
+using boost::shared_ptr;
+
+void
+AudioMapping::add (Channel c, libdcp::Channel d)
 {
-
+       _content_to_dcp.push_back (make_pair (c, d));
 }
 
-optional<libdcp::Channel>
-AutomaticAudioMapping::source_to_dcp (int c) const
+/* XXX: this is grotty */
+int
+AudioMapping::dcp_channels () const
 {
-       if (c >= _source_channels) {
-               return optional<libdcp::Channel> ();
+       for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+               if (((int) i->second) > 2) {
+                       return 6;
+               }
        }
 
-       if (_source_channels == 1) {
-               /* mono sources to centre */
-               return libdcp::CENTRE;
-       }
-       
-       return static_cast<libdcp::Channel> (c);
+       return 2;
 }
 
-optional<int>
-AutomaticAudioMapping::dcp_to_source (libdcp::Channel c) const
+list<AudioMapping::Channel>
+AudioMapping::dcp_to_content (libdcp::Channel d) const
 {
-       if (_source_channels == 1) {
-               if (c == libdcp::CENTRE) {
-                       return 0;
-               } else {
-                       return optional<int> ();
+       list<AudioMapping::Channel> c;
+       for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+               if (i->second == d) {
+                       c.push_back (i->first);
                }
        }
 
-       if (static_cast<int> (c) >= _source_channels) {
-               return optional<int> ();
-       }
-       
-       return static_cast<int> (c);
+       return c;
 }
 
-int
-AutomaticAudioMapping::dcp_channels () const
+list<AudioMapping::Channel>
+AudioMapping::content_channels () const
 {
-       if (_source_channels == 1) {
-               /* The source is mono, so to put the mono channel into
-                  the centre we need to generate a 5.1 soundtrack.
-               */
-               return 6;
+       list<AudioMapping::Channel> c;
+       for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+               if (find (c.begin(), c.end(), i->first) == c.end ()) {
+                       c.push_back (i->first);
+               }
        }
 
-       return _source_channels;
+       return c;
 }
 
-optional<int>
-ConfiguredAudioMapping::dcp_to_source (libdcp::Channel c) const
+list<libdcp::Channel>
+AudioMapping::content_to_dcp (Channel c) const
 {
-       map<int, libdcp::Channel>::const_iterator i = _source_to_dcp.begin ();
-       while (i != _source_to_dcp.end() && i->second != c) {
-               ++i;
-       }
-
-       if (i == _source_to_dcp.end ()) {
-               return boost::none;
+       list<libdcp::Channel> d;
+       for (list<pair<Channel, libdcp::Channel> >::const_iterator i = _content_to_dcp.begin(); i != _content_to_dcp.end(); ++i) {
+               if (i->first == c) {
+                       d.push_back (i->second);
+               }
        }
 
-       return i->first;
+       return d;
 }
 
-optional<libdcp::Channel>
-ConfiguredAudioMapping::source_to_dcp (int c) const
+bool
+operator== (AudioMapping::Channel const & a, AudioMapping::Channel const & b)
 {
-       map<int, libdcp::Channel>::const_iterator i = _source_to_dcp.find (c);
-       if (i == _source_to_dcp.end ()) {
-               return boost::none;
-       }
-
-       return i->second;
+       shared_ptr<const AudioContent> sa = a.content.lock ();
+       shared_ptr<const AudioContent> sb = b.content.lock ();
+       return sa == sb && a.index == b.index;
 }
 
        
+       
index 8804bde06174f8e9669a02a81ff207ecbd76310a..10ac20b48eb1631076e51204ade111f687fdc0f6 100644 (file)
 
 */
 
-#include <vector>
-#include <map>
-#include <boost/optional.hpp>
+#ifndef DVDOMATIC_AUDIO_MAPPING_H
+#define DVDOMATIC_AUDIO_MAPPING_H
+
+#include <list>
+#include <string>
 #include <libdcp/types.h>
+#include <boost/shared_ptr.hpp>
+#include "audio_content.h"
 
 class AudioMapping
 {
 public:
-       virtual boost::optional<libdcp::Channel> source_to_dcp (int c) const = 0;
-       virtual boost::optional<int> dcp_to_source (libdcp::Channel c) const = 0;
-};
-
-class AutomaticAudioMapping : public AudioMapping
-{
-public:
-       AutomaticAudioMapping (int);
+       struct Channel {
+               Channel (boost::weak_ptr<const AudioContent> c, int i)
+                       : content (c)
+                       , index (i)
+               {}
+               
+               boost::weak_ptr<const AudioContent> content;
+               int index;
+       };
+
+       void add (Channel, libdcp::Channel);
 
-       boost::optional<libdcp::Channel> source_to_dcp (int c) const;
-       boost::optional<int> dcp_to_source (libdcp::Channel c) const;
        int dcp_channels () const;
-       
-private:
-       int _source_channels;
-};
+       std::list<Channel> dcp_to_content (libdcp::Channel) const;
+       std::list<std::pair<Channel, libdcp::Channel> > content_to_dcp () const {
+               return _content_to_dcp;
+       }
 
-class ConfiguredAudioMapping : public AudioMapping
-{
-public:
-       boost::optional<libdcp::Channel> source_to_dcp (int c) const;
-       boost::optional<int> dcp_to_source (libdcp::Channel c) const;
+       std::list<Channel> content_channels () const;
+       std::list<libdcp::Channel> content_to_dcp (Channel) const;
 
 private:
-       std::map<int, libdcp::Channel> _source_to_dcp;
+       std::list<std::pair<Channel, libdcp::Channel> > _content_to_dcp;
 };
+
+extern bool operator== (AudioMapping::Channel const &, AudioMapping::Channel const &);
+
+#endif
index a0b88e33ed5ae8df6e6fa72bbddfc1e89bce210a..0542587a0af59505efcf4d4528bee80b04e4b4b0 100644 (file)
@@ -427,6 +427,8 @@ Encoder::encoder_thread (ServerDescription* server)
 void
 Encoder::write_audio (shared_ptr<const AudioBuffers> data)
 {
+#if 0
+       XXX
        AutomaticAudioMapping m (_film->audio_channels ());
        if (m.dcp_channels() != _film->audio_channels()) {
 
@@ -444,6 +446,7 @@ Encoder::write_audio (shared_ptr<const AudioBuffers> data)
 
                data = b;
        }
+#endif 
 
        _writer->write (data);
 }
index cc95105e5cc280afc482f3c607df477c8ceb76dd..d36abe2c3ffdef191f4c0238bdf5f331fc6d112e 100644 (file)
@@ -152,12 +152,13 @@ FFmpegContent::examine (shared_ptr<Film> film, shared_ptr<Job> job, bool quick)
         signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
         signal_changed (FFmpegContentProperty::AUDIO_STREAMS);
         signal_changed (FFmpegContentProperty::AUDIO_STREAM);
+        signal_changed (AudioContentProperty::AUDIO_CHANNELS);
 }
 
 string
 FFmpegContent::summary () const
 {
-       return String::compose (_("Movie: %1"), file().filename ());
+       return String::compose (_("Movie: %1"), file().filename().string());
 }
 
 string
index 69d2a28e20afc30f2fe63c6c6f4c9ad92389e0e3..b1f740ec2c10db725c9adcc5c8e2aa79142225a2 100644 (file)
@@ -960,6 +960,7 @@ Film::signal_changed (Property p)
        case Film::CONTENT:
                _playlist->setup (content ());
                set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
+               set_audio_mapping (_playlist->default_audio_mapping ());
                break;
        default:
                break;
@@ -1235,12 +1236,25 @@ Film::set_ffmpeg_audio_stream (FFmpegAudioStream s)
        }
 }
 
+void
+Film::set_audio_mapping (AudioMapping m)
+{
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _audio_mapping = m;
+       }
+
+       signal_changed (AUDIO_MAPPING);
+}
+
 void
 Film::content_changed (boost::weak_ptr<Content> c, int p)
 {
        if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
                set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
-       }
+       } else if (p == AudioContentProperty::AUDIO_CHANNELS) {
+               set_audio_mapping (_playlist->default_audio_mapping ());
+       }               
 
        if (ui_signaller) {
                ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
index 9c5f561e65775b978b431bf2541ba8efd546cdb2..532d32bdcc5477c3ee863231743101cb714416c6 100644 (file)
@@ -36,6 +36,7 @@
 #include "dci_metadata.h"
 #include "types.h"
 #include "ffmpeg_content.h"
+#include "audio_mapping.h"
 
 class DCPContentType;
 class Format;
@@ -146,7 +147,8 @@ public:
                COLOUR_LUT,
                J2K_BANDWIDTH,
                DCI_METADATA,
-               DCP_FRAME_RATE
+               DCP_FRAME_RATE,
+               AUDIO_MAPPING
        };
 
 
@@ -262,6 +264,11 @@ public:
                return _dcp_frame_rate;
        }
 
+       AudioMapping audio_mapping () const {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               return _audio_mapping;
+       }
+
        /* SET */
 
        void set_directory (std::string);
@@ -294,6 +301,7 @@ public:
        void set_dci_metadata (DCIMetadata);
        void set_dcp_frame_rate (int);
        void set_dci_date_today ();
+       void set_audio_mapping (AudioMapping);
 
        /** Emitted when some property has of the Film has changed */
        mutable boost::signals2::signal<void (Property)> Changed;
@@ -314,6 +322,7 @@ private:
        void read_metadata ();
        void content_changed (boost::weak_ptr<Content>, int);
        boost::shared_ptr<FFmpegContent> ffmpeg () const;
+       void setup_default_audio_mapping ();
 
        /** Log to write to */
        boost::shared_ptr<Log> _log;
@@ -378,6 +387,7 @@ private:
        int _dcp_frame_rate;
        /** The date that we should use in a DCI name */
        boost::gregorian::date _dci_date;
+       AudioMapping _audio_mapping;
 
        /** true if our state has changed since we last saved it */
        mutable bool _dirty;
index 912c180a8286f37f959ee53e33dc517edbdb3f1f..59fde40bb7d1059025bdaa91f941485c2204e2c5 100644 (file)
@@ -45,7 +45,7 @@ ImageMagickContent::ImageMagickContent (shared_ptr<const cxml::Node> node)
 string
 ImageMagickContent::summary () const
 {
-       return String::compose (_("Image: %1"), file().filename ());
+       return String::compose (_("Image: %1"), file().filename().string());
 }
 
 bool
index e0220ef6f6377768852a80ec0e60a489586b28c3..3f7905fa9c11c251bacdeeefb38f565aab699141 100644 (file)
@@ -30,6 +30,7 @@
 using std::list;
 using std::cout;
 using std::vector;
+using std::min;
 using boost::shared_ptr;
 using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
@@ -224,3 +225,47 @@ Playlist::content_changed (weak_ptr<Content> c, int p)
 {
        ContentChanged (c, p);
 }
+
+AudioMapping
+Playlist::default_audio_mapping () const
+{
+       AudioMapping m;
+
+       switch (_audio_from) {
+       case AUDIO_NONE:
+               break;
+       case AUDIO_FFMPEG:
+               if (_ffmpeg->audio_channels() == 1) {
+                       /* Map mono sources to centre */
+                       m.add (AudioMapping::Channel (_ffmpeg, 0), libdcp::CENTRE);
+               } else {
+                       int const N = min (_ffmpeg->audio_channels (), MAX_AUDIO_CHANNELS);
+                       /* Otherwise just start with a 1:1 mapping */
+                       for (int i = 0; i < N; ++i) {
+                               m.add (AudioMapping::Channel (_ffmpeg, i), (libdcp::Channel) i);
+                       }
+               }
+               break;
+
+       case AUDIO_SNDFILE:
+       {
+               int n = 0;
+               for (list<shared_ptr<const SndfileContent> >::const_iterator i = _sndfile.begin(); i != _sndfile.end(); ++i) {
+                       cout << "sndfile " << (*i)->audio_channels() << "\n";
+                       for (int j = 0; j < (*i)->audio_channels(); ++j) {
+                               m.add (AudioMapping::Channel (*i, j), (libdcp::Channel) n);
+                               ++n;
+                               if (n >= MAX_AUDIO_CHANNELS) {
+                                       break;
+                               }
+                       }
+                       if (n >= MAX_AUDIO_CHANNELS) {
+                               break;
+                       }
+               }
+               break;
+       }
+       }
+
+       return m;
+}
index 1a24a02277bc1bd3bb3caf5dff55e3f3e54110e4..1d189cb070be9077f0ac241fcbefcf190366ac03 100644 (file)
@@ -25,6 +25,7 @@
 #include "video_sink.h"
 #include "audio_sink.h"
 #include "ffmpeg_content.h"
+#include "audio_mapping.h"
 
 class Content;
 class FFmpegContent;
@@ -53,6 +54,8 @@ public:
        libdcp::Size video_size () const;
        ContentVideoFrame video_length () const;
 
+       AudioMapping default_audio_mapping () const;
+
        enum VideoFrom {
                VIDEO_NONE,
                VIDEO_FFMPEG,
index ee4f21eefb959e05b2f5ec8e06f03441c88b3e24..539b0dfb586a4926c47e4e1f3e6678966e254ad9 100644 (file)
 
 using std::string;
 using std::stringstream;
+using std::cout;
 using boost::shared_ptr;
 using boost::lexical_cast;
 
 SndfileContent::SndfileContent (boost::filesystem::path f)
        : Content (f)
        , AudioContent (f)
+       , _audio_channels (0)
+       , _audio_length (0)
+       , _audio_frame_rate (0)
 {
 
 }
@@ -49,7 +53,7 @@ SndfileContent::SndfileContent (shared_ptr<const cxml::Node> node)
 string
 SndfileContent::summary () const
 {
-       return String::compose (_("Sound file: %1"), file().filename ());
+       return String::compose (_("Sound file: %1"), file().filename().string());
 }
 
 string
@@ -115,3 +119,4 @@ SndfileContent::as_xml (xmlpp::Node* node) const
        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));
 }
+
index 6df1a1f210d92d52dd99aeb67d3289941b71b989..7258826ba1774f5a9fd3e32656bb32a2a761edd3 100644 (file)
@@ -78,15 +78,13 @@ Writer::Writer (shared_ptr<Film> f)
 
        _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
 
-       AutomaticAudioMapping m (_film->audio_channels ());
-       
-       if (m.dcp_channels() > 0) {
+       if (_film->audio_channels() > 0) {
                _sound_asset.reset (
                        new libdcp::SoundAsset (
                                _film->dir (_film->dcp_name()),
                                N_("audio.mxf"),
                                _film->dcp_frame_rate (),
-                               m.dcp_channels (),
+                               _film->audio_mapping().dcp_channels (),
                                dcp_audio_sample_rate (_film->audio_frame_rate())
                                )
                        );
index f53ea7c19f2facba3698a4faee1adc0bb3f836ea..4c50260fa47b35d231b06803f19fcfe062b59978 100644 (file)
@@ -20,7 +20,6 @@
 #include <boost/filesystem.hpp>
 #include "lib/audio_analysis.h"
 #include "lib/film.h"
-#include "lib/audio_mapping.h"
 #include "audio_dialog.h"
 #include "audio_plot.h"
 #include "wx_util.h"
@@ -93,7 +92,6 @@ AudioDialog::set_film (shared_ptr<Film> f)
        _film = f;
 
        try_to_load_analysis ();
-       setup_channels ();
        _plot->set_gain (_film->audio_gain ());
 
        _film_changed_connection = _film->Changed.connect (bind (&AudioDialog::film_changed, this, _1));
@@ -102,23 +100,6 @@ AudioDialog::set_film (shared_ptr<Film> f)
        SetTitle (wxString::Format (_("DVD-o-matic audio - %s"), std_to_wx(_film->name()).data()));
 }
 
-void
-AudioDialog::setup_channels ()
-{
-       if (!_film->has_audio()) {
-               return;
-       }
-
-       AutomaticAudioMapping m (_film->audio_channels ());
-       
-       for (int i = 0; i < MAX_AUDIO_CHANNELS; ++i) {
-               if (m.dcp_to_source(static_cast<libdcp::Channel>(i))) {
-                       _channel_checkbox[i]->Show ();
-               } else {
-                       _channel_checkbox[i]->Hide ();
-               }
-       }
-}      
 
 void
 AudioDialog::try_to_load_analysis ()
@@ -135,12 +116,8 @@ AudioDialog::try_to_load_analysis ()
                
        _plot->set_analysis (a);
 
-       AutomaticAudioMapping m (_film->audio_channels ());
-       optional<libdcp::Channel> c = m.source_to_dcp (0);
-       if (c) {
-               _channel_checkbox[c.get()]->SetValue (true);
-               _plot->set_channel_visible (0, true);
-       }
+       _channel_checkbox[0]->SetValue (true);
+       _plot->set_channel_visible (0, true);
 
        for (int i = 0; i < AudioPoint::COUNT; ++i) {
                _type_checkbox[i]->SetValue (true);
@@ -158,11 +135,7 @@ AudioDialog::channel_clicked (wxCommandEvent& ev)
 
        assert (c < MAX_AUDIO_CHANNELS);
 
-       AutomaticAudioMapping m (_film->audio_channels ());
-       optional<int> s = m.dcp_to_source (static_cast<libdcp::Channel> (c));
-       if (s) {
-               _plot->set_channel_visible (s.get(), _channel_checkbox[c]->GetValue ());
-       }
+       _plot->set_channel_visible (c, _channel_checkbox[c]->GetValue ());
 }
 
 void
@@ -172,9 +145,6 @@ AudioDialog::film_changed (Film::Property p)
        case Film::AUDIO_GAIN:
                _plot->set_gain (_film->audio_gain ());
                break;
-       case Film::CONTENT:
-               setup_channels ();
-               break;
        default:
                break;
        }
index 514faeea0e952ff83c716392d3aacf5a5514d2ee..db1d74f306393ceaf9860919aefe82434a92ea36 100644 (file)
@@ -39,7 +39,6 @@ private:
        void type_clicked (wxCommandEvent &);
        void smoothing_changed (wxScrollEvent &);
        void try_to_load_analysis ();
-       void setup_channels ();
 
        boost::shared_ptr<Film> _film;
        AudioPlot* _plot;
diff --git a/src/wx/audio_mapping_view.cc b/src/wx/audio_mapping_view.cc
new file mode 100644 (file)
index 0000000..d62609d
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+    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 <wx/renderer.h>
+#include <wx/grid.h>
+#include <libdcp/types.h>
+#include "lib/audio_mapping.h"
+#include "audio_mapping_view.h"
+#include "wx_util.h"
+
+using std::cout;
+using std::list;
+using boost::shared_ptr;
+
+/* This could go away with wxWidgets 2.9, which has an API call
+   to find these values.
+*/
+
+#ifdef __WXMSW__
+#define CHECKBOX_WIDTH 16
+#define CHECKBOX_HEIGHT 16
+#endif
+
+#ifdef __WXGTK__
+#define CHECKBOX_WIDTH 20
+#define CHECKBOX_HEIGHT 20
+#endif
+
+
+class NoSelectionStringRenderer : public wxGridCellStringRenderer
+{
+public:
+       void Draw (wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect, int row, int col, bool)
+       {
+               wxGridCellStringRenderer::Draw (grid, attr, dc, rect, row, col, false);
+       }
+};
+
+class CheckBoxRenderer : public wxGridCellRenderer
+{
+public:
+
+       void Draw (wxGrid& grid, wxGridCellAttr &, wxDC& dc, const wxRect& rect, int row, int col, bool)
+       {
+               wxRendererNative::Get().DrawCheckBox (
+                       &grid,
+                       dc, rect,
+                       grid.GetCellValue (row, col) == "1" ? static_cast<int>(wxCONTROL_CHECKED) : 0
+                       );
+       }
+
+       wxSize GetBestSize (wxGrid &, wxGridCellAttr &, wxDC &, int, int)
+       {
+               return wxSize (CHECKBOX_WIDTH + 4, CHECKBOX_HEIGHT + 4);
+       }
+       
+       wxGridCellRenderer* Clone () const
+       {
+               return new CheckBoxRenderer;
+       }
+};
+
+
+AudioMappingView::AudioMappingView (wxWindow* parent)
+       : wxPanel (parent, wxID_ANY)
+{
+       _grid = new wxGrid (this, wxID_ANY);
+
+       _grid->CreateGrid (0, 7);
+       _grid->HideRowLabels ();
+       _grid->DisableDragRowSize ();
+       _grid->DisableDragColSize ();
+       _grid->EnableEditing (false);
+       _grid->SetCellHighlightPenWidth (0);
+       _grid->SetDefaultRenderer (new NoSelectionStringRenderer);
+
+       _grid->SetColLabelValue (0, _("Content channel"));
+       _grid->SetColLabelValue (1, _("L"));
+       _grid->SetColLabelValue (2, _("R"));
+       _grid->SetColLabelValue (3, _("C"));
+       _grid->SetColLabelValue (4, _("Lfe"));
+       _grid->SetColLabelValue (5, _("Ls"));
+       _grid->SetColLabelValue (6, _("Rs"));
+
+       _grid->AutoSize ();
+
+       wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
+       s->Add (_grid, 1, wxEXPAND);
+       SetSizerAndFit (s);
+
+       Connect (wxID_ANY, wxEVT_GRID_CELL_LEFT_CLICK, wxGridEventHandler (AudioMappingView::left_click), 0, this);
+}
+
+void
+AudioMappingView::left_click (wxGridEvent& ev)
+{
+       if (ev.GetCol() == 0) {
+               return;
+       }
+       
+       if (_grid->GetCellValue (ev.GetRow(), ev.GetCol()) == "1") {
+               _grid->SetCellValue (ev.GetRow(), ev.GetCol(), "0");
+       } else {
+               _grid->SetCellValue (ev.GetRow(), ev.GetCol(), "1");
+       }
+}
+
+void
+AudioMappingView::set_mapping (AudioMapping map)
+{
+       if (_grid->GetNumberRows ()) {
+               _grid->DeleteRows (0, _grid->GetNumberRows ());
+       }
+
+       list<AudioMapping::Channel> content_channels = map.content_channels ();
+       _grid->InsertRows (0, content_channels.size ());
+
+       for (size_t r = 0; r < content_channels.size(); ++r) {
+               for (int c = 1; c < 7; ++c) {
+                       _grid->SetCellRenderer (r, c, new CheckBoxRenderer);
+               }
+       }
+       
+       int n = 0;
+       for (list<AudioMapping::Channel>::iterator i = content_channels.begin(); i != content_channels.end(); ++i) {
+               shared_ptr<const AudioContent> ac = i->content.lock ();
+               assert (ac);
+               _grid->SetCellValue (n, 0, wxString::Format ("%s %d", std_to_wx (ac->file().filename().string()), i->index + 1));
+
+               list<libdcp::Channel> const d = map.content_to_dcp (*i);
+               for (list<libdcp::Channel>::const_iterator j = d.begin(); j != d.end(); ++j) {
+                       _grid->SetCellValue (n, static_cast<int> (*j) + 1, "1");
+               }
+               ++n;
+       }
+
+       _grid->AutoSize ();
+}
+
diff --git a/src/wx/audio_mapping_view.h b/src/wx/audio_mapping_view.h
new file mode 100644 (file)
index 0000000..3642941
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+    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/signals2.hpp>
+#include <wx/wx.h>
+#include <wx/grid.h>
+
+class AudioMappingView : public wxPanel
+{
+public:
+       AudioMappingView (wxWindow *);
+
+       void set_mapping (AudioMapping);
+
+private:
+       void left_click (wxGridEvent &);
+
+       wxGrid* _grid;
+};
index 6c42359fb69f109910335f81c7f688bba37eea6b..6f08b656768af6f3b95205158ff9f665517c97d9 100644 (file)
@@ -50,7 +50,7 @@
 #include "scaler.h"
 #include "audio_dialog.h"
 #include "imagemagick_content_dialog.h"
-#include "sndfile_content_dialog.h"
+#include "audio_mapping_view.h"
 
 using std::string;
 using std::cout;
@@ -197,7 +197,9 @@ FilmEditor::connect_to_widgets ()
        _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
        _format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
        _trust_content_headers->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_headers_changed), 0, this);
-       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler (FilmEditor::content_item_selected), 0, this);
+       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this);
+       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this);
+       _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler (FilmEditor::content_activated), 0, this);
        _content_add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_add_clicked), 0, this);
        _content_remove->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_remove_clicked), 0, this);
        _content_edit->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_edit_clicked), 0, this);
@@ -406,6 +408,9 @@ FilmEditor::make_audio_panel ()
                 grid->Add (s, 1, wxEXPAND);
         }
 
+       _audio_mapping = new AudioMappingView (_audio_panel);
+       _audio_sizer->Add (_audio_mapping, 1, wxEXPAND | wxALL, 6);
+       
        _audio_gain->SetRange (-60, 60);
        _audio_delay->SetRange (-1000, 1000);
 }
@@ -691,6 +696,10 @@ FilmEditor::film_changed (Film::Property p)
                        _frame_rate_description->SetLabel (wxT (""));
                        _best_dcp_frame_rate->Disable ();
                }
+               break;
+       case Film::AUDIO_MAPPING:
+               _audio_mapping->set_mapping (_film->audio_mapping ());
+               break;
        }
 }
 
@@ -849,6 +858,7 @@ FilmEditor::set_film (shared_ptr<Film> f)
        film_changed (Film::J2K_BANDWIDTH);
        film_changed (Film::DCI_METADATA);
        film_changed (Film::DCP_FRAME_RATE);
+       film_changed (Film::AUDIO_MAPPING);
 
        film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::SUBTITLE_STREAMS);
        film_content_changed (boost::shared_ptr<Content> (), FFmpegContentProperty::SUBTITLE_STREAM);
@@ -1242,6 +1252,15 @@ FilmEditor::content_remove_clicked (wxCommandEvent &)
        }
 }
 
+void
+FilmEditor::content_activated (wxListEvent& ev)
+{
+       ContentList c = _film->content ();
+       assert (ev.GetIndex() >= 0 && size_t (ev.GetIndex()) < c.size ());
+
+       edit_content (c[ev.GetIndex()]);
+}
+
 void
 FilmEditor::content_edit_clicked (wxCommandEvent &)
 {
@@ -1250,22 +1269,19 @@ FilmEditor::content_edit_clicked (wxCommandEvent &)
                return;
        }
 
+       edit_content (c);
+}
+
+void
+FilmEditor::edit_content (shared_ptr<Content> c)
+{
        shared_ptr<ImageMagickContent> im = dynamic_pointer_cast<ImageMagickContent> (c);
        if (im) {
                ImageMagickContentDialog* d = new ImageMagickContentDialog (this, im);
-               int const r = d->ShowModal ();
-               d->Destroy ();
-
-               if (r == wxID_OK) {
-                       im->set_video_length (d->video_length() * 24);
-               }
-       }
-
-       shared_ptr<SndfileContent> sf = dynamic_pointer_cast<SndfileContent> (c);
-       if (sf) {
-               SndfileContentDialog* d = new SndfileContentDialog (this, sf);
                d->ShowModal ();
                d->Destroy ();
+
+               im->set_video_length (d->video_length() * 24);
        }
 }
 
@@ -1288,7 +1304,7 @@ FilmEditor::content_later_clicked (wxCommandEvent &)
 }
 
 void
-FilmEditor::content_item_selected (wxListEvent &)
+FilmEditor::content_selection_changed (wxListEvent &)
 {
         setup_content_button_sensitivity ();
        setup_content_information ();
@@ -1311,11 +1327,12 @@ FilmEditor::setup_content_button_sensitivity ()
 {
         _content_add->Enable (_generally_sensitive);
 
-       bool const have_selection = _content->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1;
-        _content_edit->Enable (have_selection && _generally_sensitive);
-        _content_remove->Enable (have_selection && _generally_sensitive);
-        _content_earlier->Enable (have_selection && _generally_sensitive);
-        _content_later->Enable (have_selection && _generally_sensitive);
+       shared_ptr<Content> selection = selected_content ();
+
+        _content_edit->Enable (selection && _generally_sensitive && dynamic_pointer_cast<ImageMagickContent> (selection));
+        _content_remove->Enable (selection && _generally_sensitive);
+        _content_earlier->Enable (selection && _generally_sensitive);
+        _content_later->Enable (selection && _generally_sensitive);
 }
 
 shared_ptr<Content>
index b6f6a24eec87cdf6f025d2f3e8d0ad2d7f013816..bc6b045c409999ae073d518487f88d8b77623c7b 100644 (file)
@@ -33,6 +33,7 @@ class wxListCtrl;
 class wxListEvent;
 class Film;
 class AudioDialog;
+class AudioMappingView;
 
 /** @class FilmEditor
  *  @brief A wx widget to edit a film's metadata, and perform various functions.
@@ -63,7 +64,8 @@ private:
        void top_crop_changed (wxCommandEvent &);
        void bottom_crop_changed (wxCommandEvent &);
        void trust_content_headers_changed (wxCommandEvent &);
-       void content_item_selected (wxListEvent &);
+       void content_selection_changed (wxListEvent &);
+       void content_activated (wxListEvent &);
        void content_add_clicked (wxCommandEvent &);
        void content_remove_clicked (wxCommandEvent &);
        void content_edit_clicked (wxCommandEvent &);
@@ -110,6 +112,7 @@ private:
        
        void active_jobs_changed (bool);
        boost::shared_ptr<Content> selected_content ();
+       void edit_content (boost::shared_ptr<Content>);
 
        wxNotebook* _notebook;
        wxPanel* _film_panel;
@@ -152,6 +155,7 @@ private:
        wxButton* _show_audio;
        wxSpinCtrl* _audio_delay;
        wxChoice* _ffmpeg_audio_stream;
+       AudioMappingView* _audio_mapping;
        wxCheckBox* _with_subtitles;
        wxChoice* _ffmpeg_subtitle_stream;
        wxSpinCtrl* _subtitle_offset;
diff --git a/src/wx/sndfile_content_dialog.cc b/src/wx/sndfile_content_dialog.cc
deleted file mode 100644 (file)
index f305b15..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
-    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 "lib/util.h"
-#include "lib/sndfile_content.h"
-#include "sndfile_content_dialog.h"
-#include "wx_util.h"
-
-using boost::shared_ptr;
-
-SndfileContentDialog::SndfileContentDialog (wxWindow* parent, shared_ptr<SndfileContent> content)
-       : wxDialog (parent, wxID_ANY, _("Sound file"))
-{
-       wxFlexGridSizer* grid = new wxFlexGridSizer (7, 6, 0);
-
-       add_label_to_sizer (grid, this, wxT (""));
-       add_label_to_sizer (grid, this, _("L"));
-       add_label_to_sizer (grid, this, _("R"));
-       add_label_to_sizer (grid, this, _("C"));
-       add_label_to_sizer (grid, this, _("Lfe"));
-       add_label_to_sizer (grid, this, _("Ls"));
-       add_label_to_sizer (grid, this, _("Rs"));
-
-       _buttons.resize (content->audio_channels ());
-       for (int i = 0; i < content->audio_channels(); ++i) {
-
-               if (content->audio_channels() == 1) {
-                       add_label_to_sizer (grid, this, _("Source"));
-               } else {
-                       add_label_to_sizer (grid, this, wxString::Format (_("Source %d"), i + 1));
-               }
-               
-               for (int j = 0; j < MAX_AUDIO_CHANNELS; ++j) {
-                       wxRadioButton* b = new wxRadioButton (this, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, j ? 0 : wxRB_GROUP);
-                       _buttons[i].push_back (b);
-                       grid->Add (b, wxSHRINK);
-               }
-       }
-
-       wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
-       overall_sizer->Add (grid, 1, wxEXPAND | wxALL, 6);
-
-       wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
-       if (buttons) {
-               overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
-       }
-
-       SetSizer (overall_sizer);
-       overall_sizer->Layout ();
-       overall_sizer->SetSizeHints (this);
-}
diff --git a/src/wx/sndfile_content_dialog.h b/src/wx/sndfile_content_dialog.h
deleted file mode 100644 (file)
index 5a32889..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-    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 <vector>
-#include <boost/shared_ptr.hpp>
-#include <wx/wx.h>
-#include "lib/audio_mapping.h"
-
-class SndfileContent;
-
-class SndfileContentDialog : public wxDialog
-{
-public:
-       SndfileContentDialog (wxWindow *, boost::shared_ptr<SndfileContent>);
-
-       ConfiguredAudioMapping audio_mapping () const;
-
-private:
-       std::vector<std::vector<wxRadioButton *> > _buttons;
-};
index bd21af6ceabfecc29e963122af8a6fe70665436e..7f9cde9acae25bad106a0a6c9327df135e6b4314 100644 (file)
@@ -5,6 +5,7 @@ import i18n
 
 sources = """
           audio_dialog.cc
+          audio_mapping_view.cc
           audio_plot.cc
           config_dialog.cc
           dci_metadata_dialog.cc
@@ -20,7 +21,6 @@ sources = """
           new_film_dialog.cc
           properties_dialog.cc
           server_dialog.cc
-          sndfile_content_dialog.cc
           wx_util.cc
           wx_ui_signaller.cc
           """