1 /*
2     Copyright (C) 2008-2011 Fons Adriaensen <fons@linuxaudio.org>
3     Copyright (C) 2013 by Robin Gareus <robin@gareus.org>
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 
20 #include <math.h>
21 #include "kmeterdsp.h"
22 
23 namespace LV2M {
24 
25 float  Kmeterdsp::_omega;
26 int    Kmeterdsp::_hold;
27 float  Kmeterdsp::_fsamp;
28 
29 
Kmeterdsp(void)30 Kmeterdsp::Kmeterdsp (void) :
31     _z1 (0),
32     _z2 (0),
33     _rms (0),
34     _peak (0),
35     _cnt (0),
36     _fpp (0),
37     _fall (0),
38     _flag (false)
39 {
40 }
41 
42 
~Kmeterdsp(void)43 Kmeterdsp::~Kmeterdsp (void)
44 {
45 }
46 
init(float fsamp)47 void Kmeterdsp::init (float fsamp)
48 {
49     const float hold = 0.5f;
50     _fsamp = fsamp;
51 
52     _hold = (int)(hold * fsamp + 0.5f); // number of samples to hold peak
53     _omega = 9.72f / fsamp; // ballistic filter coefficient
54 }
55 
process(float * p,int n)56 void Kmeterdsp::process (float *p, int n)
57 {
58     // Called by JACK's process callback.
59     //
60     // p : pointer to sample buffer
61     // n : number of samples to process
62 
63     float  s, t, z1, z2;
64 
65     if (_fpp != n) {
66 	const float fall = 15.0f;
67 	const float tme = (float) n / _fsamp; // period time in seconds
68 	_fall = powf (10.0f, -0.05f * fall * tme); // per period fallback multiplier
69 	_fpp = n;
70     }
71 
72     t = 0;
73     // Get filter state.
74     z1 = _z1 > 50 ? 50 : (_z1 < 0 ? 0 : _z1);
75     z2 = _z2 > 50 ? 50 : (_z2 < 0 ? 0 : _z2);
76 
77     // Perform filtering. The second filter is evaluated
78     // only every 4th sample - this is just an optimisation.
79     n /= 4;  // Loop is unrolled by 4.
80     while (n--)
81     {
82 	s = *p++;
83 	s *= s;
84 	if (t < s) t = s;             // Update digital peak.
85 	z1 += _omega * (s - z1);      // Update first filter.
86 	s = *p++;
87 	s *= s;
88 	if (t < s) t = s;             // Update digital peak.
89 	z1 += _omega * (s - z1);      // Update first filter.
90 	s = *p++;
91 	s *= s;
92 	if (t < s) t = s;             // Update digital peak.
93 	z1 += _omega * (s - z1);      // Update first filter.
94 	s = *p++;
95 	s *= s;
96 	if (t < s) t = s;             // Update digital peak.
97 	z1 += _omega * (s - z1);      // Update first filter.
98         z2 += 4 * _omega * (z1 - z2); // Update second filter.
99     }
100 
101     if (isnan(z1)) z1 = 0;
102     if (isnan(z2)) z2 = 0;
103     if (!isfinite(t)) t = 0;
104 
105     // Save filter state. The added constants avoid denormals.
106     _z1 = z1 + 1e-20f;
107     _z2 = z2 + 1e-20f;
108 
109     s = sqrtf (2.0f * z2);
110     t = sqrtf (t);
111 
112     if (_flag) // Display thread has read the rms value.
113     {
114 	_rms  = s;
115 	_flag = false;
116     }
117     else
118     {
119         // Adjust RMS value and update maximum since last read().
120         if (s > _rms) _rms = s;
121     }
122 
123     // Digital peak hold and fallback.
124     if (t >= _peak)
125     {
126 	// If higher than current value, update and set hold counter.
127 	_peak = t;
128 	_cnt = _hold;
129     }
130     else if (_cnt > 0)
131     {
132 	// else decrement counter if not zero,
133 	_cnt-=_fpp;
134     }
135     else
136     {
137         _peak *= _fall;     // else let the peak value fall back,
138 	_peak += 1e-10f;    // and avoid denormals.
139     }
140 }
141 
142 /* Returns highest _rms value since last call */
read()143 float Kmeterdsp::read ()
144 {
145     float rv= _rms;
146     _flag = true; // Resets _rms in next process().
147     return rv;
148 }
149 
read(float & rms,float & peak)150 void Kmeterdsp::read (float &rms, float &peak)
151 {
152     rms  = _rms;
153     peak = _peak;
154     _flag = true; // Resets _rms in next process().
155 }
156 
reset()157 void Kmeterdsp::reset ()
158 {
159     _z1 = _z2 = _rms = _peak = .0f;
160     _cnt = 0;
161     _flag = false;
162 }
163 
164 } /* end namespace */
165 /* vi:set ts=8 sts=8 sw=4: */
166