Use make_shared<>.
[dcpomatic.git] / src / lib / audio_analysis.cc
1 /*
2     Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
6     DCP-o-matic is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     DCP-o-matic is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
18
19 */
20
21 #include "audio_analysis.h"
22 #include "cross.h"
23 #include "util.h"
24 #include "raw_convert.h"
25 #include "playlist.h"
26 #include "audio_content.h"
27 #include <libxml++/libxml++.h>
28 #include <boost/filesystem.hpp>
29 #include <boost/foreach.hpp>
30 #include <boost/make_shared.hpp>
31 #include <stdint.h>
32 #include <cmath>
33 #include <cstdio>
34 #include <iostream>
35 #include <inttypes.h>
36
37 using std::ostream;
38 using std::istream;
39 using std::string;
40 using std::vector;
41 using std::cout;
42 using std::max;
43 using std::list;
44 using boost::shared_ptr;
45 using boost::make_shared;
46 using boost::dynamic_pointer_cast;
47
48 AudioAnalysis::AudioAnalysis (int channels)
49 {
50         _data.resize (channels);
51 }
52
53 AudioAnalysis::AudioAnalysis (boost::filesystem::path filename)
54 {
55         cxml::Document f ("AudioAnalysis");
56         f.read_file (filename);
57
58         BOOST_FOREACH (cxml::NodePtr i, f.node_children ("Channel")) {
59                 vector<AudioPoint> channel;
60
61                 BOOST_FOREACH (cxml::NodePtr j, i->node_children ("Point")) {
62                         channel.push_back (AudioPoint (j));
63                 }
64
65                 _data.push_back (channel);
66         }
67
68         _sample_peak = f.optional_number_child<float> ("Peak");
69         if (!_sample_peak) {
70                 /* New key */
71                 _sample_peak = f.optional_number_child<float> ("SamplePeak");
72         }
73
74         if (f.optional_number_child<DCPTime::Type> ("PeakTime")) {
75                 _sample_peak_time = DCPTime (f.number_child<DCPTime::Type> ("PeakTime"));
76         } else if (f.optional_number_child<DCPTime::Type> ("SamplePeakTime")) {
77                 _sample_peak_time = DCPTime (f.number_child<DCPTime::Type> ("SamplePeakTime"));
78         }
79
80         _true_peak = f.optional_number_child<float> ("TruePeak");
81         _integrated_loudness = f.optional_number_child<float> ("IntegratedLoudness");
82         _loudness_range = f.optional_number_child<float> ("LoudnessRange");
83
84         _analysis_gain = f.optional_number_child<double> ("AnalysisGain");
85 }
86
87 void
88 AudioAnalysis::add_point (int c, AudioPoint const & p)
89 {
90         DCPOMATIC_ASSERT (c < channels ());
91         _data[c].push_back (p);
92 }
93
94 AudioPoint
95 AudioAnalysis::get_point (int c, int p) const
96 {
97         DCPOMATIC_ASSERT (p < points (c));
98         return _data[c][p];
99 }
100
101 int
102 AudioAnalysis::channels () const
103 {
104         return _data.size ();
105 }
106
107 int
108 AudioAnalysis::points (int c) const
109 {
110         DCPOMATIC_ASSERT (c < channels ());
111         return _data[c].size ();
112 }
113
114 void
115 AudioAnalysis::write (boost::filesystem::path filename)
116 {
117         shared_ptr<xmlpp::Document> doc = make_shared<xmlpp::Document> ();
118         xmlpp::Element* root = doc->create_root_node ("AudioAnalysis");
119
120         BOOST_FOREACH (vector<AudioPoint>& i, _data) {
121                 xmlpp::Element* channel = root->add_child ("Channel");
122                 BOOST_FOREACH (AudioPoint& j, i) {
123                         j.as_xml (channel->add_child ("Point"));
124                 }
125         }
126
127         if (_sample_peak) {
128                 root->add_child("SamplePeak")->add_child_text (raw_convert<string> (_sample_peak.get ()));
129                 root->add_child("SamplePeakTime")->add_child_text (raw_convert<string> (_sample_peak_time.get().get ()));
130         }
131
132         if (_true_peak) {
133                 root->add_child("TruePeak")->add_child_text (raw_convert<string> (_true_peak.get ()));
134         }
135
136         if (_integrated_loudness) {
137                 root->add_child("IntegratedLoudness")->add_child_text (raw_convert<string> (_integrated_loudness.get ()));
138         }
139
140         if (_loudness_range) {
141                 root->add_child("LoudnessRange")->add_child_text (raw_convert<string> (_loudness_range.get ()));
142         }
143
144         if (_analysis_gain) {
145                 root->add_child("AnalysisGain")->add_child_text (raw_convert<string> (_analysis_gain.get ()));
146         }
147
148         doc->write_to_file_formatted (filename.string ());
149 }
150
151 float
152 AudioAnalysis::gain_correction (shared_ptr<const Playlist> playlist)
153 {
154         if (playlist->content().size() == 1 && analysis_gain ()) {
155                 /* In this case we know that the analysis was of a single piece of content and
156                    we know that content's gain when the analysis was run.  Hence we can work out
157                    what correction is now needed to make it look `right'.
158                 */
159                 DCPOMATIC_ASSERT (playlist->content().front()->audio);
160                 return playlist->content().front()->audio->gain() - analysis_gain().get ();
161         }
162
163         return 0.0f;
164 }