Krotos Modules 3
Loading...
Searching...
No Matches
KrotosAudioBufferDSPPhaseDetection.cpp
Go to the documentation of this file.
1/*
2==============================================================================
3
4KrotosAudioBufferDSP.cpp
5Created: 13 June 2019 12:22:00am
6Author: sandyw
7
8==============================================================================
9*/
10
11namespace krotos
12{
13 int KrotosAudioBufferDSP::calcFreeGrain(int centerIndex, int previousGrain, GrainRange range)
14 {
15 enum corrType
16 {
17 M2 = 0,
18 M1,
19 ZE,
20 P1,
21 P2,
22 SIZE
23 };
24
25 float cor[SIZE];
26 cor[M2] = autoCorrelateCentred(centerIndex, previousGrain - 2);
27 cor[M1] = autoCorrelateCentred(centerIndex, previousGrain - 1);
28 cor[ZE] = autoCorrelateCentred(centerIndex, previousGrain);
29 cor[P1] = autoCorrelateCentred(centerIndex, previousGrain + 1);
30 cor[P2] = autoCorrelateCentred(centerIndex, previousGrain + 2);
31
32 float tolerance = 1.0f + (m_phaseAnalysisCoefficients.cal3 * 0.01f);
33 int increment;
34 int maxSteps;
35 if (cor[ZE] < cor[P1]) // Travelling fwds ?
36 {
37 increment = 1;
38 maxSteps = int(float(previousGrain) * tolerance) - previousGrain;
39 }
40 else
41 {
42 increment = -1;
43 maxSteps = previousGrain - int(float(previousGrain) / tolerance);
44 }
45
46 if (maxSteps < 0)
47 {
48 maxSteps = 0;
49 }
50
51 int grain = previousGrain;
52
53 while ((!((cor[ZE] > cor[M2]) && (cor[ZE] > cor[M1]) && (cor[ZE] > cor[P1]) && (cor[ZE] > cor[P2]))) &&
54 (grain < range.max) && (grain > range.min))
55 // while ((!((cor[ZE] > cor[M2]) && (cor[ZE] > cor[M1]) && (cor[ZE] > cor[P1]) && (cor[ZE] > cor[P2]))) &&
56 // maxSteps--) // non ranged terminator - leave in
57 {
58 if (increment > 0)
59 {
60 cor[M2] = cor[M1];
61 cor[M1] = cor[ZE];
62 cor[ZE] = cor[P1];
63 cor[P1] = cor[P2];
64 cor[P2] = autoCorrelateCentred(centerIndex, grain + 3);
65 }
66 else
67 {
68 cor[P2] = cor[P1];
69 cor[P1] = cor[ZE];
70 cor[ZE] = cor[M1];
71 cor[M1] = cor[M2];
72 cor[M2] = autoCorrelateCentred(centerIndex, grain - 3);
73 }
74
75 grain += increment;
76 }
77
78 if ((grain >= range.max) || (grain <= range.min) || (maxSteps == 0))
79 {
80 return -1;
81 }
82
83 return grain;
84 }
85
86 void findPeaks(std::vector<float>& graphArray, std::vector<Point<float>>& markerArray, int maxPeaks)
87 {
88 int graphSize = static_cast<int>(graphArray.size());
89 int startAnalisys = 2;
90 int endAnalisys = graphSize - 2;
91
92 markerArray.clear();
93
94 for (int i = startAnalisys; i < endAnalisys; ++i)
95 {
96 if ((graphArray[i - 1] < graphArray[i] && graphArray[i] > graphArray[i + 1]) &&
97 (graphArray[i - 2] < graphArray[i] && graphArray[i] > graphArray[i + 2]))
98 {
99 markerArray.push_back(Point<float>{(float)i / (float)graphSize, graphArray[i]});
100 }
101 if (markerArray.size() >= maxPeaks)
102 break;
103 }
104 }
105
106 int centralise(std::vector<float>& graphArray, int position)
107 {
108 int graphSize = static_cast<int>(graphArray.size());
109 int startAnalisys = 3;
110 int endAnalisys = graphSize - 3;
111
112 if ((position <= startAnalisys) || (position >= endAnalisys))
113 {
114 return -1;
115 }
116
117 int increment = -1;
118 if (graphArray[position] < graphArray[position + 1]) // Are we travelling forward in the array ?
119 {
120 increment = 1;
121 }
122
123 int i = position;
124 while ((!((graphArray[i - 1] < graphArray[i] && graphArray[i] > graphArray[i + 1]) &&
125 (graphArray[i - 2] < graphArray[i] && graphArray[i] > graphArray[i + 2]))) &&
126 (i < endAnalisys) && (i > startAnalisys))
127 {
128 i = int(int(i) + increment);
129 }
130
131 if ((i >= endAnalisys) || (i <= startAnalisys))
132 {
133 return -1;
134 }
135
136 return int(i);
137 }
138
139 void sortMarkersByX(std::vector<juce::Point<float>>& markerArray, bool decending = false)
140 {
141 int i, j, k;
142 int n = static_cast<int>(markerArray.size());
143 // Gap 'i' between index of the element to be compared, initially n/2.
144 for (i = n / 2; i > 0; i = i / 2)
145 {
146 for (j = i; j < n; j++)
147 {
148 for (k = j - i; k >= 0; k = k - i)
149 {
150 // If value at higher index is greater, then break the loop.
151 if (decending)
152 {
153 if (markerArray[k + i].getX() < markerArray[k].getX())
154 break;
155 }
156 else
157 {
158 if (markerArray[k + i].getX() > markerArray[k].getX())
159 break;
160 }
161 // Switch the values otherwise.
162 {
163 Point<float> temp = markerArray[k];
164 markerArray[k] = markerArray[k + i];
165 markerArray[k + i] = temp;
166 }
167 }
168 }
169 }
170 }
171
172 void sortMarkersByY(std::vector<Point<float>>& markerArray, bool decending = false)
173 {
174 int i, j, k;
175 int n = static_cast<int>(markerArray.size());
176 // Gap 'i' between index of the element to be compared, initially n/2.
177 for (i = n / 2; i > 0; i = i / 2)
178 {
179 for (j = i; j < n; j++)
180 {
181 for (k = j - i; k >= 0; k = k - i)
182 {
183 // If value at higher index is greater, then break the loop.
184 if (decending)
185 {
186 if (markerArray[k + i].getY() < markerArray[k].getY())
187 break;
188 }
189 else
190 {
191 if (markerArray[k + i].getY() > markerArray[k].getY())
192 break;
193 }
194 // Switch the values otherwise.
195 {
196 Point<float> temp = markerArray[k];
197 markerArray[k] = markerArray[k + i];
198 markerArray[k + i] = temp;
199 }
200 }
201 }
202 }
203 }
204
205 bool KrotosAudioBufferDSP::scanForGrainsFreeMarker(std::vector<int>& returnGrains, const int audioIndexStart,
206 const int useableIndexEnd, const int startGrainSize)
207 {
208 int phaseZeroPosition = audioIndexStart;
209 int previousGrainSize = startGrainSize;
210 do // Forwards scan
211 {
212 int newGrainSize = this->calcFreeGrain(phaseZeroPosition, previousGrainSize, m_analysisGrainSizeRange);
213 if (newGrainSize < 0)
214 {
215 return false; // Aborted early
216 }
217 returnGrains.push_back(newGrainSize);
218 phaseZeroPosition += newGrainSize;
219 previousGrainSize = newGrainSize;
220 } while (phaseZeroPosition < useableIndexEnd);
221 return true; // Finished successfully
222 }
223
225 const int audioIndexStart, const int useableIndexEnd,
226 const int startGrainSize)
227 {
228 int phaseZeroPosition = audioIndexStart;
229 int previousGrainSize = startGrainSize;
230 do // Backwards scan
231 {
232 int newGrainSize = this->calcFreeGrain(phaseZeroPosition, previousGrainSize, m_analysisGrainSizeRange);
233 if (newGrainSize < 0)
234 {
235 return false; // Aborted early
236 }
237 phaseZeroPosition -= newGrainSize; // Decrement phase
238 if (phaseZeroPosition < useableIndexEnd)
239 {
240 break;
241 }
242 returnGrains.push_back(newGrainSize);
243 previousGrainSize = newGrainSize;
244 } while (phaseZeroPosition > useableIndexEnd);
245 return true; // Finished successfully
246 }
247
248 void KrotosAudioBufferDSP::buildDescriptors(std::vector<AudioDescriptor>& descriptors,
249 const std::vector<int>& grainsList, const int startGrainAudioIndex,
250 bool goingBackwards = false)
251 {
252 int audioIndex = startGrainAudioIndex;
253 bool newSubPath = true;
254
255 for (int i = 0; i < grainsList.size(); i++)
256 {
257 int newGrainSize = grainsList[i];
258 float frequency = this->getSampleRate() / (float)newGrainSize;
259
260 if (goingBackwards)
261 {
262 audioIndex -= newGrainSize; // Decrement phase
263 }
264
265 descriptors.push_back(AudioDescriptor{frequency, 0, audioIndex, (float)newGrainSize, 0, true, false});
266
267 newSubPath = false;
268
269 if (!goingBackwards)
270 {
271 audioIndex += newGrainSize; // Increment phase
272 }
273 }
274 }
275
277 {
278 float hzMin{10.0f};
279 float hzMax{500.0f};
280 int minGrainSize{int(this->getSampleRate() / hzMax)};
281 int maxGrainSize{int(this->getSampleRate() / hzMin)};
282 m_analysisGrainSizeRange.setLimits(minGrainSize, maxGrainSize);
284 }
285
287 {
289 coeff.cal0 = 10.0f; // Power
290 coeff.tracking = m_trackingValue; // Tracking (position 0 -> 1)
291 coeff.cycles = m_trackingCycles;
292 coeff.cal3 = 1.2f; //
293 coeff.cal4 = 8.0f; // Num trackers
294 coeff.cal5 = 0.0f; // Floor
296 }
297
299 {
300 FloatVectorOperations::disableDenormalisedNumberSupport();
301
304
305 if (this->size() < m_analysisGrainSizeRange.max)
306 {
307 return;
308 }
309
310 invalidate();
311
312 std::vector<AudioDescriptor> grainDescriptionByFrequency;
313 std::vector<AudioDescriptor> grainDescriptionByTime;
314
315 int useableIndexStart = int(float(this->size()) * m_startPos);
316 if (useableIndexStart < m_analysisGrainSizeRange.max)
317 useableIndexStart = m_analysisGrainSizeRange.max;
318 int useableIndexEnd = int(float(this->size()) * m_endPos);
319 if (useableIndexEnd > (this->size() - m_analysisGrainSizeRange.max))
320 useableIndexEnd = (this->size() - m_analysisGrainSizeRange.max);
321
322 std::vector<int> grainListFwds;
323 std::vector<int> grainListBack;
324
325 int lengthFwds{0};
326 int lengthBack{0};
327
328 int bestLengthFwds{0};
329 int bestLengthBack{0};
330
331 int bestLength = static_cast<int>(1000000000000);
332 int bestAudioIndex{-1};
333 int bestGrainSize{0};
334
335 int startGrainAudioIndex =
336 static_cast<int>(jmap<float>(m_phaseAnalysisCoefficients.tracking, 0.0f, 1.0f, m_startPos, m_endPos) *
337 static_cast<float>(this->size()));
338
339 // int startGrainSize = (m_analysisGrainSizeRange.min + m_analysisGrainSizeRange.max) / 2;
340
341 int engineRPM = m_phaseAnalysisCoefficients.cycles;
342 int engineHz = engineRPM / 60.f;
343
344 int startGrainSize = static_cast<int>(this->getSampleRate() / engineHz);
345
346 // Grain scans use full range
347 useableIndexStart = static_cast<int>(static_cast<float>(this->size()) * m_startPos);
348 useableIndexEnd = static_cast<int>(static_cast<float>(this->size()) * m_endPos);
349 grainListFwds.clear();
350 grainListBack.clear();
351 scanForGrainsFreeMarker(grainListFwds, startGrainAudioIndex, useableIndexEnd, startGrainSize);
352 scanBackwardsForGrainsFreeMarker(grainListBack, startGrainAudioIndex, useableIndexStart, startGrainSize);
353
354 // Throw away the final grain of each scan as it may have run out of wav, and be unreliable
355 if (grainListFwds.size() > 0)
356 grainListFwds.pop_back();
357 if (grainListBack.size() > 0)
358 grainListBack.pop_back();
359
360 std::vector<AudioDescriptor> descriptorsFree;
361 buildDescriptors(descriptorsFree, grainListFwds, startGrainAudioIndex);
362 buildDescriptors(descriptorsFree, grainListBack, startGrainAudioIndex, true);
363
364 if (!descriptorsFree.size())
365 return; // Will be returning invalidated
366
367 // The free scan is out of order, so sort by audio index
368 std::sort(descriptorsFree.begin(), descriptorsFree.end(),
369 [](AudioDescriptor& a, AudioDescriptor& b) { return a.audioIndex < b.audioIndex; });
370
371 // Description is now in time order, so we can tag the grainIndexByTime
372 int timeIndex = 0;
373 for (std::vector<AudioDescriptor>::iterator it = descriptorsFree.begin(); it != descriptorsFree.end(); ++it)
374 {
375 it->grainIndexByTime = timeIndex++;
376 }
377
378 const int numCycles = 1;
379 std::vector<AudioDescriptor> descriptorsTime;
380
381 if (numCycles == 1)
382 {
383 descriptorsTime = descriptorsFree;
384 }
385 else
386 {
387 int cycleCounter = 0;
388 float grainSizeAccumulator = 0;
389 for (std::vector<AudioDescriptor>::iterator it = descriptorsFree.begin(); it != descriptorsFree.end(); ++it)
390 {
391 grainSizeAccumulator += it->grainSize;
392 if (++cycleCounter == numCycles) // Thinning out the number of descriptors we keep
393 {
394 AudioDescriptor newDesc = *it;
395 newDesc.grainIndexByTime = it->grainIndexByTime / numCycles;
396 newDesc.grainSize = grainSizeAccumulator;
397 newDesc.frequency = this->getSampleRate() / grainSizeAccumulator;
398 descriptorsTime.push_back(newDesc);
399 grainSizeAccumulator = 0.f;
400 cycleCounter = 0;
401 }
402 }
403 }
404
405 // Sort by frequency
406 std::sort(descriptorsTime.begin(), descriptorsTime.end(),
407 [](AudioDescriptor& a, AudioDescriptor& b) { return a.frequency < b.frequency; });
408
409 // Description is now in frequency order, so we can tag the grainIndexFrequencyOrder
410 int frequencyIndex = 0;
411 for (std::vector<AudioDescriptor>::iterator it = descriptorsTime.begin(); it != descriptorsTime.end(); ++it)
412 {
413 it->grainIndexByFrequency = frequencyIndex++;
414 }
415
416 grainDescriptionByFrequency = descriptorsTime; // Store it - by frequency
417
418 // Re-sort by time
419 std::sort(descriptorsTime.begin(), descriptorsTime.end(),
420 [](AudioDescriptor& a, AudioDescriptor& b) { return a.audioIndex < b.audioIndex; });
421
422 grainDescriptionByTime = descriptorsTime; // Store it - by time
423
424 if ((grainDescriptionByFrequency.size() > 0) && (grainDescriptionByTime.size() > 0))
425 {
426 m_grainDescriptionByFrequency = grainDescriptionByFrequency;
427 m_grainDescriptionByTime = grainDescriptionByTime;
429 }
430 }
431
432} // namespace krotos
Definition KrotosAudioBufferDSP.h:113
int max
Definition KrotosAudioBufferDSP.h:159
void setToLimits()
Definition KrotosAudioBufferDSP.h:144
int min
Definition KrotosAudioBufferDSP.h:158
void setLimits(GrainRange limits)
Definition KrotosAudioBufferDSP.h:138
void setAnalysisGrainSizeRange()
Set the frequency range we want to analyse for.
Definition KrotosAudioBufferDSPPhaseDetection.cpp:276
AnalysisCoefficientsPhase m_phaseAnalysisCoefficients
Definition KrotosAudioBufferDSP.h:586
void buildDescriptors(std::vector< AudioDescriptor > &descriptors, const std::vector< int > &grainsList, const int startGrainAudioIndex, bool goingBackwards)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:248
float m_trackingValue
Definition KrotosAudioBufferDSP.h:564
float m_endPos
Definition KrotosAudioBufferDSP.h:584
volatile std::atomic< bool > m_trackingDataIsValid
Definition KrotosAudioBufferDSP.h:568
bool scanBackwardsForGrainsFreeMarker(std::vector< int > &returnGrains, const int audioIndexStart, const int useableIndexEnd, const int startGrainSize)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:224
float m_startPos
Definition KrotosAudioBufferDSP.h:583
int calcFreeGrain(int centerIndex, int previousGrainSize, GrainRange range)
calcFreeGrain - find the next grain size
Definition KrotosAudioBufferDSPPhaseDetection.cpp:13
void invalidate()
Definition KrotosAudioBufferDSP.h:414
int size()
Definition KrotosAudioBufferDSP.h:394
std::vector< AudioDescriptor > m_grainDescriptionByFrequency
Definition KrotosAudioBufferDSP.h:572
std::vector< AudioDescriptor > m_grainDescriptionByTime
Definition KrotosAudioBufferDSP.h:575
void setPhaseAnalysisCoefficients(AnalysisCoefficientsPhase coeff)
Definition KrotosAudioBufferDSP.h:392
GrainRange m_analysisGrainSizeRange
Definition KrotosAudioBufferDSP.h:567
bool scanForGrainsFreeMarker(std::vector< int > &returnGrains, const int audioIndexStart, const int useableIndexEnd, const int startGrainSize)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:205
float m_trackingCycles
Definition KrotosAudioBufferDSP.h:565
void analysePhase()
Definition KrotosAudioBufferDSPPhaseDetection.cpp:298
float autoCorrelateCentred(int centerIndex, int testSize)
autoCorrelateCentred - Autocorrelate both sides of supplied index
Definition KrotosAudioBufferDSPCorrelation.cpp:52
void setDefaultPhaseAnalysisCoefficients()
Definition KrotosAudioBufferDSPPhaseDetection.cpp:286
float getSampleRate(void)
Definition KrotosAudioBuffer.cpp:506
Definition AirAbsorptionFilter.cpp:2
void findPeaks(std::vector< float > &graphArray, std::vector< Point< float > > &markerArray, int maxPeaks)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:86
void sortMarkersByY(std::vector< Point< float > > &markerArray, bool decending=false)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:172
void sortMarkersByX(std::vector< juce::Point< float > > &markerArray, bool decending=false)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:139
int centralise(std::vector< float > &graphArray, int position)
Definition KrotosAudioBufferDSPPhaseDetection.cpp:106
AnalysisCoefficientsPhase - A class containing attributes used during phase analysis of audio.
Definition KrotosAudioBufferDSP.h:29
float cycles
Definition KrotosAudioBufferDSP.h:31
float cal5
Definition KrotosAudioBufferDSP.h:35
float cal0
Definition KrotosAudioBufferDSP.h:32
float cal4
Definition KrotosAudioBufferDSP.h:34
float tracking
Definition KrotosAudioBufferDSP.h:30
float cal3
Definition KrotosAudioBufferDSP.h:33
A class to contains any attributes we want to store for the described audio.
Definition KrotosAudioBufferDSP.h:177
int grainIndexByTime
Definition KrotosAudioBufferDSP.h:182
float frequency
Definition KrotosAudioBufferDSP.h:178
float grainSize
Definition KrotosAudioBufferDSP.h:181