Restore short-cutting of analysis gain updates.
authorCarl Hetherington <cth@carlh.net>
Wed, 1 Jul 2015 22:37:55 +0000 (23:37 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 1 Jul 2015 22:37:55 +0000 (23:37 +0100)
If we have an analysis of one piece of content and
the gain changes we don't re-run the analysis, instead
applying a suitable `correction' in the UI.

src/lib/analyse_audio_job.cc
src/lib/audio_analysis.cc
src/lib/audio_analysis.h
src/lib/film.cc
src/wx/audio_dialog.cc
src/wx/audio_dialog.h
src/wx/audio_plot.cc
src/wx/audio_plot.h

index 1cec15c..2f48d12 100644 (file)
@@ -89,6 +89,16 @@ AnalyseAudioJob::run ()
        }
 
        _analysis->set_peak (_overall_peak, DCPTime::from_frames (_overall_peak_frame, _film->audio_frame_rate ()));
+
+       if (_playlist->content().size() == 1) {
+               /* If there was only one piece of content in this analysis we may later need to know what its
+                  gain was when we analysed it.
+               */
+               shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (_playlist->content().front ());
+               DCPOMATIC_ASSERT (ac);
+               _analysis->set_analysis_gain (ac->audio_gain ());
+       }
+
        _analysis->write (_film->audio_analysis_path (_playlist));
 
        set_progress (1);
index 7e1dc6e..127def8 100644 (file)
@@ -102,6 +102,7 @@ AudioAnalysis::AudioAnalysis (boost::filesystem::path filename)
 
        _peak = f.number_child<float> ("Peak");
        _peak_time = DCPTime (f.number_child<DCPTime::Type> ("PeakTime"));
+       _analysis_gain = f.optional_number_child<double> ("AnalysisGain");
 }
 
 void
@@ -149,5 +150,9 @@ AudioAnalysis::write (boost::filesystem::path filename)
                root->add_child("PeakTime")->add_child_text (raw_convert<string> (_peak_time.get().get ()));
        }
 
+       if (_analysis_gain) {
+               root->add_child("AnalysisGain")->add_child_text (raw_convert<string> (_analysis_gain.get ()));
+       }
+
        doc->write_to_file_formatted (filename.string ());
 }
index 2411b43..478b0e5 100644 (file)
@@ -75,12 +75,25 @@ public:
                return _peak_time;
        }
 
+       boost::optional<double> analysis_gain () const {
+               return _analysis_gain;
+       }
+
+       void set_analysis_gain (double gain) {
+               _analysis_gain = gain;
+       }
+
        void write (boost::filesystem::path);
 
 private:
        std::vector<std::vector<AudioPoint> > _data;
        boost::optional<float> _peak;
        boost::optional<DCPTime> _peak_time;
+       /** If this analysis was run on a single piece of
+        *  content we store its gain in dB when the analysis
+        *  happened.
+        */
+       boost::optional<double> _analysis_gain;
 };
 
 #endif
index 5310ef7..e19b309 100644 (file)
@@ -241,7 +241,15 @@ Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
 
                digester.add (ac->digest ());
                digester.add (ac->audio_mapping().digest ());
-               digester.add (ac->audio_gain ());
+               if (playlist->content().size() != 1) {
+                       /* Analyses should be considered equal regardless of gain
+                          if they were made from just one piece of content.  This
+                          is because we can fake any gain change in a single-content
+                          analysis at the plotting stage rather than having to
+                          recompute it.
+                       */
+                       digester.add (ac->audio_gain ());
+               }
        }
 
        if (audio_processor ()) {
index cd59693..17d50ef 100644 (file)
 #include "lib/job_manager.h"
 #include <boost/filesystem.hpp>
 
+using std::cout;
 using boost::shared_ptr;
 using boost::bind;
 using boost::optional;
 using boost::const_pointer_cast;
+using boost::dynamic_pointer_cast;
 
 /** @param content Content to analyse, or 0 to analyse all of the film's audio */
 AudioDialog::AudioDialog (wxWindow* parent, shared_ptr<Film> film, shared_ptr<AudioContent> content)
@@ -109,7 +111,7 @@ AudioDialog::AudioDialog (wxWindow* parent, shared_ptr<Film> film, shared_ptr<Au
        overall_sizer->Layout ();
        overall_sizer->SetSizeHints (this);
 
-       _film_connection = film->ContentChanged.connect (boost::bind (&AudioDialog::try_to_load_analysis, this));
+       _film_connection = film->ContentChanged.connect (boost::bind (&AudioDialog::content_changed, this, _2));
        SetTitle (_("DCP-o-matic audio"));
 
        if (content) {
@@ -152,6 +154,7 @@ AudioDialog::try_to_load_analysis ()
         }
 
        _plot->set_analysis (_analysis);
+       _plot->set_gain_correction (gain_correction ());
        setup_peak_time ();
 
        /* Set up some defaults if no check boxes are checked */
@@ -214,8 +217,18 @@ AudioDialog::channel_clicked (wxCommandEvent& ev)
 void
 AudioDialog::content_changed (int p)
 {
-       if (p == AudioContentProperty::AUDIO_GAIN || p == AudioContentProperty::AUDIO_STREAMS) {
+       if (p == AudioContentProperty::AUDIO_STREAMS) {
                try_to_load_analysis ();
+       } else if (p == AudioContentProperty::AUDIO_GAIN) {
+               if (_playlist->content().size() == 1) {
+                       /* We can use a short-cut to render the effect of this
+                          change, rather than recalculating everything.
+                       */
+                       _plot->set_gain_correction (gain_correction ());
+                       setup_peak_time ();
+               } else {
+                       try_to_load_analysis ();
+               }
        }
 }
 
@@ -250,7 +263,7 @@ AudioDialog::setup_peak_time ()
                return;
        }
 
-       float peak_dB = 20 * log10 (_analysis->peak().get());
+       float const peak_dB = 20 * log10 (_analysis->peak().get()) + gain_correction ();
 
        _peak_time->SetLabel (
                wxString::Format (
@@ -274,3 +287,22 @@ AudioDialog::Show (bool show)
        try_to_load_analysis ();
        return r;
 }
+
+/** @return gain correction in dB required to be added to raw gain values to render
+ *  the dialog correctly.
+ */
+float
+AudioDialog::gain_correction ()
+{
+       if (_playlist->content().size() == 1 && _analysis->analysis_gain ()) {
+               /* In this case we know that the analysis was of a single piece of content and
+                  we know that content's gain when the analysis was run.  Hence we can work out
+                  what correction is now needed to make it look `right'.
+               */
+               shared_ptr<const AudioContent> ac = dynamic_pointer_cast<const AudioContent> (_playlist->content().front ());
+               DCPOMATIC_ASSERT (ac);
+               return ac->audio_gain() - _analysis->analysis_gain().get ();
+       }
+
+       return 0.0f;
+}
index c992611..945ff01 100644 (file)
@@ -42,6 +42,7 @@ private:
        void try_to_load_analysis ();
        void analysis_finished ();
        void setup_peak_time ();
+       float gain_correction ();
 
        boost::shared_ptr<AudioAnalysis> _analysis;
        boost::weak_ptr<Film> _film;
index 1c5c1f9..605738b 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2015 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
 
 */
 
-#include <iostream>
-#include <boost/bind.hpp>
-#include <wx/graphics.h>
 #include "audio_plot.h"
 #include "lib/audio_decoder.h"
 #include "lib/audio_analysis.h"
 #include "wx/wx_util.h"
+#include <wx/graphics.h>
+#include <boost/bind.hpp>
+#include <iostream>
 
 using std::cout;
 using std::vector;
@@ -39,6 +39,7 @@ int const AudioPlot::max_smoothing = 128;
 AudioPlot::AudioPlot (wxWindow* parent)
        : wxPanel (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
        , _smoothing (max_smoothing / 2)
+       , _gain_correction (0)
 {
 #ifndef __WXOSX__
        SetDoubleBuffered (true);
@@ -214,12 +215,12 @@ AudioPlot::plot_peak (wxGraphicsPath& path, int channel, Metrics const & metrics
                return;
        }
 
-       path.MoveToPoint (metrics.db_label_width, y_for_linear (_analysis->get_point(channel, 0)[AudioPoint::PEAK], metrics));
+       path.MoveToPoint (metrics.db_label_width, y_for_linear (get_point(channel, 0)[AudioPoint::PEAK], metrics));
 
        float peak = 0;
        int const N = _analysis->points(channel);
        for (int i = 0; i < N; ++i) {
-               float const p = _analysis->get_point(channel, i)[AudioPoint::PEAK];
+               float const p = get_point(channel, i)[AudioPoint::PEAK];
                peak -= 0.01f * (1 - log10 (_smoothing) / log10 (max_smoothing));
                if (p > peak) {
                        peak = p;
@@ -238,14 +239,14 @@ AudioPlot::plot_rms (wxGraphicsPath& path, int channel, Metrics const & metrics)
                return;
        }
 
-       path.MoveToPoint (metrics.db_label_width, y_for_linear (_analysis->get_point(channel, 0)[AudioPoint::RMS], metrics));
+       path.MoveToPoint (metrics.db_label_width, y_for_linear (get_point(channel, 0)[AudioPoint::RMS], metrics));
 
        list<float> smoothing;
 
        int const N = _analysis->points(channel);
 
-       float const first = _analysis->get_point(channel, 0)[AudioPoint::RMS];
-       float const last = _analysis->get_point(channel, N - 1)[AudioPoint::RMS];
+       float const first = get_point(channel, 0)[AudioPoint::RMS];
+       float const last = get_point(channel, N - 1)[AudioPoint::RMS];
 
        int const before = _smoothing / 2;
        int const after = _smoothing - before;
@@ -256,7 +257,7 @@ AudioPlot::plot_rms (wxGraphicsPath& path, int channel, Metrics const & metrics)
        }
        for (int i = 0; i < after; ++i) {
                if (i < N) {
-                       smoothing.push_back (_analysis->get_point(channel, i)[AudioPoint::RMS]);
+                       smoothing.push_back (get_point(channel, i)[AudioPoint::RMS]);
                } else {
                        smoothing.push_back (last);
                }
@@ -267,7 +268,7 @@ AudioPlot::plot_rms (wxGraphicsPath& path, int channel, Metrics const & metrics)
                int const next_for_window = i + after;
 
                if (next_for_window < N) {
-                       smoothing.push_back (_analysis->get_point(channel, i)[AudioPoint::RMS]);
+                       smoothing.push_back (get_point(channel, i)[AudioPoint::RMS]);
                } else {
                        smoothing.push_back (last);
                }
@@ -293,3 +294,21 @@ AudioPlot::set_smoothing (int s)
        _smoothing = s;
        Refresh ();
 }
+
+void
+AudioPlot::set_gain_correction (double gain)
+{
+       _gain_correction = gain;
+       Refresh ();
+}
+
+AudioPoint
+AudioPlot::get_point (int channel, int point) const
+{
+       AudioPoint p = _analysis->get_point (channel, point);
+       for (int i = 0; i < AudioPoint::COUNT; ++i) {
+               p[i] *= pow (10, _gain_correction / 20);
+       }
+
+       return p;
+}
index 846ac22..2027b65 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2015 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
@@ -35,6 +35,7 @@ public:
        void set_type_visible (int t, bool v);
        void set_smoothing (int);
        void set_message (wxString);
+       void set_gain_correction (double gain);
 
        static const int max_smoothing;
 
@@ -43,14 +44,15 @@ private:
        void plot_peak (wxGraphicsPath &, int, Metrics const &) const;
        void plot_rms (wxGraphicsPath &, int, Metrics const &) const;
        float y_for_linear (float, Metrics const &) const;
+       AudioPoint get_point (int channel, int point) const;
 
        boost::shared_ptr<AudioAnalysis> _analysis;
        bool _channel_visible[MAX_DCP_AUDIO_CHANNELS];
        bool _type_visible[AudioPoint::COUNT];
        int _smoothing;
        std::vector<wxColour> _colours;
-
        wxString _message;
+       float _gain_correction;
 
        static const int _minimum;
 };