Add time axis to audio analysis (part of #1082).
authorCarl Hetherington <cth@carlh.net>
Mon, 26 Mar 2018 15:26:32 +0000 (16:26 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 26 Mar 2018 15:26:32 +0000 (16:26 +0100)
ChangeLog
src/lib/analyse_audio_job.cc
src/lib/audio_analysis.cc
src/lib/audio_analysis.h
src/wx/audio_plot.cc
src/wx/timeline_time_axis_view.cc
src/wx/wx_util.cc
src/wx/wx_util.h

index e44b683055fd6e6978b73ea0822650fd0d619496..60ab10a1010a91b75ae3bed8a5f75a412ef54d70 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2018-03-26  Carl Hetherington  <cth@carlh.net>
+
+       * Add time axis to audio analysis (part of #1082).
+
+       * Note: from this point in the ChangeLog release versions
+       are marked using git tags and not written to the ChangeLog.
+
 2018-03-25  Carl Hetherington  <cth@carlh.net>
 
        * Version 2.13.2 released.
index b96c7047b727007b8f5799c9b9da2e4dd4c83e9c..db917301aaac01d9f37a111f1126f6998f862e50 100644 (file)
@@ -163,6 +163,8 @@ AnalyseAudioJob::run ()
                _analysis->set_analysis_gain (ac->gain ());
        }
 
+       _analysis->set_samples_per_point (_samples_per_point);
+       _analysis->set_sample_rate (_film->audio_frame_rate ());
        _analysis->write (_film->audio_analysis_path (_playlist));
 
        set_progress (1);
index 61cdd5fcce2b61a2d9d121038d4f86b6060b1281..b58a3127d2d5a7af0d9dc8b53638100772b88453 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2016 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -90,6 +90,8 @@ AudioAnalysis::AudioAnalysis (boost::filesystem::path filename)
        _loudness_range = f.optional_number_child<float> ("LoudnessRange");
 
        _analysis_gain = f.optional_number_child<double> ("AnalysisGain");
+       _samples_per_point = f.optional_number_child<int64_t> ("SamplesPerPoint");
+       _sample_rate = f.optional_number_child<int64_t> ("SampleRate");
 }
 
 void
@@ -156,6 +158,14 @@ AudioAnalysis::write (boost::filesystem::path filename)
                root->add_child("AnalysisGain")->add_child_text (raw_convert<string> (_analysis_gain.get ()));
        }
 
+       if (_samples_per_point) {
+               root->add_child("SamplesPerPoint")->add_child_text (raw_convert<string> (_samples_per_point.get()));
+       }
+
+       if (_sample_rate) {
+               root->add_child("SampleRate")->add_child_text (raw_convert<string> (_sample_rate.get()));
+       }
+
        doc->write_to_file_formatted (filename.string ());
 }
 
index 6da651c95a06bf670719222052a307b43a160b25..0a5e6194dfccf13f396cd8e02f9f1e08715ed1a3 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -100,6 +100,22 @@ public:
                _analysis_gain = gain;
        }
 
+       boost::optional<int64_t> samples_per_point () const {
+               return _samples_per_point;
+       }
+
+       void set_samples_per_point (int64_t spp) {
+               _samples_per_point = spp;
+       }
+
+       boost::optional<int> sample_rate () const {
+               return _sample_rate;
+       }
+
+       void set_sample_rate (int sr) {
+               _sample_rate = sr;
+       }
+
        void write (boost::filesystem::path);
 
        float gain_correction (boost::shared_ptr<const Playlist> playlist);
@@ -115,6 +131,8 @@ private:
         *  happened.
         */
        boost::optional<double> _analysis_gain;
+       boost::optional<int64_t> _samples_per_point;
+       boost::optional<int> _sample_rate;
 
        static int const _current_state_version;
 };
index fe031aaac28aa5b8d095d6241206b74f0bbc4fb9..493d99f5e7d2e5ebdc95e6e25cf777849247888f 100644 (file)
@@ -122,7 +122,7 @@ struct Metrics
        double db_label_width;
        int height;
        int y_origin;
-       float x_scale;
+       float x_scale; ///< pixels per data point
        float y_scale;
 };
 
@@ -142,7 +142,7 @@ AudioPlot::paint ()
                return;
        }
 
-       wxGraphicsPath grid = gc->CreatePath ();
+       wxGraphicsPath h_grid = gc->CreatePath ();
        gc->SetFont (gc->CreateFont (*wxSMALL_FONT));
        wxDouble db_label_height;
        wxDouble db_label_descent;
@@ -161,15 +161,59 @@ AudioPlot::paint ()
 
        for (int i = _minimum; i <= 0; i += 10) {
                int const y = (metrics.height - (i - _minimum) * metrics.y_scale) - metrics.y_origin;
-               grid.MoveToPoint (metrics.db_label_width - 4, y);
-               grid.AddLineToPoint (metrics.db_label_width + data_width, y);
+               h_grid.MoveToPoint (metrics.db_label_width - 4, y);
+               h_grid.AddLineToPoint (metrics.db_label_width + data_width, y);
                gc->DrawText (std_to_wx (String::compose ("%1dB", i)), 0, y - (db_label_height / 2));
        }
 
        gc->SetPen (wxPen (wxColour (200, 200, 200)));
-       gc->StrokePath (grid);
+       gc->StrokePath (h_grid);
 
-       gc->DrawText (_("Time"), data_width, metrics.height - metrics.y_origin + db_label_height / 2);
+       if (_analysis->samples_per_point() && _analysis->sample_rate()) {
+
+               /* Draw an x axis with marks */
+
+               wxGraphicsPath v_grid = gc->CreatePath ();
+
+               DCPOMATIC_ASSERT (_analysis->samples_per_point().get() != 0.0);
+               double const pps = _analysis->sample_rate().get() * metrics.x_scale / _analysis->samples_per_point().get();
+
+               gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
+
+               double const mark_interval = calculate_mark_interval (rint (128 / pps));
+
+               DCPTime t = DCPTime::from_seconds (mark_interval);
+               while ((t.seconds() * pps) < data_width) {
+                       double tc = t.seconds ();
+                       int const h = tc / 3600;
+                       tc -= h * 3600;
+                       int const m = tc / 60;
+                       tc -= m * 60;
+                       int const s = tc;
+
+                       wxString str = wxString::Format (wxT ("%02d:%02d:%02d"), h, m, s);
+                       wxDouble str_width;
+                       wxDouble str_height;
+                       wxDouble str_descent;
+                       wxDouble str_leading;
+                       gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading);
+
+                       int const tx = llrintf (metrics.db_label_width + t.seconds() * pps);
+                       gc->DrawText (str, tx - str_width / 2, metrics.height - metrics.y_origin + db_label_height);
+
+                       v_grid.MoveToPoint (tx, metrics.height - metrics.y_origin + 4);
+                       v_grid.AddLineToPoint (tx, metrics.y_origin);
+
+                       t += DCPTime::from_seconds (mark_interval);
+               }
+
+               gc->SetPen (wxPen (wxColour (200, 200, 200)));
+               gc->StrokePath (v_grid);
+
+       } else {
+               /* This is an old analysis without samples_per_point information */
+               gc->DrawText (_("Time"), data_width, metrics.height - metrics.y_origin + db_label_height / 2);
+       }
 
        if (_type_visible[AudioPoint::PEAK]) {
                for (int c = 0; c < MAX_DCP_AUDIO_CHANNELS; ++c) {
index 45bf7e7cf51667e70031197df63e8690c824b890..1947faa0bfcb4c694a8a5720a606458eb33939e3 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "timeline_time_axis_view.h"
 #include "timeline.h"
+#include "wx_util.h"
 #include <wx/wx.h>
 #include <wx/graphics.h>
 
@@ -56,23 +57,7 @@ TimelineTimeAxisView::do_paint (wxGraphicsContext* gc, list<dcpomatic::Rect<int>
 
        gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
 
-       double mark_interval = rint (128 / pps);
-       if (mark_interval > 5) {
-               mark_interval -= lrint (mark_interval) % 5;
-       }
-       if (mark_interval > 10) {
-               mark_interval -= lrint (mark_interval) % 10;
-       }
-       if (mark_interval > 60) {
-               mark_interval -= lrint (mark_interval) % 60;
-       }
-       if (mark_interval > 3600) {
-               mark_interval -= lrint (mark_interval) % 3600;
-       }
-
-       if (mark_interval < 1) {
-               mark_interval = 1;
-       }
+       double const mark_interval = calculate_mark_interval (rint (128 / pps));
 
        wxGraphicsPath path = gc->CreatePath ();
        path.MoveToPoint (_timeline.tracks_position().x, _y);
index e017f404de8df788a2017923704145c703d17fda..4cd0d4bd793a074d893ae516822026ca205660a6 100644 (file)
@@ -414,3 +414,26 @@ path_from_file_dialog (wxFileDialog* dialog, string extension)
 {
        return boost::filesystem::path(wx_to_std(dialog->GetPath())).replace_extension(extension);
 }
+
+double
+calculate_mark_interval (double mark_interval)
+{
+       if (mark_interval > 5) {
+               mark_interval -= lrint (mark_interval) % 5;
+       }
+       if (mark_interval > 10) {
+               mark_interval -= lrint (mark_interval) % 10;
+       }
+       if (mark_interval > 60) {
+               mark_interval -= lrint (mark_interval) % 60;
+       }
+       if (mark_interval > 3600) {
+               mark_interval -= lrint (mark_interval) % 3600;
+       }
+
+       if (mark_interval < 1) {
+               mark_interval = 1;
+       }
+
+       return mark_interval;
+}
index e36a35e1e336e4c83ca0bee8354d1bc5b8b026d6..2691eccf471a602e3ee50d2301f0c88b40533978 100644 (file)
@@ -83,6 +83,7 @@ extern wxString time_to_timecode (DCPTime t, double fps);
 extern void setup_audio_channels_choice (wxChoice* choice, int minimum);
 extern wxSplashScreen* maybe_show_splash ();
 extern boost::filesystem::path path_from_file_dialog (wxFileDialog* dialog, std::string extension);
+extern double calculate_mark_interval (double start);
 
 extern void checked_set (FilePickerCtrl* widget, boost::filesystem::path value);
 extern void checked_set (wxSpinCtrl* widget, int value);