7fd73c0cf1f723896826c77fec3720c5c404d4e8 from master; tidy audio analysis dialogue...
authorCarl Hetherington <cth@carlh.net>
Sat, 9 May 2015 23:15:44 +0000 (00:15 +0100)
committerCarl Hetherington <cth@carlh.net>
Sat, 9 May 2015 23:15:44 +0000 (00:15 +0100)
TO_PORT
src/lib/analyse_audio_job.cc
src/lib/analyse_audio_job.h
src/lib/audio_analysis.cc
src/lib/audio_analysis.h
src/wx/audio_dialog.cc
src/wx/audio_dialog.h
src/wx/audio_panel.cc
src/wx/film_viewer.cc
src/wx/wx_util.cc
src/wx/wx_util.h

diff --git a/TO_PORT b/TO_PORT
index 20d9c0951ee5eaf3933dd6066daa2eafab32c700..40b6d3cf5589febf6fae9443c9bcb4c7a8a1354e 100644 (file)
--- a/TO_PORT
+++ b/TO_PORT
@@ -1,4 +1,3 @@
-f248f57f745176349a5ac938842216954aab477e
 3eabb0b4eb8a2303ce4f7aa5ed2fd77645d803a8
 07d0be5e167b326caefb8e9981d99faf3823b15b
 0b3fb0ba4c841de950e6f62ddd3992925ca781c6
index 079fe884e762f76980f1b08799f659363ed9ef8b..cdf6238767e21bc7ba0931a04edaf52d75de1277 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
@@ -39,6 +39,8 @@ AnalyseAudioJob::AnalyseAudioJob (shared_ptr<const Film> f, shared_ptr<AudioCont
        , _content (c)
        , _done (0)
        , _samples_per_point (1)
+       , _overall_peak (0)
+       , _overall_peak_frame (0)
 {
 
 }
@@ -81,6 +83,7 @@ AnalyseAudioJob::run ()
                set_progress (t.seconds() / _film->length().seconds());
        }
 
+       _analysis->set_peak (_overall_peak, DCPTime::from_frames (_overall_peak_frame, _film->audio_frame_rate ()));
        _analysis->write (content->audio_analysis_path ());
        
        set_progress (1);
@@ -101,6 +104,15 @@ AnalyseAudioJob::analyse (shared_ptr<const AudioBuffers> b)
                        _current[j][AudioPoint::RMS] += pow (s, 2);
                        _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], fabsf (s));
 
+                       float const as = fabs (s);
+
+                       _current[j][AudioPoint::PEAK] = max (_current[j][AudioPoint::PEAK], as);
+
+                       if (as > _overall_peak) {
+                               _overall_peak = as;
+                               _overall_peak_frame = _done + i;
+                       }
+
                        if ((_done % _samples_per_point) == 0) {
                                _current[j][AudioPoint::RMS] = sqrt (_current[j][AudioPoint::RMS] / _samples_per_point);
                                _analysis->add_point (j, _current[j]);
index 6f64dd27248c13f029aab6257c35181f57ad972b..0f9605eedddba6c386fc472dafd4a644cda5b5bc 100644 (file)
@@ -52,6 +52,9 @@ private:
        int64_t _samples_per_point;
        std::vector<AudioPoint> _current;
 
+       float _overall_peak;
+       AudioFrame _overall_peak_frame;
+
        boost::shared_ptr<AudioAnalysis> _analysis;
 
        static const int _num_points;
index 19a0d876ebc75ab962de69a5408ef42abaa640a0..ee34b0d80b7fc2d3168f557e3947c68f85c995c9 100644 (file)
@@ -22,6 +22,7 @@
 #include "cross.h"
 #include <boost/filesystem.hpp>
 #include <stdint.h>
+#include <inttypes.h>
 #include <cmath>
 #include <cassert>
 #include <cstdio>
@@ -115,6 +116,17 @@ AudioAnalysis::AudioAnalysis (boost::filesystem::path filename)
                }
        }
 
+       /* These may not exist in old analysis files, so be careful
+          about reading them.
+       */
+       
+       float peak;
+       DCPTime::Type peak_time;
+       if (fscanf (f, "%f%" SCNd64, &peak, &peak_time) == 2) {
+               _peak = peak;
+               _peak_time = DCPTime (peak_time);
+       }
+       
        fclose (f);
 }
 
@@ -164,6 +176,10 @@ AudioAnalysis::write (boost::filesystem::path filename)
                }
        }
 
+       if (_peak) {
+               fprintf (f, "%f%" PRId64, _peak.get (), _peak_time.get().get ());
+       }
+
        fclose (f);
        boost::filesystem::rename (tmp, filename);
 }
index 865d647816e00ebcb6a8dac6c617db936ddd2329..1872c57ad3c2207060c41d54378305c735492a4c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2014 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
@@ -24,7 +24,9 @@
 #ifndef DCPOMATIC_AUDIO_ANALYSIS_H
 #define DCPOMATIC_AUDIO_ANALYSIS_H
 
+#include "types.h"
 #include <boost/filesystem.hpp>
+#include <boost/optional.hpp>
 #include <vector>
 
 /** @class AudioPoint
@@ -69,15 +71,29 @@ public:
        AudioAnalysis (boost::filesystem::path);
 
        void add_point (int c, AudioPoint const & p);
+       void set_peak (float peak, DCPTime time) {
+               _peak = peak;
+               _peak_time = time;
+       }
        
        AudioPoint get_point (int c, int p) const;
        int points (int c) const;
        int channels () const;
 
+       boost::optional<float> peak () const {
+               return _peak;
+       }
+
+       boost::optional<DCPTime> peak_time () const {
+               return _peak_time;
+       }
+
        void write (boost::filesystem::path);
 
 private:
        std::vector<std::vector<AudioPoint> > _data;
+       boost::optional<float> _peak;
+       boost::optional<DCPTime> _peak_time;
 };
 
 #endif
index 1d41fc1854acf23647b9d2fbdf0c85761cded4a4..1ce0f38a8c9b0e5d71411c740db8ef5ed29b6d9c 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
@@ -29,31 +29,43 @@ using boost::shared_ptr;
 using boost::bind;
 using boost::optional;
 
-AudioDialog::AudioDialog (wxWindow* parent)
+AudioDialog::AudioDialog (wxWindow* parent, shared_ptr<Film> film)
        : wxDialog (parent, wxID_ANY, _("Audio"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
+       , _film (film)
        , _plot (0)
 {
+       wxFont subheading_font (*wxNORMAL_FONT);
+       subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
+
        wxBoxSizer* sizer = new wxBoxSizer (wxHORIZONTAL);
+       
+       wxBoxSizer* left = new wxBoxSizer (wxVERTICAL);
 
        _plot = new AudioPlot (this);
-       sizer->Add (_plot, 1, wxALL | wxEXPAND, 12);
+       left->Add (_plot, 1, wxALL | wxEXPAND, 12);
+       _peak_time = new wxStaticText (this, wxID_ANY, wxT (""));
+       left->Add (_peak_time, 0, wxALL, 12);
+
+       sizer->Add (left, 1, wxALL, 12);
 
-       wxBoxSizer* side = new wxBoxSizer (wxVERTICAL);
+       wxBoxSizer* right = new wxBoxSizer (wxVERTICAL);
 
        {
                wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Channels"));
-               side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
+               m->SetFont (subheading_font);
+               right->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, 16);
        }
 
        for (int i = 0; i < MAX_DCP_AUDIO_CHANNELS; ++i) {
                _channel_checkbox[i] = new wxCheckBox (this, wxID_ANY, std_to_wx (audio_channel_name (i)));
-               side->Add (_channel_checkbox[i], 1, wxEXPAND | wxALL, 3);
+               right->Add (_channel_checkbox[i], 0, wxEXPAND | wxALL, 3);
                _channel_checkbox[i]->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AudioDialog::channel_clicked, this, _1));
        }
 
        {
                wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Type"));
-               side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
+               m->SetFont (subheading_font);
+               right->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
        }
        
        wxString const types[] = {
@@ -63,20 +75,21 @@ AudioDialog::AudioDialog (wxWindow* parent)
 
        for (int i = 0; i < AudioPoint::COUNT; ++i) {
                _type_checkbox[i] = new wxCheckBox (this, wxID_ANY, types[i]);
-               side->Add (_type_checkbox[i], 1, wxEXPAND | wxALL, 3);
+               right->Add (_type_checkbox[i], 0, wxEXPAND | wxALL, 3);
                _type_checkbox[i]->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AudioDialog::type_clicked, this, _1));
        }
 
        {
                wxStaticText* m = new wxStaticText (this, wxID_ANY, _("Smoothing"));
-               side->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
+               m->SetFont (subheading_font);
+               right->Add (m, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 16);
        }
        
        _smoothing = new wxSlider (this, wxID_ANY, AudioPlot::max_smoothing / 2, 1, AudioPlot::max_smoothing);
        _smoothing->Bind (wxEVT_SCROLL_THUMBTRACK, boost::bind (&AudioDialog::smoothing_changed, this));
-       side->Add (_smoothing, 1, wxEXPAND);
+       right->Add (_smoothing, 0, wxEXPAND);
 
-       sizer->Add (side, 0, wxALL, 12);
+       sizer->Add (right, 0, wxALL, 12);
 
        SetSizer (sizer);
        sizer->Layout ();
@@ -107,14 +120,14 @@ AudioDialog::try_to_load_analysis ()
 
        if (!boost::filesystem::exists (_content->audio_analysis_path())) {
                _plot->set_analysis (shared_ptr<AudioAnalysis> ());
+               _analysis.reset ();
                _analysis_finished_connection = _content->analyse_audio (bind (&AudioDialog::analysis_finished, this));
                return;
        }
        
-       shared_ptr<AudioAnalysis> a;
-       
-       a.reset (new AudioAnalysis (_content->audio_analysis_path ()));
-       _plot->set_analysis (a);
+       _analysis.reset (new AudioAnalysis (_content->audio_analysis_path ()));
+       _plot->set_analysis (_analysis);
+       setup_peak_time ();
 
        /* Set up some defaults if no check boxes are checked */
        
@@ -173,6 +186,7 @@ AudioDialog::content_changed (int p)
 {
        if (p == AudioContentProperty::AUDIO_GAIN) {
                _plot->set_gain (_content->audio_gain ());
+               setup_peak_time ();
        } else if (p == AudioContentProperty::AUDIO_MAPPING) {
                try_to_load_analysis ();
        }
@@ -196,3 +210,32 @@ AudioDialog::smoothing_changed ()
 {
        _plot->set_smoothing (_smoothing->GetValue ());
 }
+
+void
+AudioDialog::setup_peak_time ()
+{
+       if (!_analysis || !_analysis->peak ()) {
+               return;
+       }
+       
+       shared_ptr<Film> film = _film.lock ();
+       if (!film) {
+               return;
+       }
+       
+       float peak_dB = 20 * log10 (_analysis->peak().get()) + _content->audio_gain();
+       
+       _peak_time->SetLabel (
+               wxString::Format (
+                       _("Peak is %.2fdB at %s"),
+                       peak_dB,
+                       time_to_timecode (_analysis->peak_time().get(), film->video_frame_rate ()).data ()
+                       )
+               );
+       
+       if (peak_dB > -3) {
+               _peak_time->SetForegroundColour (wxColour (255, 0, 0));
+       } else {
+               _peak_time->SetForegroundColour (wxColour (0, 0, 0));
+       }
+}
index b277852928d19f53e3849464a0541d24c65288c3..aef8ea944c86707bbb81f01461448d01f48b5b20 100644 (file)
@@ -29,7 +29,7 @@ class Film;
 class AudioDialog : public wxDialog
 {
 public:
-       AudioDialog (wxWindow *);
+       AudioDialog (wxWindow *, boost::shared_ptr<Film> film);
 
        void set_content (boost::shared_ptr<AudioContent>);
 
@@ -40,9 +40,13 @@ private:
        void smoothing_changed ();
        void try_to_load_analysis ();
        void analysis_finished ();
+       void setup_peak_time ();
 
        boost::shared_ptr<AudioContent> _content;
+       boost::shared_ptr<AudioAnalysis> _analysis;
+       boost::weak_ptr<Film> _film;
        AudioPlot* _plot;
+       wxStaticText* _peak_time;
        wxCheckBox* _channel_checkbox[MAX_DCP_AUDIO_CHANNELS];
        wxCheckBox* _type_checkbox[AudioPoint::COUNT];
        wxSlider* _smoothing;
index 2a41aeb2def0543b497bd3879d2ee643af24f90f..4d783ca9da71b9e6f5ec69c2b87fb7afaf8ee6e2 100644 (file)
@@ -217,7 +217,7 @@ AudioPanel::show_clicked ()
                return;
        }
        
-       _audio_dialog = new AudioDialog (this);
+       _audio_dialog = new AudioDialog (this, _parent->film ());
        _audio_dialog->Show ();
        _audio_dialog->set_content (ac.front ());
 }
index 311ec734c45734aff0a5dab1295a8fc5e3178a9c..0938d52a4cdd094af5cfe0255addf9376e5b52e1 100644 (file)
@@ -354,16 +354,7 @@ FilmViewer::set_position_text ()
        double const fps = _film->video_frame_rate ();
        /* Count frame number from 1 ... not sure if this is the best idea */
        _frame_number->SetLabel (wxString::Format (wxT("%d"), int (rint (_position.seconds() * fps)) + 1));
-       
-       double w = _position.seconds ();
-       int const h = (w / 3600);
-       w -= h * 3600;
-       int const m = (w / 60);
-       w -= m * 60;
-       int const s = floor (w);
-       w -= s;
-       int const f = rint (w * fps);
-       _timecode->SetLabel (wxString::Format (wxT("%02d:%02d:%02d.%02d"), h, m, s, f));
+       _timecode->SetLabel (time_to_timecode (_position, fps));
 }
 
 void
index e9c07c91fe7bf2a566532f328ed7681d000e1d8b..9c13cfbcb69e741cbff7f8d8e42c8804bf62494e 100644 (file)
@@ -330,3 +330,17 @@ context_translation (wxString s)
 
        return t;
 }
+
+wxString
+time_to_timecode (DCPTime t, float fps)
+{
+       double w = t.seconds ();
+       int const h = (w / 3600);
+       w -= h * 3600;
+       int const m = (w / 60);
+       w -= m * 60;
+       int const s = floor (w);
+       w -= s;
+       int const f = rint (w * fps);
+       return wxString::Format (wxT("%02d:%02d:%02d.%02d"), h, m, s, f);
+}
index dfa0fca5e4c6e1d1744a7b675ad5bc9a93a88f15..f2ab2d8c599311573da9013204f49b2424dc725e 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef DCPOMATIC_WX_UTIL_H
 #define DCPOMATIC_WX_UTIL_H
 
+#include "lib/dcpomatic_time.h"
 #include <wx/wx.h>
 #include <wx/gbsizer.h>
 #include <boost/function.hpp>
@@ -65,6 +66,7 @@ extern wxString std_to_wx (std::string);
 extern void dcpomatic_setup_i18n ();
 extern wxString context_translation (wxString);
 extern std::string string_client_data (wxClientData* o);
+extern wxString time_to_timecode (DCPTime t, float fps);
 
 extern void checked_set (wxFilePickerCtrl* widget, std::string value);
 extern void checked_set (wxSpinCtrl* widget, int value);