Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SlicerT::findSlices - check if lower_bound did not find anything #7214

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions plugins/SlicerT/SlicerT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,113 +157,114 @@
// uses the spectral flux to determine the change in magnitude
// resources:
// http://www.iro.umontreal.ca/~pift6080/H09/documents/papers/bello_onset_tutorial.pdf
void SlicerT::findSlices()
{
if (m_originalSample.sampleSize() <= 1) { return; }
m_slicePoints = {};

const int windowSize = 512;
const float minBeatLength = 0.05f; // in seconds, ~ 1/4 length at 220 bpm

int sampleRate = m_originalSample.sampleRate();
int minDist = sampleRate * minBeatLength;

float maxMag = -1;
std::vector<float> singleChannel(m_originalSample.sampleSize(), 0);
for (int i = 0; i < m_originalSample.sampleSize(); i++)
{
singleChannel[i] = (m_originalSample.data()[i][0] + m_originalSample.data()[i][1]) / 2;
maxMag = std::max(maxMag, singleChannel[i]);
}

// normalize and find 0 crossings
std::vector<int> zeroCrossings;
float lastValue = 1;
for (int i = 0; i < singleChannel.size(); i++)
{
singleChannel[i] /= maxMag;
if (sign(lastValue) != sign(singleChannel[i]))
{
zeroCrossings.push_back(i);
lastValue = singleChannel[i];
}
}

std::vector<float> prevMags(windowSize / 2, 0);
std::vector<float> fftIn(windowSize, 0);
std::array<fftwf_complex, windowSize> fftOut;

fftwf_plan fftPlan = fftwf_plan_dft_r2c_1d(windowSize, fftIn.data(), fftOut.data(), FFTW_MEASURE);

int lastPoint = -minDist - 1; // to always store 0 first
float spectralFlux = 0;
float prevFlux = 1E-10; // small value, no divison by zero

for (int i = 0; i < singleChannel.size() - windowSize; i += windowSize)
{
// fft
std::copy_n(singleChannel.data() + i, windowSize, fftIn.data());
fftwf_execute(fftPlan);

// calculate spectral flux in regard to last window
for (int j = 0; j < windowSize / 2; j++) // only use niquistic frequencies
{
float real = fftOut[j][0];
float imag = fftOut[j][1];
float magnitude = std::sqrt(real * real + imag * imag);

// using L2-norm (euclidean distance)
float diff = std::sqrt(std::pow(magnitude - prevMags[j], 2));
spectralFlux += diff;

prevMags[j] = magnitude;
}

if (spectralFlux / prevFlux > 1.0f + m_noteThreshold.value() && i - lastPoint > minDist)
{
m_slicePoints.push_back(i);
lastPoint = i;
if (m_slicePoints.size() > 128) { break; } // no more keys on the keyboard
}

prevFlux = spectralFlux;
spectralFlux = 1E-10; // again for no divison by zero
}

m_slicePoints.push_back(m_originalSample.sampleSize());

for (float& sliceValue : m_slicePoints)
{
int closestZeroCrossing = *std::lower_bound(zeroCrossings.begin(), zeroCrossings.end(), sliceValue);
if (std::abs(sliceValue - closestZeroCrossing) < windowSize) { sliceValue = closestZeroCrossing; }
auto closestZeroCrossing = std::lower_bound(zeroCrossings.begin(), zeroCrossings.end(), sliceValue);
if (closestZeroCrossing == zeroCrossings.end()) { continue; }
if (std::abs(sliceValue - *closestZeroCrossing) < windowSize) { sliceValue = *closestZeroCrossing; }
}

float beatsPerMin = m_originalBPM.value() / 60.0f;
float samplesPerBeat = m_originalSample.sampleRate() / beatsPerMin * 4.0f;
int noteSnap = m_sliceSnap.value();
int sliceLock = samplesPerBeat / std::exp2(noteSnap + 1);
if (noteSnap == 0) { sliceLock = 1; }
for (float& sliceValue : m_slicePoints)
{
sliceValue += sliceLock / 2;
sliceValue -= static_cast<int>(sliceValue) % sliceLock;
}

m_slicePoints.erase(std::unique(m_slicePoints.begin(), m_slicePoints.end()), m_slicePoints.end());

for (float& sliceIndex : m_slicePoints)
{
sliceIndex /= m_originalSample.sampleSize();
}

m_slicePoints[0] = 0;
m_slicePoints[m_slicePoints.size() - 1] = 1;

emit dataChanged();
}

// find the bpm of the sample by assuming its in 4/4 time signature ,
// and lies in the 100 - 200 bpm range

Check notice on line 267 in plugins/SlicerT/SlicerT.cpp

View check run for this annotation

codefactor.io / CodeFactor

plugins/SlicerT/SlicerT.cpp#L160-L267

Complex Method
void SlicerT::findBPM()
{
if (m_originalSample.sampleSize() <= 1) { return; }
Expand Down
Loading