1 /*
2 * Copyright (C) 2016-2017 Paul Davis <paul@linuxaudiosystems.com>
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 along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19
20 #include "ardour/analysis_graph.h"
21 #include "ardour/route.h"
22 #include "ardour/session.h"
23
24 #include "temporal/time.h"
25
26 #include "audiographer/process_context.h"
27 #include "audiographer/general/chunker.h"
28 #include "audiographer/general/interleaver.h"
29 #include "audiographer/general/analyser.h"
30 #include "audiographer/general/peak_reader.h"
31
32 #include "pbd/i18n.h"
33
34 using namespace ARDOUR;
35 using namespace AudioGrapher;
36
AnalysisGraph(Session * s)37 AnalysisGraph::AnalysisGraph (Session *s)
38 : _session (s)
39 , _max_chunksize (8192)
40 , _samples_read (0)
41 , _samples_end (0)
42 , _canceled (false)
43 {
44 _buf = (Sample *) malloc(sizeof(Sample) * _max_chunksize);
45 _mixbuf = (Sample *) malloc(sizeof(Sample) * _max_chunksize);
46 _gainbuf = (float *) malloc(sizeof(float) * _max_chunksize);
47 }
48
~AnalysisGraph()49 AnalysisGraph::~AnalysisGraph ()
50 {
51 free (_buf);
52 free (_mixbuf);
53 free (_gainbuf);
54 }
55
56 void
analyze_region(boost::shared_ptr<AudioRegion> region)57 AnalysisGraph::analyze_region (boost::shared_ptr<AudioRegion> region)
58 {
59 int n_channels = region->n_channels();
60 if (n_channels == 0 || n_channels > _max_chunksize) {
61 return;
62 }
63 samplecnt_t n_samples = _max_chunksize - (_max_chunksize % n_channels);
64
65 interleaver.reset (new Interleaver<Sample> ());
66 interleaver->init (n_channels, _max_chunksize);
67 chunker.reset (new Chunker<Sample> (n_samples));
68 analyser.reset (new Analyser (
69 _session->nominal_sample_rate(),
70 n_channels,
71 n_samples,
72 region->length()));
73
74 interleaver->add_output(chunker);
75 chunker->add_output (analyser);
76
77 samplecnt_t x = 0;
78 samplecnt_t length = region->length();
79 while (x < length) {
80 samplecnt_t chunk = std::min (_max_chunksize, length - x);
81 samplecnt_t n = 0;
82 for (unsigned int channel = 0; channel < region->n_channels(); ++channel) {
83 memset (_buf, 0, chunk * sizeof (Sample));
84 n = region->read_at (_buf, _mixbuf, _gainbuf, region->position() + x, chunk, channel);
85 ConstProcessContext<Sample> context (_buf, n, 1);
86 if (n < _max_chunksize) {
87 context().set_flag (ProcessContext<Sample>::EndOfInput);
88 }
89 interleaver->input (channel)->process (context);
90
91 if (n == 0) {
92 std::cerr << "AnalysisGraph::analyze_region read zero samples\n";
93 break;
94 }
95 }
96 x += n;
97 _samples_read += n;
98 Progress (_samples_read, _samples_end);
99 if (_canceled) {
100 return;
101 }
102 }
103 _results.insert (std::make_pair (region->name(), analyser->result ()));
104 }
105
106 void
analyze_range(boost::shared_ptr<Route> route,boost::shared_ptr<AudioPlaylist> pl,const std::list<AudioRange> & range)107 AnalysisGraph::analyze_range (boost::shared_ptr<Route> route, boost::shared_ptr<AudioPlaylist> pl, const std::list<AudioRange>& range)
108 {
109 const uint32_t n_audio = route->n_inputs().n_audio();
110 if (n_audio == 0 || n_audio > _max_chunksize) {
111 return;
112 }
113 const samplecnt_t n_samples = _max_chunksize - (_max_chunksize % n_audio);
114
115 for (std::list<AudioRange>::const_iterator j = range.begin(); j != range.end(); ++j) {
116
117 interleaver.reset (new Interleaver<Sample> ());
118 interleaver->init (n_audio, _max_chunksize);
119
120 chunker.reset (new Chunker<Sample> (n_samples));
121 analyser.reset (new Analyser (
122 _session->nominal_sample_rate(),
123 n_audio, n_samples, (*j).length()));
124
125 interleaver->add_output(chunker);
126 chunker->add_output (analyser);
127
128 samplecnt_t x = 0;
129 while (x < j->length()) {
130 samplecnt_t chunk = std::min (_max_chunksize, (*j).length() - x);
131 samplecnt_t n = 0;
132 for (uint32_t channel = 0; channel < n_audio; ++channel) {
133 n = pl->read (_buf, _mixbuf, _gainbuf, (*j).start + x, chunk, channel);
134
135 ConstProcessContext<Sample> context (_buf, n, 1);
136 if (n < _max_chunksize) {
137 context().set_flag (ProcessContext<Sample>::EndOfInput);
138 }
139 interleaver->input (channel)->process (context);
140 }
141 x += n;
142 _samples_read += n;
143 Progress (_samples_read, _samples_end);
144 if (_canceled) {
145 return;
146 }
147 }
148
149 std::string name = string_compose (_("%1 (%2..%3)"), route->name(),
150 Timecode::timecode_format_sampletime (
151 (*j).start,
152 _session->nominal_sample_rate(),
153 100, false),
154 Timecode::timecode_format_sampletime (
155 (*j).start + (*j).length(),
156 _session->nominal_sample_rate(),
157 100, false)
158 );
159 _results.insert (std::make_pair (name, analyser->result ()));
160 }
161 }
162