Revert "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 <stdint.h>
31 #include <cmath>
32 #include <cstdio>
33 #include <iostream>
34 #include <inttypes.h>
35
36 using std::ostream;
37 using std::istream;
38 using std::string;
39 using std::vector;
40 using std::cout;
41 using std::max;
42 using std::list;
43 using boost::shared_ptr;
44 using boost::dynamic_pointer_cast;
45
46 AudioAnalysis::AudioAnalysis (int channels)
47 {
48         _data.resize (channels);
49 }
50
51 AudioAnalysis::AudioAnalysis (boost::filesystem::path filename)
52 {
53         cxml::Document f ("AudioAnalysis");
54         f.read_file (filename);
55
56         BOOST_FOREACH (cxml::NodePtr i, f.node_children ("Channel")) {
57                 vector<AudioPoint> channel;
58
59                 BOOST_FOREACH (cxml::NodePtr j, i->node_children ("Point")) {
60                         channel.push_back (AudioPoint (j));
61                 }
62
63                 _data.push_back (channel);
64         }
65
66         _sample_peak = f.optional_number_child<float> ("Peak");
67         if (!_sample_peak) {
68                 /* New key */
69                 _sample_peak = f.optional_number_child<float> ("SamplePeak");
70         }
71
72         if (f.optional_number_child<DCPTime::Type> ("PeakTime")) {
73                 _sample_peak_time = DCPTime (f.number_child<DCPTime::Type> ("PeakTime"));
74         } else if (f.optional_number_child<DCPTime::Type> ("SamplePeakTime")) {
75                 _sample_peak_time = DCPTime (f.number_child<DCPTime::Type> ("SamplePeakTime"));
76         }
77
78         _true_peak = f.optional_number_child<float> ("TruePeak");
79         _integrated_loudness = f.optional_number_child<float> ("IntegratedLoudness");
80         _loudness_range = f.optional_number_child<float> ("LoudnessRange");
81
82         _analysis_gain = f.optional_number_child<double> ("AnalysisGain");
83 }
84
85 void
86 AudioAnalysis::add_point (int c, AudioPoint const & p)
87 {
88         DCPOMATIC_ASSERT (c < channels ());
89         _data[c].push_back (p);
90 }
91
92 AudioPoint
93 AudioAnalysis::get_point (int c, int p) const
94 {
95         DCPOMATIC_ASSERT (p < points (c));
96         return _data[c][p];
97 }
98
99 int
100 AudioAnalysis::channels () const
101 {
102         return _data.size ();
103 }
104
105 int
106 AudioAnalysis::points (int c) const
107 {
108         DCPOMATIC_ASSERT (c < channels ());
109         return _data[c].size ();
110 }
111
112 void
113 AudioAnalysis::write (boost::filesystem::path filename)
114 {
115         shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
116         xmlpp::Element* root = doc->create_root_node ("AudioAnalysis");
117
118         BOOST_FOREACH (vector<AudioPoint>& i, _data) {
119                 xmlpp::Element* channel = root->add_child ("Channel");
120                 BOOST_FOREACH (AudioPoint& j, i) {
121                         j.as_xml (channel->add_child ("Point"));
122                 }
123         }
124
125         if (_sample_peak) {
126                 root->add_child("SamplePeak")->add_child_text (raw_convert<string> (_sample_peak.get ()));
127                 root->add_child("SamplePeakTime")->add_child_text (raw_convert<string> (_sample_peak_time.get().get ()));
128         }
129
130         if (_true_peak) {
131                 root->add_child("TruePeak")->add_child_text (raw_convert<string> (_true_peak.get ()));
132         }
133
134         if (_integrated_loudness) {
135                 root->add_child("IntegratedLoudness")->add_child_text (raw_convert<string> (_integrated_loudness.get ()));
136         }
137
138         if (_loudness_range) {
139                 root->add_child("LoudnessRange")->add_child_text (raw_convert<string> (_loudness_range.get ()));
140         }
141
142         if (_analysis_gain) {
143                 root->add_child("AnalysisGain")->add_child_text (raw_convert<string> (_analysis_gain.get ()));
144         }
145
146         doc->write_to_file_formatted (filename.string ());
147 }
148
149 float
150 AudioAnalysis::gain_correction (shared_ptr<const Playlist> playlist)
151 {
152         if (playlist->content().size() == 1 && analysis_gain ()) {
153                 /* In this case we know that the analysis was of a single piece of content and
154                    we know that content's gain when the analysis was run.  Hence we can work out
155                    what correction is now needed to make it look `right'.
156                 */
157                 DCPOMATIC_ASSERT (playlist->content().front()->audio);
158                 return playlist->content().front()->audio->gain() - analysis_gain().get ();
159         }
160
161         return 0.0f;
162 }