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