-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Fix time offset in key and beat analysis #2152
Changes from 11 commits
169ca80
1ce3e74
ca7055a
1c0a248
6ec246f
67d8ecc
9939dbd
0af3ddd
0d3ca5f
b099b7a
944702c
e5f7548
e1351b7
a84bfc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,6 @@ bool AnalyzerQueenMaryBeats::initialize(int samplerate) { | |
} | ||
|
||
bool AnalyzerQueenMaryBeats::processSamples(const CSAMPLE* pIn, const int iLen) { | ||
DEBUG_ASSERT(iLen == kAnalysisSamplesPerBlock); | ||
DEBUG_ASSERT(iLen % kAnalysisChannels == 0); | ||
if (!m_pDetectionFunction) { | ||
return false; | ||
|
@@ -63,30 +62,21 @@ bool AnalyzerQueenMaryBeats::processSamples(const CSAMPLE* pIn, const int iLen) | |
} | ||
|
||
bool AnalyzerQueenMaryBeats::finalize() { | ||
// TODO(rryan) if iLen is less than frame size, pad with zeros. Do we need | ||
// flush support? | ||
m_helper.finalize(); | ||
|
||
int nonZeroCount = m_detectionResults.size(); | ||
while (nonZeroCount > 0 && m_detectionResults.at(nonZeroCount - 1) <= 0.0) { | ||
--nonZeroCount; | ||
} | ||
|
||
std::vector<double> df; | ||
std::vector<double> beatPeriod; | ||
std::vector<double> beatPeriod(nonZeroCount); | ||
std::vector<double> tempi; | ||
|
||
df.reserve(nonZeroCount); | ||
beatPeriod.reserve(nonZeroCount); | ||
|
||
// NOTE(rryan): The VAMP plugin skipped the first 2 detection function | ||
// results so I do as well. Not sure why. | ||
for (int i = 2; i < nonZeroCount; ++i) { | ||
for (int i = 0; i < nonZeroCount; ++i) { | ||
df.push_back(m_detectionResults.at(i)); | ||
beatPeriod.push_back(0.0); | ||
} | ||
|
||
if (df.empty()) { | ||
return false; | ||
} | ||
|
||
TempoTrackV2 tt(m_iSampleRate, kStepSize); | ||
|
@@ -95,10 +85,31 @@ bool AnalyzerQueenMaryBeats::finalize() { | |
std::vector<double> beats; | ||
tt.calculateBeats(df, beatPeriod, beats); | ||
|
||
m_resultBeats.resize(beats.size()); | ||
double* result = (double*)&m_resultBeats.at(0); | ||
for (size_t i = 0; i < beats.size(); ++i) { | ||
result[i] = beats[i] * kStepSize; | ||
// In some tracks a beat at 0:00 is detected when a noise floor starts. | ||
// Here we check the level and the position for plausibility and remove | ||
// the beat if this is the case. | ||
size_t firstBeat = 0; | ||
if (beats.size() >= 3) { | ||
if (m_detectionResults.at(beats.at(0)) < | ||
(m_detectionResults.at(beats.at(0)) + | ||
m_detectionResults.at(beats.at(0))) / 4) { | ||
// the beat is not half es high than the average of the two | ||
// following beats. Skip it. | ||
firstBeat = 1; | ||
} else { | ||
int diff = (beats.at(1) - beats.at(0)) - (beats.at(2) - beats.at(1)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why in the else part? Shouldn't this be applied regardless if firstBeat = 0 or 1, i.e. after shifting the index of the first beat? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ...but then we would shift the first beat once more. Not sure what the intention was here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue I am trying to solve is that the fist beat is often off. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not familiar with the details of beat detection algorithm and would follow your recommendation. Just tried to verify the code and check for inconsistencies or unintended behaviour. |
||
// we don't allow a signifcant tempo change after the first beat | ||
if (diff > 2 || diff < -2) { | ||
// firt beat is off grid. Skip it. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ping |
||
firstBeat = 1; | ||
} | ||
} | ||
} | ||
|
||
m_resultBeats.reserve(beats.size()); | ||
for (size_t i = firstBeat; i < beats.size(); ++i) { | ||
double result = beats[i] * kStepSize; | ||
m_resultBeats.push_back(result); | ||
} | ||
|
||
m_pDetectionFunction.reset(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,63 @@ | ||
#include "analyzer/plugins/buffering_utils.h" | ||
|
||
#include "util/math.h" | ||
#include "util/sample.h" | ||
|
||
#include <string.h> | ||
|
||
namespace mixxx { | ||
|
||
bool DownmixAndOverlapHelper::initialize(size_t windowSize, size_t stepSize, WindowReadyCallback callback) { | ||
m_buffer.resize(windowSize); | ||
m_buffer.assign(windowSize, 0.0); | ||
m_callback = callback; | ||
m_windowSize = windowSize; | ||
m_stepSize = stepSize; | ||
m_bufferWritePosition = 0; | ||
// make sure the first frame is centered into the fft window. This makes sure | ||
// that the result is significant starting fom the first step. | ||
m_bufferWritePosition = windowSize / 2; | ||
uklotzde marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return m_windowSize > 0 && m_stepSize > 0 && | ||
m_stepSize <= m_windowSize && callback; | ||
} | ||
|
||
bool DownmixAndOverlapHelper::processStereoSamples(const CSAMPLE* pInput, size_t inputStereoSamples) { | ||
const size_t numInputFrames = inputStereoSamples / 2; | ||
return processInner(pInput, numInputFrames); | ||
} | ||
|
||
bool DownmixAndOverlapHelper::finalize() { | ||
// We need to append at least m_windowSize / 2 - m_stepSize silence | ||
// to have a valid analysis results for the last track samples. | ||
// Since we go in fixed steps up to "m_stepSize - 1" samples remains | ||
// unrocessed. That th reason we use "m_windowSize / 2 - 1" below | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some minor typos in comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ping |
||
// instead of "m_windowSize / 2 - m_stepSize" | ||
size_t framesToFillWindow = m_windowSize - m_bufferWritePosition; | ||
size_t numInputFrames = math_max(framesToFillWindow, m_windowSize / 2 - 1); | ||
return processInner(nullptr, numInputFrames); | ||
} | ||
|
||
bool DownmixAndOverlapHelper::processInner( | ||
const CSAMPLE* pInput, size_t numInputFrames) { | ||
size_t inRead = 0; | ||
double* pDownmix = m_buffer.data(); | ||
|
||
while (inRead < numInputFrames) { | ||
size_t writeAvailable = math_min(numInputFrames, | ||
m_windowSize - m_bufferWritePosition); | ||
|
||
for (size_t i = 0; i < writeAvailable; ++i) { | ||
// We analyze a mono downmix of the signal since we don't think | ||
// stereo does us any good. | ||
pDownmix[m_bufferWritePosition + i] = (pInput[(inRead + i) * 2] + | ||
pInput[(inRead + i) * 2 + 1]) * | ||
0.5; | ||
if (pInput) { | ||
for (size_t i = 0; i < writeAvailable; ++i) { | ||
// We analyze a mono downmix of the signal since we don't think | ||
// stereo does us any good. | ||
pDownmix[m_bufferWritePosition + i] = (pInput[(inRead + i) * 2] + | ||
pInput[(inRead + i) * 2 + 1]) * | ||
0.5; | ||
} | ||
} else { | ||
// we are in the finalize call. Add silence to | ||
// complete samples left in th buffer. | ||
for (size_t i = 0; i < writeAvailable; ++i) { | ||
pDownmix[m_bufferWritePosition + i] = 0; | ||
} | ||
} | ||
m_bufferWritePosition += writeAvailable; | ||
inRead += writeAvailable; | ||
|
@@ -52,9 +81,4 @@ bool DownmixAndOverlapHelper::processStereoSamples(const CSAMPLE* pInput, size_t | |
return true; | ||
} | ||
|
||
bool DownmixAndOverlapHelper::finalize() { | ||
// TODO(rryan) flush support? | ||
return true; | ||
} | ||
|
||
} | ||
} // namespace mixxx |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3 times beats.at(0)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping