Krotos Modules 3
Loading...
Searching...
No Matches
SlopEnvelopeOnsetdetection.cpp
Go to the documentation of this file.
1namespace krotos
2{
4 {
5 jassert(sampleRate > 0.0f);
6
7 m_sampleRate = sampleRate;
8 setupEnvFol(2.0f, 250.0f);
9 }
10
11 void EnvelopeFollowerSPD::setupEnvFol(float attackTimeMs, float releaseTimeMs)
12 {
13 jassert(attackTimeMs > 0 && releaseTimeMs > 0);
14
15 m_attackTimeSec = attackTimeMs * powf(10.0f, -3.0f);
16 m_releaseTimeSec = releaseTimeMs * powf(10.0f, -3.0f);
17
18 m_gAtt = 1.0f - expf(-1.0f / (m_attackTimeSec * m_sampleRate));
19 m_gRel = 1.0f - expf(-1.0f / (m_releaseTimeSec * m_sampleRate));
20 }
21
22 void EnvelopeFollowerSPD::setAttackMs(float attackTimeMs)
23 {
24 jassert(attackTimeMs > 0);
25 setupEnvFol(attackTimeMs, m_releaseTimeSec);
26 }
27
28 void EnvelopeFollowerSPD::setReleaseMs(float releaseTimeMs)
29 {
30 jassert(releaseTimeMs > 0);
31 setupEnvFol(m_attackTimeSec, releaseTimeMs);
32 }
33
34 std::vector<float> EnvelopeFollowerSPD::filterVector(std::vector<float> inputMonoVector)
35 {
36 int numSamples = static_cast<int>(inputMonoVector.size());
37 std::vector<float> envelope(numSamples, 0.0f);
38
39 for (int i = 1; i < numSamples; i++)
40 {
41 float inputSample = fabsf(inputMonoVector.at(i));
42
43 // We are in the attack Phase
44 if (inputSample > fabsf(inputMonoVector.at(i - 1)))
45 {
46 envelope.at(i) = envelope.at(i - 1) * (1.0f - m_gAtt) + (m_gAtt * inputSample);
47 }
48 else // We are in release phase
49 {
50 envelope.at(i) = envelope.at(i - 1) * (1.0f - m_gRel) + (m_gRel * inputSample);
51 }
52 }
53
54 return envelope;
55 }
56
57 std::vector<float> EnvelopeFollowerSPD::normaliseEnvelope(std::vector<float> envelope)
58 {
59 float min = *min_element(envelope.begin(), envelope.end());
60 float max = *max_element(envelope.begin(), envelope.end());
61
62 // Normalize envelope Array
63 for (int i = 0; i < envelope.size(); i++)
64 {
65 envelope.at(i) = (jmap(envelope.at(i), min, max, 0.0f, 1.0f));
66 }
67
68 // if it hits something went wrong in the normalisation process
69 jassert(*max_element(envelope.begin(), envelope.end()) <= 1.0f);
70 jassert(*min_element(envelope.begin(), envelope.end()) >= 0.0f);
71
72 return envelope;
73 }
74
75 //-----------------------------------------------------------------------------------------------------------------------------
76 std::vector<float> SlopeGenerator::createSlopeVectorFromEnvelope(std::vector<float> envelopeArray)
77 {
78 int numSamples = static_cast<int>(envelopeArray.size());
79
80 jassert(numSamples > 4);
81 jassert(m_numRegressionPoints > 0);
82
83 // number of linear Regression frames
84 int numRegFrames = numSamples - m_numRegressionPoints + 1;
85
86 std::vector<float> slopeArray(numRegFrames, 0.0f);
87
88 float numer{0.0f};
89 float denom{0.0f};
90 float tMean{0.0f};
91 float envMean{0.0f};
92
93 std::vector<float> timeLocal(4);
94
95 for (int i = 0; i < numRegFrames; i++)
96 {
97 // local time frame vector
98 iota(timeLocal.begin(), timeLocal.end(), static_cast<float>(i + 1));
99 // calculate moving time frame mean
100 tMean = accumulate(timeLocal.begin(), timeLocal.end(), 0.0f) / m_numRegressionPoints;
101 jassert(tMean != 0);
102
103 // local envelope frame vector
104 std::vector<float>::const_iterator envFirst = envelopeArray.begin() + i;
105 std::vector<float>::const_iterator envLast = envelopeArray.begin() + i + 4;
106 std::vector<float> envLocal(envFirst, envLast);
107 // calculate moving envelope frame mean
108 envMean = accumulate(envLocal.begin(), envLocal.end(), 0.0f) / m_numRegressionPoints;
109
110 // Apply least squares error criterion
111 // ΤΟDO: Localise to C++, starting from 0 - m_numRegressionPoints
112 for (int n = 1; n < m_numRegressionPoints + 1; n++)
113 {
114 numer += (timeLocal.at(n - 1) - tMean) * (envLocal.at(n - 1) - envMean);
115 denom += powf(timeLocal.at(n - 1) - tMean, 2.0f) + 1e-10f;
116 jassert(denom != 0.0f);
117 }
118
119 // Store value in slope Array
120 slopeArray.at(i) = (numer / denom);
121
122 numer = 0.0f;
123 denom = 0.0f;
124 }
125
126 return slopeArray;
127 }
128
129 //-----------------------------------------------------------------------------------------------------------------------------
130 OnsetDetector::OnsetDetector(float sampleRate) { m_sampleRate = sampleRate; }
131
132 void OnsetDetector::setThresholdPercentage(float thresholdPercentage) { m_threshold = thresholdPercentage; }
133
134 void OnsetDetector::setOnsetMiliSeconds(float onsetMiliSec)
135 {
136 jassert(m_sampleRate > 0);
137 m_onset = static_cast<int>(onsetMiliSec * m_sampleRate);
138 }
139
140 void OnsetDetector::setDecayLengthMiliSeconds(float decayLengthMiliSec)
141 {
142 jassert(m_sampleRate > 0);
143 m_decayLength = static_cast<int>(decayLengthMiliSec * m_sampleRate);
144 }
145
146 std::vector<int> OnsetDetector::calculateOnsetsGlobal(std::vector<float> slopeVector,
147 std::vector<float> normalisedEnvelopeVector)
148 {
149 // You have not set the parameters if this hits, call setters before calling
150 // this function !!
151 jassert(m_threshold != 0.f);
152 jassert(m_decayLength != 0);
153 jassert(m_onset != 0);
154
155 // Calculate maximum Value of SlopeArray to set onset detection threshold
156 float maxSlope = *max_element(slopeVector.begin(), slopeVector.end());
157
158 float thresholdValue = m_threshold * maxSlope;
159
160 int finishPosition = 0;
161 int startPosition = 0;
162 std::vector<int> attackPositions;
163
164 while (finishPosition < slopeVector.size())
165 {
166 startPosition = finishPosition + 1;
167
168 // local slope array vector
169 std::vector<float>::const_iterator slopeFirst = slopeVector.begin() + startPosition;
170 std::vector<float>::const_iterator slopeLast = slopeVector.end();
171 std::vector<float> slopeLocal(slopeFirst, slopeLast);
172
173 // local normalised envelope array vector
174 std::vector<float>::const_iterator envNormFirst = normalisedEnvelopeVector.begin() + startPosition;
175 std::vector<float>::const_iterator envNormLast = normalisedEnvelopeVector.end();
176 std::vector<float> envNormLocal(envNormFirst, envNormLast);
177
178 // Call function
179 std::pair<int, int> attackFinishPair = detectAttackSegment(slopeLocal, envNormLocal, thresholdValue);
180
181 if (m_finishFlag == true)
182 {
183 break;
184 }
185
186 attackPositions.push_back(attackFinishPair.first + finishPosition);
187
188 finishPosition += attackFinishPair.second;
189 }
190
191 return attackPositions;
192 }
193
194 std::pair<int, int> OnsetDetector::detectAttackSegment(const std::vector<float>& slopeFrame,
195 const std::vector<float>& normalisedEnvelopeFrame,
196 float thresholdValue)
197 {
198 std::pair<int, int> attackFinishPair;
199
200 // Find first Attack position in the local Slope array vector
201 auto iter =
202 find_if(slopeFrame.begin(), slopeFrame.end(), [=](float x) { return (fabsf(x) >= thresholdValue); });
203 auto firstAttackPosition = static_cast<int>((distance(slopeFrame.begin(), iter)));
204
205 attackFinishPair.first = firstAttackPosition;
206
207 // We did not find any attacks, terminate
208 if (firstAttackPosition == slopeFrame.size())
209 {
210 m_finishFlag = true;
211 return attackFinishPair;
212 }
213 else
214 {
215 // Skip ahead by OnsetSamples and search for local maximum
216 int localMaxPosition = -1;
217 for (int i = firstAttackPosition + m_onset + 2; i < slopeFrame.size(); i++)
218 {
219 // check for local max
220 if (slopeFrame.at(i - 1) > m_epsilon && slopeFrame.at(i) < -m_epsilon)
221 {
222 localMaxPosition = i - 1;
223 break;
224 }
225 }
226
227 // No local maximum, return attackPosition and terminate
228 if (localMaxPosition == -1)
229 {
230 m_finishFlag = true;
231 return attackFinishPair;
232 }
233
234 // Progress through data from local max to check if moving average power
235 // hits noise floor
236 bool rest = false;
237
238 // number of frames to check
239 int numRestFrames = static_cast<int>(
240 floorf(static_cast<float>(normalisedEnvelopeFrame.size() - localMaxPosition) / m_decayLength));
241 // counter for while loop
242 int n = 0;
243 int start = 0;
244 int finish = 0;
245
246 while (n < (numRestFrames - 1) && rest == false)
247 {
248 start = localMaxPosition + (n * m_decayLength);
249 finish = start + m_decayLength - 1;
250
251 std::vector<float>::const_iterator envSegmentFirst = normalisedEnvelopeFrame.begin() + start;
252 std::vector<float>::const_iterator envSegmentLast = normalisedEnvelopeFrame.begin() + finish;
253 std::vector<float> decayRegionEnv(envSegmentFirst, envSegmentLast);
254
255 // calculate average power of signal in that region
256 transform(decayRegionEnv.begin(), decayRegionEnv.end(), decayRegionEnv.begin(),
257 [](float x) -> float { return x * x; });
258 float movAvgPow = (accumulate(decayRegionEnv.begin(), decayRegionEnv.end(), 0.0f)) / m_decayLength;
259
260 // if we find new rest segment return last position and attackposition and
261 // terminate
262 if (movAvgPow < 0.01f * normalisedEnvelopeFrame.at(localMaxPosition))
263 {
264 rest = true;
265 }
266
267 n += 1;
268 }
269
270 attackFinishPair.second = finish;
271
272 return attackFinishPair;
273 }
274 }
275
276} // namespace krotos
void setupEnvFol(float attackTimeMs, float releaseTimeMs)
Definition SlopEnvelopeOnsetdetection.cpp:11
std::vector< float > filterVector(std::vector< float > inputMonoVector)
Definition SlopEnvelopeOnsetdetection.cpp:34
std::vector< float > normaliseEnvelope(std::vector< float > inputMonoVector)
Definition SlopEnvelopeOnsetdetection.cpp:57
float m_sampleRate
Definition SlopEnvelopeOnsetdetection.h:52
float m_attackTimeSec
Definition SlopEnvelopeOnsetdetection.h:50
void setReleaseMs(float releaseTimeMs)
Definition SlopEnvelopeOnsetdetection.cpp:28
void setAttackMs(float attackTimeMs)
Definition SlopEnvelopeOnsetdetection.cpp:22
float m_releaseTimeSec
Definition SlopEnvelopeOnsetdetection.h:51
float m_gAtt
Definition SlopEnvelopeOnsetdetection.h:55
EnvelopeFollowerSPD(float sampleRate)
Definition SlopEnvelopeOnsetdetection.cpp:3
float m_gRel
Definition SlopEnvelopeOnsetdetection.h:56
std::vector< int > calculateOnsetsGlobal(std::vector< float > slopeVector, std::vector< float > normalisedEnvelopeVector)
Definition SlopEnvelopeOnsetdetection.cpp:146
int m_onset
Definition SlopEnvelopeOnsetdetection.h:139
float m_sampleRate
Definition SlopEnvelopeOnsetdetection.h:145
float m_epsilon
Definition SlopEnvelopeOnsetdetection.h:143
bool m_finishFlag
Definition SlopEnvelopeOnsetdetection.h:147
float m_threshold
Definition SlopEnvelopeOnsetdetection.h:137
std::pair< int, int > detectAttackSegment(const std::vector< float > &slopeFrame, const std::vector< float > &normalisedEnvelopeFrame, float thresholdValue)
Definition SlopEnvelopeOnsetdetection.cpp:194
void setDecayLengthMiliSeconds(float decayLengthMiliSeconds)
Definition SlopEnvelopeOnsetdetection.cpp:140
int m_decayLength
Definition SlopEnvelopeOnsetdetection.h:141
void setOnsetMiliSeconds(float onsetMiliSeconds)
Definition SlopEnvelopeOnsetdetection.cpp:134
void setThresholdPercentage(float thresholdPercentage)
Definition SlopEnvelopeOnsetdetection.cpp:132
OnsetDetector(float sampleRate)
Definition SlopEnvelopeOnsetdetection.cpp:130
int m_numRegressionPoints
Definition SlopEnvelopeOnsetdetection.h:92
std::vector< float > createSlopeVectorFromEnvelope(std::vector< float > envelopeVector)
Definition SlopEnvelopeOnsetdetection.cpp:76
Definition AirAbsorptionFilter.cpp:2