Make show audio work on the whole DCP, not individual content.
[dcpomatic.git] / src / lib / film.cc
index e5c5d931265a41bf293d2eef940251d7c85811eb..590acedfd370f56dd04a5b2077c3a5758b78fc9f 100644 (file)
@@ -41,6 +41,8 @@
 #include "safe_stringstream.h"
 #include "environment_info.h"
 #include "raw_convert.h"
+#include "audio_processor.h"
+#include "md5_digester.h"
 #include <libcxml/cxml.h>
 #include <dcp/cpl.h>
 #include <dcp/signer.h>
@@ -126,6 +128,7 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _sequence_video (true)
        , _interop (false)
        , _burn_subtitles (false)
+       , _audio_processor (0)
        , _state_version (current_state_version)
        , _dirty (false)
 {
@@ -245,9 +248,28 @@ Film::filename_safe_name () const
 }
 
 boost::filesystem::path
-Film::audio_analysis_dir () const
+Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
 {
-       return dir ("analysis");
+       boost::filesystem::path p = dir ("analysis");
+
+       MD5Digester digester;
+       BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
+               shared_ptr<AudioContent> ac = dynamic_pointer_cast<AudioContent> (i);
+               if (!ac) {
+                       continue;
+               }
+               
+               digester.add (ac->digest ());
+               digester.add (ac->audio_mapping().digest ());
+               digester.add (ac->audio_gain ());
+       }
+
+       if (audio_processor ()) {
+               digester.add (audio_processor()->id ());
+       }
+
+       p /= digester.get ();
+       return p;
 }
 
 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
@@ -328,6 +350,9 @@ Film::metadata () const
        root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
        root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
        root->add_child("Key")->add_child_text (_key.hex ());
+       if (_audio_processor) {
+               root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
+       }
        _playlist->as_xml (root->add_child ("Playlist"));
 
        return doc;
@@ -408,6 +433,12 @@ Film::read_metadata ()
        }
        _key = dcp::Key (f.string_child ("Key"));
 
+       if (f.optional_string_child ("AudioProcessor")) {
+               _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
+       } else {
+               _audio_processor = 0;
+       }
+
        list<string> notes;
        /* This method is the only one that can return notes (so far) */
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
@@ -584,11 +615,11 @@ Film::isdcf_name (bool if_created_now) const
 
        /* Find all mapped channels */
 
-       list<dcp::Channel> mapped;
+       list<int> mapped;
        for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
                shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (*i);
                if (ac) {
-                       list<dcp::Channel> c = ac->audio_mapping().mapped_dcp_channels ();
+                       list<int> c = ac->audio_mapping().mapped_output_channels ();
                        copy (c.begin(), c.end(), back_inserter (mapped));
                }
        }
@@ -600,13 +631,13 @@ Film::isdcf_name (bool if_created_now) const
                        
        int non_lfe = 0;
        int lfe = 0;
-       for (list<dcp::Channel>::const_iterator i = mapped.begin(); i != mapped.end(); ++i) {
-               if (static_cast<int> (*i) >= audio_channels()) {
+       for (list<int>::const_iterator i = mapped.begin(); i != mapped.end(); ++i) {
+               if (*i >= audio_channels()) {
                        /* This channel is mapped but is not included in the DCP */
                        continue;
                }
                
-               if ((*i) == dcp::LFE) {
+               if (static_cast<dcp::Channel> (*i) == dcp::LFE) {
                        ++lfe;
                } else {
                        ++non_lfe;
@@ -769,6 +800,13 @@ Film::set_burn_subtitles (bool b)
        signal_changed (BURN_SUBTITLES);
 }
 
+void
+Film::set_audio_processor (AudioProcessor const * processor)
+{
+       _audio_processor = processor;
+       signal_changed (AUDIO_PROCESSOR);
+}
+
 void
 Film::signal_changed (Property p)
 {
@@ -983,9 +1021,7 @@ Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
 {
        if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
                set_video_frame_rate (_playlist->best_dcp_frame_rate ());
-       } else if (
-               p == AudioContentProperty::AUDIO_MAPPING ||
-               p == AudioContentProperty::AUDIO_CHANNELS) {
+       } else if (p == AudioContentProperty::AUDIO_STREAMS) {
                signal_changed (NAME);
        }
 
@@ -1144,3 +1180,53 @@ Film::subtitle_language () const
 
        return all;
 }
+
+/** Change the gains of the supplied AudioMapping to make it a default
+ *  for this film.  The defaults are guessed based on what processor (if any)
+ *  is in use and the number of input channels.
+ */
+void
+Film::make_audio_mapping_default (AudioMapping& mapping) const
+{
+       if (audio_processor ()) {
+               audio_processor()->make_audio_mapping_default (mapping);
+       } else {
+               mapping.make_zero ();
+               if (mapping.input_channels() == 1) {
+                       /* Mono -> Centre */
+                       mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
+               } else {
+                       /* 1:1 mapping */
+                       for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
+                               mapping.set (i, i, 1);
+                       }
+               }
+       }
+}
+
+/** @return The names of the channels that audio contents' outputs are passed into;
+ *  this is either the DCP or a AudioProcessor.
+ */
+vector<string>
+Film::audio_output_names () const
+{
+       if (audio_processor ()) {
+               return audio_processor()->input_names ();
+       }
+       
+       vector<string> n;
+       n.push_back (_("L"));
+       n.push_back (_("R"));
+       n.push_back (_("C"));
+       n.push_back (_("Lfe"));
+       n.push_back (_("Ls"));
+       n.push_back (_("Rs"));
+       n.push_back (_("HI"));
+       n.push_back (_("VI"));
+       n.push_back (_("Lc"));
+       n.push_back (_("Rc"));
+       n.push_back (_("BsL"));
+       n.push_back (_("BsR"));
+
+       return vector<string> (n.begin(), n.begin() + audio_channels ());
+}