summaryrefslogtreecommitdiff
path: root/libs/ardour/meter.cc
blob: 1ce610d13c21efacf4377781c3247b791747cf32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
    Copyright (C) 2006 Paul Davis 
    
    This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation; either version 2 of the License, or (at your option)
    any later version.
    
    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.
    
    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <ardour/meter.h>
#include <algorithm>
#include <cmath>
#include <ardour/buffer_set.h>
#include <ardour/peak.h>
#include <ardour/dB.h>
#include <ardour/session.h>

namespace ARDOUR {


/** Get peaks from @a bufs
 * Input acceptance is lenient - the first n audio buffers from @a bufs will
 * be metered, where n was set by the last call to setup(), excess meters will
 * be set to 0.
 */
void
PeakMeter::run (BufferSet& bufs, jack_nframes_t nframes, jack_nframes_t offset)
{
	size_t meterable = std::min(bufs.count().get(DataType::AUDIO), _peak_power.size());

	// Meter what we have
	for (size_t n = 0; n < meterable; ++n) {
		_peak_power[n] = compute_peak (bufs.get_audio(n).data(nframes, offset), nframes, _peak_power[n]); 
	}

	// Zero any excess peaks
	for (size_t n = meterable; n < _peak_power.size(); ++n) {
		_peak_power[n] = 0;
	}
}

void
PeakMeter::reset ()
{
	for (size_t i = 0; i < _peak_power.size(); ++i) {
		_peak_power[i] = 0;
	}
}

void
PeakMeter::setup (const ChanCount& in)
{
	uint32_t limit = in.get(DataType::AUDIO);

	while (_peak_power.size() > limit) {
		_peak_power.pop_back();
		_visible_peak_power.pop_back();
	}

	while (_peak_power.size() < limit) {
		_peak_power.push_back (0);
		_visible_peak_power.push_back (0);
	}

	assert(_peak_power.size() == limit);
	assert(_visible_peak_power.size() == limit);
}

/** To be driven by the Meter signal from IO.
 * Caller MUST hold io_lock!
 */
void
PeakMeter::meter ()
{
	assert(_visible_peak_power.size() == _peak_power.size());

	const size_t limit = _peak_power.size();

	for (size_t n = 0; n < limit; ++n) {

		/* XXX we should use atomic exchange here */

		/* grab peak since last read */

 		float new_peak = _peak_power[n];
		_peak_power[n] = 0;
		
		/* compute new visible value using falloff */

		if (new_peak > 0.0) {
			new_peak = coefficient_to_dB (new_peak);
		} else {
			new_peak = minus_infinity();
		}
		
		if (Config->get_meter_falloff() == 0.0f || new_peak > _visible_peak_power[n]) {
			_visible_peak_power[n] = new_peak;
		} else {
			// do falloff
			new_peak = _visible_peak_power[n] - Config->get_meter_falloff();
			_visible_peak_power[n] = std::max (new_peak, -INFINITY);
		}
	}
}

} // namespace ARDOUR