Krotos Modules 3
Loading...
Searching...
No Matches
KrotosAudioBufferDSP.cpp
Go to the documentation of this file.
1/*
2==============================================================================
3
4KrotosAudioBufferDSP.cpp
5
6Created:
7 13 June 2019 12:22:00am S White
8
9Contributers:
10 Team Krotos
11
12==============================================================================
13*/
14
15namespace krotos
16{
17
18 KrotosAudioBufferDSP::KrotosAudioBufferDSP() : Thread("Analysis Thread") { addListener(this); }
19
25
27
34
36 {
37 // This method needs to be non-blocking so it doesn't hold up the
38 // message thread which has called it
39#if !UNIT_TEST
40#if ENABLE_REFORMER
41 // This method will sometimes be called repeatedly in fast succession as the various Params
42 // which call Analyse() are updated during state recall - we need to be able to
43 // abort and then return to do the analysis. If an analysis is already in progress ...
44 if (isThreadRunning())
45 {
46 signalThreadShouldExit(); // ... then we ask it to exit ...
47 notify(); // We can't predict how long it will take to exit, so we
48 // don't wait around, we start a timer
49 if (!isTimerRunning())
50 {
51 // If the timer has not already been started
52 startTimer(ANALYSIS_TIMER_PERIOD); // ... then we start it
53 // The timer will will call Analyse again after ANALYSIS_TIMER_PERIOD mS
54 }
55 }
56 else // If there is no analysis running, we are clear to start one
57 {
58 if (isTimerRunning()) // If the timer is running, we stop it ...
59 {
60 stopTimer(); // ... as it is no longer needed
61 }
62 startThread(); // We are sure analysis is not already in progress,
63 // so lets start the analysis thread
64 }
65#else
66 return;
67#endif /* INCLUDE_REFORMER */
68#endif /* UNIT_TEST */
69 }
70
72 {
73 invalidate();
74
75 // Reinstate this switch once we have a working choice of scheme
76 switch (getAnalysisScheme())
77 {
78
81 break;
82
85 break;
86
88 default:
89 break;
90 }
91
92 if (!analysisResultsAreValid()) // If analysis didn't complete ...
93 {
94 if (threadShouldExit())
95 return;
96
97 // ... then clean up any mess
99 }
100 }
101
103 {
104 // Retrieve Buffer
105 AudioBuffer<float>* buffer = dynamic_cast<AudioBuffer<float>*>(this);
106
107 // Average signal to mono if it's stereo
108 AudioBuffer<float> mono;
109 mono.setSize(1, getNumSamples());
110 mono.copyFrom(0, 0, *buffer, 0, 0, getNumSamples());
111 if (buffer->getNumChannels() == 2)
112 {
113 mono.addFrom(0, 0, *buffer, 1, 0, getNumSamples());
114 mono.applyGain(0, getNumSamples(), 0.5f);
115 }
116 float* start = mono.getWritePointer(0); // get pointer to the first sample of the first channel
117 int size = mono.getNumSamples();
118 // Data from the buffer copied into vector
119 std::vector<float> bufferVector(start, start + size);
120
121 return bufferVector;
122 }
123
124 std::vector<std::vector<float>> KrotosAudioBufferDSP::segmentOnsetSlices(std::vector<int> onsetPositions,
125 std::vector<float> signalBufferMono)
126 {
127 int startSample{0};
128 int finishSample{0};
129 // Contains sliced up buffer according to onset
130 std::vector<std::vector<float>> timeDomainSlices;
131
132 for (int i = 0; i < onsetPositions.size(); i++)
133 {
134 startSample = onsetPositions.at(i);
135 if (i < onsetPositions.size() - 1)
136 finishSample = onsetPositions.at(i + 1) - 1;
137 else
138 finishSample = static_cast<int>(signalBufferMono.size());
139
140 std::vector<float> subVector(signalBufferMono.begin() + startSample,
141 signalBufferMono.begin() + finishSample);
142 timeDomainSlices.push_back(subVector);
143 }
144
145 return timeDomainSlices;
146 }
147
148 std::vector<std::vector<float>> KrotosAudioBufferDSP::segmentChunks(std::vector<int> onsetPositions,
149 std::vector<float> bufferVector,
150 std::size_t window_length)
151
152 {
153 std::vector<std::vector<float>> timeDomainSlices;
154 const auto N = bufferVector.size();
155 for (auto onset : onsetPositions)
156 {
157 const auto start_sample = onset;
158 const auto end_sample = std::min(start_sample + window_length,
159 N); // handles case where audio is shorter than window_length
160
161 std::vector<float> clip(bufferVector.begin() + start_sample, bufferVector.begin() + end_sample);
162 timeDomainSlices.push_back(clip);
163 }
164 return timeDomainSlices;
165 }
166
167 std::vector<int> KrotosAudioBufferDSP::chunk(const std::vector<float>& bufferVector, std::size_t window_length,
168 std::size_t hop_length)
169 {
170 std::vector<int> onsets;
171 if (bufferVector.size() > window_length)
172 {
173 for (size_t i = 0; i < bufferVector.size() - window_length; i += hop_length)
174 {
175 onsets.push_back(static_cast<int>(i));
176 }
177 }
178
179 // if we didn't find any, set as onset the beginning of the file
180 if (onsets.empty())
181 {
182 onsets.push_back(0);
183 }
184
185 return onsets;
186 }
187
188 std::vector<int> KrotosAudioBufferDSP::envelopeOnsetDetection(const std::vector<float>& bufferVector,
189 float samplerate)
190 {
191
192 // envelope follower with corrected peak detection
193 auto envFollower = std::make_unique<EnvelopeFollowerSPD>(samplerate);
194 // slope calculation using linear regression object
195 auto slopeGenerator = std::make_unique<SlopeGenerator>();
196 // onsetDetection object
197 auto onsetDetector = std::make_unique<OnsetDetector>(samplerate);
198
199 // Initialise OnsetDetection Parameters
200 onsetDetector.get()->setThresholdPercentage(jmap<float>(m_AnalysisCoefficients.coeff0, 0.f, 1.f, 1e-3f,
201 1.f)); // 0.4 for percussive
202 onsetDetector.get()->setOnsetMiliSeconds(jmap<float>(m_AnalysisCoefficients.coeff1, 0.f, 1.f, 100e-3f,
203 1000e-3f)); // 100e-3 for percussive
204 onsetDetector.get()->setDecayLengthMiliSeconds(jmap<float>(m_AnalysisCoefficients.coeff2, 0.f, 1.f, 1e-3f,
205 10e-3f)); // 1e-3 for percussive
206
207 // Produce envelope follower vector from input Buffer data
208 std::vector<float> signalEnvelope = envFollower.get()->filterVector(bufferVector);
209 std::vector<float> normalisedEnvelope = envFollower.get()->normaliseEnvelope(signalEnvelope);
210
211 // Create slope Array
212 auto slopeVector = slopeGenerator.get()->createSlopeVectorFromEnvelope(normalisedEnvelope);
213
214 // Detect onsets
215 auto onsetPositions = onsetDetector.get()->calculateOnsetsGlobal(slopeVector, normalisedEnvelope);
216
217 // if we didn't find any, set as onset the beginning of the file
218 if (onsetPositions.empty())
219 {
220 onsetPositions.push_back(0);
221 }
222 return onsetPositions;
223 }
224
225 std::vector<int> KrotosAudioBufferDSP::superFluxOnsetDetection(float sampleRate)
226 {
227 // Create filterBank
228 auto filterBank = std::make_unique<MusicScaleFilterBank>(sampleRate);
229 // STFT parameters
230 const int analysisFrameSize{2048};
231 const int hopSize = 221;
232 // STFT object
233 auto m_stft = std::make_unique<ShortTimeFourierTransform>(
234 analysisFrameSize, analysisFrameSize, hopSize, WindowType::Hann,
235 ShortTimeFourierTransform::fftMode::freqMagOnly, static_cast<int>(sampleRate));
236
237 // Retrieve all the spectral frames from STFT
238 AudioBuffer<float>* buffer = dynamic_cast<AudioBuffer<float>*>(this);
239 // TODO: Should return MatrixXf instead of vector<vector<float>>
240 auto spectralFrames = m_stft.get()->stft(*buffer);
241 // Filter the spectrogram with the filterBank and take the log
242 // amplitude
243 auto filteredSpec = filterBank.get()->filterSpectrum(spectralFrames);
244
245 // SuperFlux object
246 auto onsetDetector = std::make_unique<SuperFluxOnsetDetection>(filteredSpec, sampleRate, hopSize);
247 // Setup parameters for onset Detection
248 // Initialise OnsetDetection Parameters
249 onsetDetector.get()->setParametersMS(
250 jmap<float>(m_AnalysisCoefficients.coeff0, 0.f, 1.f, 30e-3f, 100e-3f) /*preMax*/,
251 jmap<float>(m_AnalysisCoefficients.coeff1, 0.f, 1.f, 30e-3f, 100e-3f) /*postMax*/,
252 jmap<float>(m_AnalysisCoefficients.coeff2, 0.f, 1.f, 100e-3f, 200e-3f) /*preAvg*/,
253 jmap<float>(m_AnalysisCoefficients.coeff3, 0.f, 1.f, 35e-3f, 140e-3f) /*postAvg*/,
254 jmap<float>(m_AnalysisCoefficients.coeff4, 0.f, 1.f, 30e-3f, 100e-3f)) /*combWidth*/;
255 onsetDetector.get()->setDeltaPercentage(jmap<float>(m_AnalysisCoefficients.coeff7, 0.f, 1.f, 0.1f, 1.5f));
256
257 // Get Onset positions in samples
258 auto onsetPositions = onsetDetector.get()->findOnsetsInSamples();
259
260 return onsetPositions;
261 }
262
263 std::vector<AudioDescriptor> KrotosAudioBufferDSP::getDescriptorsTime(
264 const std::vector<std::vector<float>>& timeDomainSlices, const std::vector<int>& onsetPositions)
265 {
266 AudioDescriptor grainDescription;
267
268 std::vector<AudioDescriptor> descriptorsTime;
269
270 for (int i = 0; i < onsetPositions.size(); i++)
271 {
272 // Update descriptors with analysis values
273 grainDescription.audioIndex = onsetPositions.at(i); // grain Index = start time
274
275 // Update descriptors with analysis values
276 grainDescription.grainSize =
277 static_cast<float>(timeDomainSlices.at(i).size()); // grain Size = analysis Frame size
278
279 descriptorsTime.push_back(grainDescription);
280 }
281 return descriptorsTime;
282 }
283
284 std::vector<std::vector<float>> KrotosAudioBufferDSP::mfccFeatureExtraction(
285 std::vector<std::vector<float>>& timeDomainSlices, const std::vector<int>& onsetPositions)
286 {
287 // STFT parameters
288 const int analysisFrameSize{2048};
289 const int hopSize{1024};
290 // STFT object
291 auto m_stft = std::make_unique<ShortTimeFourierTransform>(
292 analysisFrameSize, analysisFrameSize, hopSize, WindowType::Hann,
294
295 // freqDomain analysis framework object
296 auto freqAnalysisFramework = std::make_unique<FrequencyDomainAnalysisFramework>(
297 (analysisFrameSize), static_cast<int>(getSourceSampleRate()));
298
299 const auto n_features = freqAnalysisFramework.get()->getNumberOfMelFrequencyCepstralCoefficients();
300 std::vector<std::vector<float>> features;
301 std::vector<float> mfcc(n_features, 0.0f);
302
303 for (int i = 0; i < onsetPositions.size(); i++)
304 {
305 float* dataPtrs[1] = {timeDomainSlices.at(i).data()};
306 AudioBuffer<float> bufferSlice(dataPtrs, 1, static_cast<int>(timeDomainSlices.at(i).size()));
307 auto spectralFrames = m_stft.get()->stft(bufferSlice);
308
309 for (int j = 0; j < spectralFrames.size(); j++)
310 {
311 // Pass each frame to the frequencyAnalysisFramework to analyse
312 freqAnalysisFramework->setSpectralFrame(spectralFrames.at(j));
313
314 // Get MFCC features and pass to PCA
315 std::vector<float> mfccTemp = freqAnalysisFramework->getMFCC();
316 // Sum them up
317 transform(mfcc.begin(), mfcc.end(), mfccTemp.begin(), mfcc.begin(), std::plus<float>());
318 }
319
320 // Averaging each slice
321 transform(mfcc.begin(), mfcc.end(), mfcc.begin(),
322 [=](float j) { return (j / static_cast<float>(spectralFrames.size())); });
323 features.push_back(mfcc);
324
325 fill(mfcc.begin(), mfcc.end(), 0.0f);
326 }
327 return features;
328 }
329
330 std::vector<std::vector<float>> KrotosAudioBufferDSP::temporalFeatureExtraction(
331 std::vector<std::vector<float>>& timeDomainSlices)
332 {
333 std::vector<std::vector<float>> features;
334
335 for (const auto& segment : timeDomainSlices)
336 {
337 float ss = 0.f;
338 float temporal_centroid = 0.f;
339
340 for (size_t i = 0; i < segment.size(); ++i)
341 {
342 const auto value = (segment[i] * segment[i]);
343 temporal_centroid += (i * value);
344 ss += value;
345 }
346 const auto level = 10.f * log10(std::max(ss / segment.size(), 1e-6f)); // dB
347 temporal_centroid /= std::max(ss, 1e-5f); // [0, segment.size()]
348 features.emplace_back(std::initializer_list<float>{temporal_centroid, level, 1.f, level});
349 }
350 return features;
351 }
352
354 std::vector<std::vector<float>>& timeDomainSlices)
355 {
356 std::vector<std::vector<float>> features;
357 // Initialise the STFT parameters
358 const int analysisFrameSize{2048};
359 const int hopSize{1024};
360
361 // Initialise STFT object
362 auto m_stft = std::make_unique<ShortTimeFourierTransform>(
363 analysisFrameSize, analysisFrameSize, hopSize, WindowType::Hann,
365
366 // Initialise TimeDomain analysis framework object
367 auto timeAnalysisFramework = std::make_unique<TimeDomainAnalysisFramework>(
368 analysisFrameSize, static_cast<int>(getSourceSampleRate()));
369
370 // Initialise FreqDomain analysis framework object
371 auto freqAnalysisFramework = std::make_unique<FrequencyDomainAnalysisFramework>(
372 (analysisFrameSize), static_cast<int>(getSourceSampleRate()));
373 freqAnalysisFramework.get()->setFrequencyVector(
374 m_stft.get()->getBinsSTL(ShortTimeFourierTransform::FrequencyRange::Half));
375
376 for (const auto& segment : timeDomainSlices)
377 {
378 //++++++++++++++++++++++++++ Perform Time Domain Analysis
379 //+++++++++++++++++++++++++++++++++++++++++++++++++ Get RMS Pass
380 // each frame to the timeAnalysisFramework to analyse
381 timeAnalysisFramework->setSignalFrame(segment);
382 // Update descriptors with analysis values
383 float RMS = timeAnalysisFramework->getRMS();
384
385 //++++++++++++++++++++++++++ Perform Frequency Domain Analysis
386 //+++++++++++++++++++++++++++++++++++++++++++++ Retrieve all the
387 // spectral frames from STFT for each onset segment
388 AudioSampleBuffer segmentBuffer(1, static_cast<int>(segment.size()));
389 segmentBuffer.copyFrom(0, 0, segment.data(), static_cast<int>(segment.size()));
390 auto spectralFrames = m_stft.get()->stft(segmentBuffer);
391
392 float centroid = 0.0f;
393
394 for (const auto& spectralSegment : spectralFrames)
395 {
396 // Pass each frame to the frequencyAnalysisFramework to analyse
397 freqAnalysisFramework->setSpectralFrame(spectralSegment);
398
399 // Get centroids
400 centroid += freqAnalysisFramework->getSpectralCentroid();
401 }
402
403 // Average centroids for this onsetSegment
404 float avgCentroid = centroid / spectralFrames.size();
405 // Update features
406 features.emplace_back(std::initializer_list<float>{avgCentroid, RMS, 1.f, 1.f});
407
408 segmentBuffer.clear();
409 }
410
411 return features;
412 }
413
415 std::vector<std::vector<float>>& timeDomainSlices)
416 {
417 std::vector<std::vector<float>> features;
418 // Initialise the STFT parameters
419 const int analysisFrameSize{2048};
420 const int hopSize{1024};
421
422 // Initialise STFT object
423 auto m_stft = std::make_unique<ShortTimeFourierTransform>(
424 analysisFrameSize, analysisFrameSize, hopSize, WindowType::Hann,
426
427 // Initialise TimeDomain analysis framework object
428 auto timeAnalysisFramework = std::make_unique<TimeDomainAnalysisFramework>(
429 analysisFrameSize, static_cast<int>(getSourceSampleRate()));
430
431 // Initialise FreqDomain analysis framework object
432 auto freqAnalysisFramework = std::make_unique<FrequencyDomainAnalysisFramework>(
433 (analysisFrameSize), static_cast<int>(getSourceSampleRate()));
434 freqAnalysisFramework.get()->setFrequencyVector(
435 m_stft.get()->getBinsSTL(ShortTimeFourierTransform::FrequencyRange::Half));
436
437 for (const auto& segment : timeDomainSlices)
438 {
439 //++++++++++++++++++++++++++ Perform Time Domain Analysis
440 //+++++++++++++++++++++++++++++++++++++++++++++++++ Get RMS Pass
441 // each frame to the timeAnalysisFramework to analyse
442 timeAnalysisFramework->setSignalFrame(segment);
443 // Update descriptors with analysis values
444 float RMS = timeAnalysisFramework->getRMS();
445
446 //++++++++++++++++++++++++++ Perform Frequency Domain Analysis
447 //+++++++++++++++++++++++++++++++++++++++++++++ Retrieve all the
448 // spectral frames from STFT for each onset segment
449 AudioSampleBuffer segmentBuffer(1, static_cast<int>(segment.size()));
450 segmentBuffer.copyFrom(0, 0, segment.data(), static_cast<int>(segment.size()));
451 auto spectralFrames = m_stft.get()->stft(segmentBuffer);
452
453 float centroid = 0.0f;
454
455 for (const auto& spectralSegment : spectralFrames)
456 {
457 // Pass each frame to the frequencyAnalysisFramework to analyse
458 freqAnalysisFramework->setSpectralFrame(spectralSegment);
459
460 // Get centroids
461 centroid += freqAnalysisFramework->getSpectralFlatness();
462 }
463
464 // Average centroids for this onsetSegment
465 float avgCentroid = centroid / spectralFrames.size();
466 // Update features
467 features.emplace_back(std::initializer_list<float>{avgCentroid, RMS, 1.f, 1.f});
468
469 segmentBuffer.clear();
470 }
471
472 return features;
473 }
474
476 std::vector<std::vector<float>>& timeDomainSlices)
477 {
478 std::vector<std::vector<float>> features;
479 // Initialise the analysis parameters
480 const int analysisFrameSize{2048};
481
482 // Initialise ERB object
483 std::unique_ptr<ERB_FFTSpectrogram> m_erb = std::make_unique<ERB_FFTSpectrogram>();
484 m_erb.get()->setSampleRate(getSourceSampleRate());
485
486 // Initialise TimeDomain analysis framework object
487 auto timeAnalysisFramework = std::make_unique<TimeDomainAnalysisFramework>(
488 analysisFrameSize, static_cast<int>(getSourceSampleRate()));
489
490 // Initialise FreqDomain analysis framework object
491 auto freqAnalysisFramework = std::make_unique<FrequencyDomainAnalysisFramework>(
492 (analysisFrameSize), static_cast<int>(getSourceSampleRate()));
493 freqAnalysisFramework.get()->setFrequencyVector(m_erb.get()->getERBSpace());
494
495 for (auto& segment : timeDomainSlices)
496 {
497 //++++++++++++++++++++++++++ Perform Time Domain Analysis
498 //+++++++++++++++++++++++++++++++++++++++++++++++++ Get RMS Pass
499 // each frame to the timeAnalysisFramework to analyse
500 timeAnalysisFramework->setSignalFrame(segment);
501 // Update descriptors with analysis values
502 float RMS = timeAnalysisFramework->getRMS();
503
504 //++++++++++++++++++++++++++ Perform Frequency Domain Analysis
505 //+++++++++++++++++++++++++++++++++++++++++++++ Pass each segment to
506 // the ERB object to transform
507 auto erbFrames = m_erb.get()->filterSpectrum(segment);
508
509 float centroid = 0.0f;
510
511 for (const auto& spectralSegment : erbFrames)
512 {
513 // Pass each frame to the frequencyAnalysisFramework to analyse
514 freqAnalysisFramework->setSpectralFrame(spectralSegment);
515
516 // Get centroids
517 centroid += freqAnalysisFramework->getSpectralCentroid();
518 }
519
520 // Average centroids for this onsetSegment
521 float avgCentroid = centroid / erbFrames.size();
522 // Update features
523 features.emplace_back(std::initializer_list<float>{avgCentroid, RMS, 1.f, 1.f});
524 }
525
526 return features;
527 }
528
530 std::vector<std::vector<float>>& timeDomainSlices)
531 {
532 std::vector<std::vector<float>> features;
533 // Initialise the analysis parameters
534 const int analysisFrameSize{2048};
535
536 // Initialise ERB object
537 std::unique_ptr<ERB_FFTSpectrogram> m_erb = std::make_unique<ERB_FFTSpectrogram>();
538 m_erb.get()->setSampleRate(getSourceSampleRate());
539
540 // Initialise FreqDomain analysis framework object
541 auto freqAnalysisFramework = std::make_unique<FrequencyDomainAnalysisFramework>(
542 (analysisFrameSize), static_cast<int>(getSourceSampleRate()));
543 freqAnalysisFramework.get()->setFrequencyVector(m_erb.get()->getERBSpace());
544
545 for (auto& segment : timeDomainSlices)
546 {
547 //++++++++++++++++++++++++++ Perform Frequency Domain Analysis
548 //+++++++++++++++++++++++++++++++++++++++++++++ Pass each segment to
549 // the ERB object to transform
550 auto erbFrames = m_erb.get()->filterSpectrum(segment);
551
552 float centroid = 0.0f;
553 float flatness = 0.0f;
554
555 for (const auto& spectralSegment : erbFrames)
556 {
557 // Pass each frame to the frequencyAnalysisFramework to analyse
558 freqAnalysisFramework->setSpectralFrame(spectralSegment);
559
560 // Set vector of frequencies to calculate spectral feature on
561
562 // Get centroids
563 centroid += freqAnalysisFramework->getSpectralCentroid();
564
565 // Get flatness
566 flatness += freqAnalysisFramework->getSpectralFlatness();
567 }
568
569 // Average centroids for this onsetSegment
570 float avgCentroid = centroid / erbFrames.size();
571
572 // Average Flatness for this onsetSegment
573 float avgFlatness = flatness / erbFrames.size();
574
575 // Update features
576 features.emplace_back(std::initializer_list<float>{avgCentroid, avgFlatness, 1.f, 1.f});
577 }
578
579 return features;
580 }
581
583 std::vector<std::vector<float>>& timeDomainSlices)
584 {
585 std::vector<std::vector<float>> features;
586
587 for (const auto& segment : timeDomainSlices)
588 {
589
590 // Careful here !! Works only for overlapping windows of same length
591 auto timeAnalysisFramework = std::make_unique<TimeDomainAnalysisFramework>(
592 static_cast<int>(segment.size()), static_cast<int>(getSourceSampleRate()));
593 //++++++++++++++++++++++++++ Perform Time Domain Analysis
594 //+++++++++++++++++++++++++++++++++++++++++++++++++ Get RMS Pass
595 // each frame to the timeAnalysisFramework to analyse
596 timeAnalysisFramework->setSignalFrame(segment);
597 // Update descriptors with analysis values
598 float RMS = timeAnalysisFramework->getRMS();
599 // Get Pitch Mcleod
600 float pitch = timeAnalysisFramework->getPitchAutocorrelation();
601 // Get Peak Energy
602 float peakEnergy = timeAnalysisFramework->getPeakEnergy();
603
604 // Update features
605 features.emplace_back(std::initializer_list<float>{pitch, peakEnergy, RMS, 1.f});
606
607 timeAnalysisFramework.release();
608 }
609
610 return features;
611 }
612
613 std::vector<std::vector<float>> KrotosAudioBufferDSP::harmonicPitchEstimation(
614 std::vector<std::vector<float>>& timeDomainSlices)
615 {
616 // Place holder for Harmonic Signal Representation. At the moment useful only for debugging SWIPE Pitch
617 // estimation algorithm.
618
619 auto harmonicRep = std::make_unique<HarmonicRepresentation>(getSourceSampleRate());
620
621 // TODO: Once porting to Eigen is completed use this to concatenate all matrices returned from
622 // harmonic signal representation
623 // Resize harmRep.value if necessary to accommodate the concatenated matrix
624 /*
625 if (harmRepValue.rows() == 0)
626 {
627 harmRepValue = concatenatedMatrix;
628 }
629 else
630 {
631 harmRepValue.conservativeResize(harmRepValue.rows() + concatenatedMatrix.rows(), Eigen::NoChange);
632 harmRepValue.bottomRows(concatenatedMatrix.rows()) = concatenatedMatrix;
633 }
634 */
635 // Call calculate pitch for each segment
636 for (auto& timeSlice : timeDomainSlices)
637 {
638 auto harmonicPair = harmonicRep->processSignal(timeSlice);
639 }
640
641 // Return just the timeDomain signals, no feature extraction atm!!
642 return timeDomainSlices;
643 }
644
645 void KrotosAudioBufferDSP::applyPCA(std::vector<std::vector<float>>& features, const size_t& n_components)
646 {
647 m_principalComponentAnalysis = std::make_unique<PCA>();
648 for (const auto& feature : features)
649 {
650 m_principalComponentAnalysis->insert(feature);
651 }
652
653 m_principalComponentAnalysis->fit(n_components);
654 for (size_t i = 0; i < features.size(); ++i)
655 {
656 features[i] = m_principalComponentAnalysis->getPrincipalComponents(i);
657 }
658 }
659
660 void KrotosAudioBufferDSP::setFeaturesAndNormalise(const std::vector<std::vector<float>>& features,
661 std::vector<AudioDescriptor>& descriptorsTime)
662 {
663 size_t i = 0;
664 for (const auto& feature : features)
665 {
666 descriptorsTime.at(i).principalX = feature.at(0);
667 descriptorsTime.at(i).principalY = feature.at(1);
668 descriptorsTime.at(i).principalZ = feature.at(2);
669 descriptorsTime.at(i).principalQ = feature.at(3);
670 ++i;
671 }
673 descriptorsTime, [](AudioDescriptor& feat) { return &feat.principalX; }, 0.08f);
675 descriptorsTime, [](AudioDescriptor& feat) { return &feat.principalY; }, 0.08f);
677 [](AudioDescriptor& feat) { return &feat.principalZ; });
678 normaliseAudioDescriptorFeature(descriptorsTime, [](AudioDescriptor& feat) { return &feat.principalQ; });
679
680 std::sort(descriptorsTime.begin(), descriptorsTime.end(),
681 [](AudioDescriptor& a, AudioDescriptor& b) { return a.principalZ > b.principalZ; });
682 }
683
684 void KrotosAudioBufferDSP::buildKNNIndex(const std::vector<AudioDescriptor>& descriptorsTime)
685 {
686 m_nnSearch = std::make_unique<krotos::KDTree>();
687 for (auto& grain : descriptorsTime)
688 {
689 m_nnSearch->addDatasetItem(grain.principalX, grain.principalY);
690 }
691 m_nnSearch->buildIndex();
692 }
693
694 void KrotosAudioBufferDSP::buildKNNIndexWithZ(const std::vector<AudioDescriptor>& descriptorsTime)
695 {
696 m_nnSearch = std::make_unique<krotos::KDTree>();
697 for (auto& grain : descriptorsTime)
698 {
699 m_nnSearch->addDatasetItem(grain.principalX, grain.principalY, grain.principalZ);
700 }
701 m_nnSearch->buildIndex();
702 }
703
704 void KrotosAudioBufferDSP::sortByZ(const std::vector<AudioDescriptor>& descriptorsTime)
705 {
706 // Creating a version sorted by Z to aid with back to front blob
707 // drawing in the grain display
708 // std::vector<AudioDescriptor> byZ = descriptorsTime;
709 // std::sort(byZ.begin(), byZ.end(), [](AudioDescriptor& a,
710 // AudioDescriptor& b) { return a.principalC > b.principalC; });
711
712 if (descriptorsTime.size() > 0)
713 {
714 // Temporary everything sorted by Z for graphics
715 m_grainDescriptionByFrequency = descriptorsTime;
716 m_grainDescriptionByTime = descriptorsTime;
717 m_grainDescriptionByZ = descriptorsTime;
718 }
719 }
720
722 {
723 m_timeDomainSlices.clear();
724 m_timeDomainSlices.shrink_to_fit();
725 m_onsetPositions.clear();
726 m_onsetPositions.shrink_to_fit();
727 m_descriptorsTime.clear();
728 m_descriptorsTime.shrink_to_fit();
730 m_grainDescriptionByFrequency.shrink_to_fit();
732 m_grainDescriptionByTime.shrink_to_fit();
733 m_grainDescriptionByZ.clear();
734 m_grainDescriptionByZ.shrink_to_fit();
735 }
736
738 {
739 if (threadShouldExit())
740 return;
741
742 if (this->size() <= 4)
743 return; // won't fail after 1 successful load? discussed w/sandy
744 if (this->getSourceSampleRate() == 0.0f)
745 return; // edge case of loaded file thats a missing path
746
747 // Average Buffer to mono
748 std::vector<float> bufferVector = averageBufferToMono();
749
751
752 // Clear vectors of onsetPositions and Slices
753 m_timeDomainSlices.clear();
754 m_onsetPositions.clear();
755 m_descriptorsTime.clear();
756
757 if (threadShouldExit())
758 return;
759
760 // Perform Segmentation according to method
761 m_progressTracker.stage("Segment");
762 switch (m_segmentationMethod)
763 {
765 // Return whole buffer
766 m_timeDomainSlices.push_back(bufferVector);
767 m_onsetPositions.push_back(0);
768 break;
769 }
771 // Initialise the STFT parameters
772 auto exponent =
773 static_cast<float>(roundToInt(jmap<float>(m_AnalysisCoefficients.coeff0, 0.f, 1.f, 0.f, 4.f)));
774 int analysisFrameSize = static_cast<int>(powf(2.0f, exponent)) * 512;
775 float hopFactor = m_AnalysisCoefficients.coeff1;
776 if (hopFactor <= 0.1f)
777 hopFactor = 0.1f;
778
779 int hopSizeSamples = static_cast<int>(static_cast<float>(analysisFrameSize) * hopFactor);
780
781 m_onsetPositions = chunk(bufferVector, analysisFrameSize, hopSizeSamples);
782 m_timeDomainSlices = segmentChunks(m_onsetPositions, bufferVector, analysisFrameSize);
783 break;
784 }
788 break;
789 }
791 const auto window_length = static_cast<size_t>(getSourceSampleRate()); // 1 second audio clips
792 const auto hop_length = window_length / 2; // ...that overlap
793 m_onsetPositions = chunk(bufferVector, window_length, hop_length);
794 m_timeDomainSlices = segmentChunks(m_onsetPositions, bufferVector, window_length);
795 break;
796 }
798 // Contains slices of the buffer produced by the onset positions
801 break;
802 }
803 default: {
804 jassertfalse;
805 break;
806 }
807 }
808
809 if (threadShouldExit())
810 return;
811
812 // Feature extraction results
813 std::vector<std::vector<float>> features;
814
815 // Perform Feature Extraction according to method
816 m_progressTracker.stage("Feature Extraction");
817 switch (m_featureMethod)
818 {
822 m_grainDescriptionByZ.clear();
823 break;
824 }
827 applyPCA(features, 4);
828 break;
829 }
832 break;
833 }
836 break;
837 }
840 break;
841 }
844 break;
845 }
848 break;
849 }
852 break;
853 }
854 default: {
855 jassertfalse;
856 break;
857 }
858 }
859 // Get time descriptors
861
862 m_progressTracker.stage("Normalise Features");
864 if (threadShouldExit())
865 return;
866
867 m_progressTracker.stage("Build KNN Index");
868 if (!m_descriptorsTime.empty())
870 if (threadShouldExit())
871 return;
872
873 m_progressTracker.stage("Sort By Z");
875 if (threadShouldExit())
876 return;
877
878 m_progressTracker.end("Analysis Complete"); // Call this when you are done - message
879 // probably so briefly on screen it wont
880 // be readable
881
882 validate(); // Call this at the very end of analysis, when we are guaranteed the results are valid
883 }
884
886 {
887 return a.audioIndex < b.audioIndex;
888 }
889
891 {
892 return a.frequency < b.frequency;
893 }
894
896 {
897 const auto result = std::lower_bound(m_grainDescriptionByTime.begin(), m_grainDescriptionByTime.end(),
898 AudioDescriptor{0, 0, audioIndex, 0}, &compareByAudioIndex);
899
900 if (result == m_grainDescriptionByTime.begin())
901 {
902 return m_grainDescriptionByTime[0];
903 }
904 else
905 {
906 const auto retval = result - 1;
907
908 if (retval >= m_grainDescriptionByTime.end())
909 {
910 return m_grainDescriptionByTime[0];
911 }
912 else
913 {
914 return *retval;
915 }
916 }
917 }
918
920 {
921 const auto result = std::lower_bound(m_grainDescriptionByFrequency.begin(), m_grainDescriptionByFrequency.end(),
922 AudioDescriptor{frequency, 0, 0, 0}, &compareByAudioFrequency);
923
924 if (result == m_grainDescriptionByFrequency.begin())
925 {
927 }
928 else
929 {
930 const auto retval = result - 1;
931
932 if (retval >= m_grainDescriptionByFrequency.end())
933 {
935 }
936 else
937 {
938 return *retval;
939 }
940 }
941 }
942
944 {
945 if (index >= m_grainDescriptionByTime.size())
946 index = m_grainDescriptionByTime.size() - 1;
947 }
948
950 {
951 if (!m_grainDescriptionByTime.size())
952 {
953 jassertfalse; // You called this method with an empty grainDescriptionByTime vector
954 return StereoSample{0.f, 0.f};
955 }
956 auto grain = size_t(grainPhase);
957 double phase = grainPhase - double(grain);
959 return getInterpolatedSample(static_cast<double>(m_grainDescriptionByTime[grain].audioIndex) +
960 (static_cast<double>(m_grainDescriptionByTime[grain].grainSize) * phase));
961 }
962
964 {
965 auto grain = size_t(grainPhase);
966 double phase = grainPhase - static_cast<double>(grain);
967 auto nextGrain = grain + 1;
970 return juce::jmap<double>(phase, 0.0, 1.0, static_cast<double>(m_grainDescriptionByTime[grain].frequency),
971 static_cast<double>(m_grainDescriptionByTime[nextGrain].frequency));
972 }
973
975 {
976 return audioIndexToDescriptor(int(float(getNumSamples()) * audioPercent));
977 }
978
980
981} // namespace krotos
ProgressTracker & getProgressTracker()
Get a reference to this buffer's ProgressTracker.
Definition KrotosAudioBufferDSP.cpp:24
void run() override
Definition KrotosAudioBufferDSP.cpp:71
ProgressTracker m_progressTracker
Definition KrotosAudioBufferDSP.h:562
void timerCallback() override
Definition KrotosAudioBufferDSP.cpp:26
std::vector< std::vector< float > > erbSpecCentroidRMS_FeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:475
std::vector< int > chunk(const std::vector< float > &bufferVector, std::size_t window_length, std::size_t hop_length)
Definition KrotosAudioBufferDSP.cpp:167
std::vector< std::vector< float > > mfccFeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices, const std::vector< int > &onsetPositions)
Definition KrotosAudioBufferDSP.cpp:284
std::unique_ptr< PCA > m_principalComponentAnalysis
Definition KrotosAudioBufferDSP.h:581
const int ANALYSIS_TIMER_PERIOD
Definition KrotosAudioBufferDSP.h:509
std::unique_ptr< KDTree > m_nnSearch
Definition KrotosAudioBufferDSP.h:579
std::vector< int > m_onsetPositions
Definition KrotosAudioBufferDSP.h:590
AnalysisScheme getAnalysisScheme()
Definition KrotosAudioBufferDSP.h:500
SegmentationMethod m_segmentationMethod
Definition KrotosAudioBufferDSP.h:556
bool analysisResultsAreValid()
Definition KrotosAudioBufferDSP.h:422
std::vector< std::vector< float > > pitchPeakRMSFeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:582
double getFrequencyFromGrainPhase(double grainPhase)
Definition KrotosAudioBufferDSP.cpp:963
void setFeaturesAndNormalise(const std::vector< std::vector< float > > &features, std::vector< AudioDescriptor > &descriptorsTime)
Definition KrotosAudioBufferDSP.cpp:660
void generalScheme()
Definition KrotosAudioBufferDSP.cpp:737
std::vector< std::vector< float > > erbSpecCentroidFlatness_FeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:529
AnalysisCoefficients & getAnalysisCoefficients()
Definition KrotosAudioBufferDSP.cpp:979
std::vector< AudioDescriptor > m_descriptorsTime
Definition KrotosAudioBufferDSP.h:591
std::vector< int > envelopeOnsetDetection(const std::vector< float > &bufferVector, float samplerate)
Definition KrotosAudioBufferDSP.cpp:188
std::vector< AudioDescriptor > getDescriptorsTime(const std::vector< std::vector< float > > &timeDomainSlices, const std::vector< int > &onsetPositions)
Definition KrotosAudioBufferDSP.cpp:263
void validate()
Definition KrotosAudioBufferDSP.h:416
volatile std::atomic< bool > m_requestDisplayCacheRegeneration
Definition KrotosAudioBufferDSP.h:569
void buildKNNIndex(const std::vector< AudioDescriptor > &descriptorsTime)
Definition KrotosAudioBufferDSP.cpp:684
std::vector< std::vector< float > > specCntrRMS_FeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:353
std::vector< std::vector< float > > m_timeDomainSlices
Definition KrotosAudioBufferDSP.h:589
void analyse()
analyse Analyses the audio waveform stored in the buffer according to the selected scheme This method...
Definition KrotosAudioBufferDSP.cpp:35
void invalidate()
Definition KrotosAudioBufferDSP.h:414
bool shouldRegenerateDisplayCache()
Definition KrotosAudioBufferDSP.cpp:28
AudioDescriptor & audioIndexToDescriptor(int audioIndex)
Definition KrotosAudioBufferDSP.cpp:895
std::vector< int > superFluxOnsetDetection(float samplerate)
Definition KrotosAudioBufferDSP.cpp:225
int size()
Definition KrotosAudioBufferDSP.h:394
std::vector< AudioDescriptor > m_grainDescriptionByFrequency
Definition KrotosAudioBufferDSP.h:572
KrotosAudioBufferDSP()
Definition KrotosAudioBufferDSP.cpp:18
std::vector< std::vector< float > > specFlatnessRMS_FeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:414
std::vector< std::vector< float > > temporalFeatureExtraction(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:330
void buildKNNIndexWithZ(const std::vector< AudioDescriptor > &descriptorsTime)
Definition KrotosAudioBufferDSP.cpp:694
AnalysisCoefficients m_AnalysisCoefficients
Definition KrotosAudioBufferDSP.h:560
void cleanupAnalysis()
Definition KrotosAudioBufferDSP.cpp:721
std::vector< AudioDescriptor > m_grainDescriptionByTime
Definition KrotosAudioBufferDSP.h:575
AudioDescriptor & audioFrequencyToDescriptor(float frequency)
Definition KrotosAudioBufferDSP.cpp:919
StereoSample getSampleFromGrainPhase(double grainPhase)
Definition KrotosAudioBufferDSP.cpp:949
AudioDescriptor & audioPercentToDescriptor(float audioPercent)
Definition KrotosAudioBufferDSP.cpp:974
std::vector< std::vector< float > > segmentChunks(std::vector< int > onsetPositions, std::vector< float > signalBufferMono, std::size_t window_length)
Definition KrotosAudioBufferDSP.cpp:148
void applyPCA(std::vector< std::vector< float > > &features, const std::size_t &n_components)
Definition KrotosAudioBufferDSP.cpp:645
FeatureExtrMethod m_featureMethod
Definition KrotosAudioBufferDSP.h:557
std::vector< std::vector< float > > harmonicPitchEstimation(std::vector< std::vector< float > > &timeDomainSlices)
Definition KrotosAudioBufferDSP.cpp:613
void clampGrainDescriptionByTimeIndex(size_t &index)
Definition KrotosAudioBufferDSP.cpp:943
std::vector< float > averageBufferToMono()
Definition KrotosAudioBufferDSP.cpp:102
std::vector< std::vector< float > > segmentOnsetSlices(std::vector< int > onsetPositions, std::vector< float > signalBufferMono)
Definition KrotosAudioBufferDSP.cpp:124
void analysePhase()
Definition KrotosAudioBufferDSPPhaseDetection.cpp:298
void sortByZ(const std::vector< AudioDescriptor > &descriptorsTime)
Definition KrotosAudioBufferDSP.cpp:704
std::vector< AudioDescriptor > m_grainDescriptionByZ
Definition KrotosAudioBufferDSP.h:577
float getSourceSampleRate(void)
Definition KrotosAudioBuffer.cpp:510
StereoSample getInterpolatedSample(double index)
Get a stereo sample from the audio buffer.
Definition KrotosAudioBuffer.cpp:318
ProgressTracker - a class to help with display of a progresas bar during audio analysis.
Definition KrotosAudioBufferDSP.h:64
void setNumStages(int numStages)
Definition KrotosAudioBufferDSP.h:73
void end(String finalMessage)
Definition KrotosAudioBufferDSP.h:91
void stage(String message)
Definition KrotosAudioBufferDSP.h:83
@ freqMagOnly
Definition ShortTimeFourierTransform.h:20
Definition KrotosAudioBuffer.h:16
Definition AirAbsorptionFilter.cpp:2
void normaliseAudioDescriptorFeature(std::vector< AudioDescriptor > &decriptors, Functor functor, float border=0.f)
Normalise a chosen field within a vector of AudioDescriptors.
Definition KrotosAudioBufferDSP.h:197
static bool compareByAudioIndex(const AudioDescriptor &a, const AudioDescriptor &b)
Definition KrotosAudioBufferDSP.cpp:885
void normaliseAndInvertAudioDescriptorFeature(std::vector< AudioDescriptor > &decriptors, Functor2 functor, float border=0.f)
Normalise then invert a chosen field within a vector of AudioDescriptors.
Definition KrotosAudioBufferDSP.h:240
static bool compareByAudioFrequency(const AudioDescriptor &a, const AudioDescriptor &b)
Definition KrotosAudioBufferDSP.cpp:890
AnalysisCoefficients - A class containing attributes used during analysis of audio.
Definition KrotosAudioBufferDSP.h:42
float coeff0
Definition KrotosAudioBufferDSP.h:43
float coeff1
Definition KrotosAudioBufferDSP.h:44
float coeff7
Definition KrotosAudioBufferDSP.h:50
float coeff3
Definition KrotosAudioBufferDSP.h:46
float coeff2
Definition KrotosAudioBufferDSP.h:45
float coeff4
Definition KrotosAudioBufferDSP.h:47
A class to contains any attributes we want to store for the described audio.
Definition KrotosAudioBufferDSP.h:177
float principalQ
Definition KrotosAudioBufferDSP.h:186
float principalZ
Definition KrotosAudioBufferDSP.h:185
int audioIndex
Definition KrotosAudioBufferDSP.h:180
float principalX
Definition KrotosAudioBufferDSP.h:183
float principalY
Definition KrotosAudioBufferDSP.h:184
float frequency
Definition KrotosAudioBufferDSP.h:178
float grainSize
Definition KrotosAudioBufferDSP.h:181