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