}
_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);
_peak = f.number_child<float> ("Peak");
_peak_time = DCPTime (f.number_child<DCPTime::Type> ("PeakTime"));
+ _analysis_gain = f.optional_number_child<double> ("AnalysisGain");
}
void
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 ());
}
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
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 ()) {
#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)
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) {
}
_plot->set_analysis (_analysis);
+ _plot->set_gain_correction (gain_correction ());
setup_peak_time ();
/* Set up some defaults if no check boxes are checked */
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 ();
+ }
}
}
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 (
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;
+}
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;
/*
- 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;
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);
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;
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;
}
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);
}
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);
}
_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;
+}
/*
- 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
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;
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;
};