Merge fast detector with analyzer and use it in CBuffer

This commit is contained in:
Flamefire committed 2014-03-28 17:28:24 +01:00
1 parent b1bca37bf8
commit c781f5427f
11 files changed
+141 -314

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@ float* short2FloatArray(short* in, size_t len){
float* result = static_cast<float*>(malloc(len * sizeof(float)));
float* curOut = result;
for(int i=0; i<len; i++){
for(size_t i=0; i<len; i++){
*curOut = *in / maxShort;
curOut++;
in++;
@@ -22,7 +22,7 @@ double* short2DoubleArray(short* in, size_t len, bool scale = true){
double* result = static_cast<double*>(malloc(len * sizeof(double)));
double* curOut = result;
for(int i=0; i<len; i++){
for(size_t i=0; i<len; i++){
*curOut = *in;
if(scale)
*curOut /= maxShort;
+1 -1
View File
@@ -131,8 +131,8 @@
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<OmitFramePointers>true</OmitFramePointers>
<FloatingPointModel>Fast</FloatingPointModel>
<OmitFramePointers>false</OmitFramePointers>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
+32 -10
View File
@@ -2,6 +2,22 @@
#include "pitchTracker.h"
#include "Helper.h"
AnalyzerExt::AnalyzerExt(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step): Analyzer(44100., "", step){
PitchTracker::Init(baseToneFrequency, minHalfTone, maxHalfTone);
}
AnalyzerExt::~AnalyzerExt(){
PitchTracker::DeInit();
}
int AnalyzerExt::GetNoteFast(float* weights){
size_t size = m_fastAnalysisBuf.size();
if(size > m_SampleCt)
m_fastAnalysisBuf.pop(size - m_SampleCt);
m_fastAnalysisBuf.read(m_AnalysisBuf, m_AnalysisBuf + m_SampleCt);
return PitchTracker::GetTone(m_AnalysisBuf, m_SampleCt, weights);
}
void PtFast_Init(double baseToneFrequency, int minHalfTone, int maxHalfTone){
PitchTracker::Init(baseToneFrequency, minHalfTone, maxHalfTone);
}
@@ -19,22 +35,22 @@ int PtFast_GetTone(short* samples, int sampleCt, float* weights){
return result;
}
Analyzer* Analyzer_Create(double rate, unsigned step){
return new Analyzer(rate, "", step);
AnalyzerExt* Analyzer_Create(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step){
return new AnalyzerExt(baseToneFrequency, minHalfTone, maxHalfTone, step);
}
void Analyzer_Free(Analyzer* analyzer){
void Analyzer_Free(AnalyzerExt* analyzer){
if(analyzer)
delete analyzer;
}
void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt){
void Analyzer_InputFloat(AnalyzerExt* analyzer, float* data, int sampleCt){
if(sampleCt == 0 || !analyzer)
return;
analyzer->input(data, data + sampleCt);
}
void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt){
void Analyzer_InputShort(AnalyzerExt* analyzer, short* data, int sampleCt){
if(sampleCt == 0 || !analyzer)
return;
float* dataFloat = short2FloatArray(data, sampleCt);
@@ -42,7 +58,7 @@ void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt){
freeFloatArray(dataFloat);
}
void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt){
void Analyzer_InputByte(AnalyzerExt* analyzer, char* data, int sampleCt){
if(sampleCt == 0 || !analyzer)
return;
float* dataFloat = short2FloatArray(static_cast<short*>(static_cast<void*>(data)), sampleCt);
@@ -50,26 +66,32 @@ void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt){
freeFloatArray(dataFloat);
}
void Analyzer_Process(Analyzer* analyzer){
void Analyzer_Process(AnalyzerExt* analyzer){
if(!analyzer)
return;
analyzer->process();
}
double Analyzer_GetPeak(Analyzer* analyzer){
double Analyzer_GetPeak(AnalyzerExt* analyzer){
if(!analyzer)
return -999;
return analyzer->getPeak();
}
double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq){
double Analyzer_FindNote(AnalyzerExt* analyzer, double minFreq, double maxFreq){
if(!analyzer)
return -1;
const Tone* tone = analyzer->findTone(minFreq, maxFreq);
return (tone == NULL) ? -1 : ToneToNote(tone);
}
bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate){
int Analyzer_GetNoteFast(AnalyzerExt* analyzer, float* weights){
if(!analyzer)
return -1;
return analyzer->GetNoteFast(weights);
}
bool Analyzer_OutputFloat(AnalyzerExt* analyzer, float* data, int sampleCt, float rate){
if(!analyzer)
return false;
return analyzer->output(data, data + sampleCt, rate);
+26 -10
View File
@@ -4,19 +4,35 @@
#define DllExport extern "C" __declspec(dllexport)
class AnalyzerExt: public Analyzer{
public:
AnalyzerExt(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step = 200);
~AnalyzerExt();
template <typename InIt> void input(InIt begin, InIt end){
m_fastAnalysisBuf.insert(begin, end);
Analyzer::input(begin, end);
}
int GetNoteFast(float* weights);
private:
constexpr static size_t m_SampleCt = 4096;
RingBuffer<m_SampleCt * 2> m_fastAnalysisBuf; // Buffer used for fast analysis instead of FFT
float m_AnalysisBuf[4096];
};
DllExport void PtFast_Init(double baseToneFrequency, int minHalfTone, int maxHalfTone);
DllExport void PtFast_DeInit();
DllExport int PtFast_GetTone(short* samples, int sampleCt, float* weights);
DllExport Analyzer* Analyzer_Create(double rate, unsigned step);
DllExport void Analyzer_Free(Analyzer* analyzer);
DllExport void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt);
DllExport void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt);
DllExport void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt);
DllExport void Analyzer_Process(Analyzer* analyzer);
DllExport double Analyzer_GetPeak(Analyzer* analyzer);
DllExport double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq);
DllExport bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate);
DllExport AnalyzerExt* Analyzer_Create(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step);
DllExport void Analyzer_Free(AnalyzerExt* analyzer);
DllExport void Analyzer_InputFloat(AnalyzerExt* analyzer, float* data, int sampleCt);
DllExport void Analyzer_InputShort(AnalyzerExt* analyzer, short* data, int sampleCt);
DllExport void Analyzer_InputByte(AnalyzerExt* analyzer, char* data, int sampleCt);
DllExport void Analyzer_Process(AnalyzerExt* analyzer);
DllExport double Analyzer_GetPeak(AnalyzerExt* analyzer);
DllExport int Analyzer_GetNoteFast(AnalyzerExt* analyzer, float* weights);
DllExport double Analyzer_FindNote(AnalyzerExt* analyzer, double minFreq, double maxFreq);
DllExport bool Analyzer_OutputFloat(AnalyzerExt* analyzer, float* data, int sampleCt, float rate);
/*
public ref class CTone {
@@ -59,7 +75,7 @@ public:
String^ GetId();
private:
Analyzer* m_analyzer;
AnalyzerExt* m_analyzer;
String^ m_id;
};
+4 -4
View File
@@ -50,7 +50,7 @@ Analyzer::Analyzer(double rate, std::string id, unsigned step):
bool Analyzer::output(float* begin, float* end, double rate) {
constexpr unsigned a = 2;
const unsigned size = m_passthrough.size();
const size_t size = m_passthrough.size();
const unsigned out = static_cast<unsigned>((end - begin) / 2 /* stereo */);
if (out == 0) return false;
const unsigned in = static_cast<unsigned>(m_resampleFactor * (m_rate / rate) * out + 2 * a /* lanczos kernel */ + 5 /* safety margin for rounding errors */);
@@ -72,7 +72,7 @@ bool Analyzer::output(float* begin, float* end, double rate) {
m_resamplePos -= num;
if (size > 3000) {
// Reset
m_passthrough.pop(m_passthrough.size() - 700);
m_passthrough.pop(size - 700);
m_resampleFactor = 1.0;
} else {
m_passthrough.pop(num);
@@ -133,8 +133,8 @@ void Analyzer::calcTones() {
const size_t kMax = std::min(FFT_N / 2, size_t(FFT_MAXFREQ / freqPerBin));
std::vector<Peak> peaks(kMax + 1); // One extra to simplify loops
for (size_t k = 1; k <= kMax; ++k) {
float magnitude = std::abs(m_fft[k]);
float phase = std::arg(m_fft[k]);
double magnitude = std::abs(m_fft[k]);
double phase = std::arg(m_fft[k]);
// process phase difference
double delta = phase - m_fftLastPhase[k];
m_fftLastPhase[k] = phase;
+11 -11
View File
@@ -34,14 +34,14 @@ static const unsigned FFT_P = 10;
static const std::size_t FFT_N = 1 << FFT_P;
/// Lock-free ring buffer. Discards oldest data on overflow (not strictly thread-safe).
template <unsigned SIZE> class RingBuffer {
template <size_t SIZE> class RingBuffer {
public:
constexpr static unsigned capacity = SIZE;
constexpr static size_t capacity = SIZE;
RingBuffer(): m_read(), m_write() {} ///< Initialize empty buffer
template <typename InIt> void insert(InIt begin, InIt end) {
unsigned r = m_read; // The read position
unsigned w = m_write; // The write position
bool overflow = (end - begin >= SIZE - size());
size_t r = m_read; // The read position
size_t w = m_write; // The write position
bool overflow = (static_cast<size_t>(end - begin) >= SIZE - size());
while (begin != end) {
m_buf[w] = *begin++; // Copy sample
w = modulo(w + 1); // Update cursor
@@ -51,17 +51,17 @@ public:
}
/// Read data from current position if there is enough data to fill the range (otherwise return false). Does not move read pointer.
template <typename OutIt> bool read(OutIt begin, OutIt end) {
unsigned r = m_read;
if (size() <= end - begin) return false; // Not enough audio available
size_t r = m_read;
if (size() <= static_cast<size_t>(end - begin)) return false; // Not enough audio available
while (begin != end) *begin++ = m_buf[r++ % SIZE]; // Copy audio to output iterator
return true;
}
void pop(unsigned n) { m_read = modulo(m_read + n); } ///< Move reading pointer forward.
unsigned size() const { return modulo(m_write - m_read); }
void pop(size_t n) { m_read = modulo(m_read + n); } ///< Move reading pointer forward.
size_t size() const { return modulo(m_write - m_read); }
private:
static unsigned modulo(int idx) { return (SIZE + idx) % SIZE; } ///< Modulo operation with proper rounding (handles slightly "negative" idx as well)
static size_t modulo(size_t idx) { return idx % SIZE; } ///< Modulo operation with proper rounding (handles slightly "negative" idx as well)
float m_buf[SIZE];
volatile unsigned m_read, m_write; ///< The indices of the next read/write operations. read == write implies that buffer is empty.
volatile size_t m_read, m_write; ///< The indices of the next read/write operations. read == write implies that buffer is empty.
};
/// analyzer class
+30 -20
View File
@@ -35,15 +35,19 @@ namespace PitchTracker{
Log=Callback;
}
double *SamplesPerPeriodPerTone = NULL;
int MinHalfTone = 0, MaxHalfTone = 38;
const double HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
static double *SamplesPerPeriodPerTone = NULL;
static double BaseToneFrequency = -1;
static int MinHalfTone = -1, MaxHalfTone = -1;
static const double HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
void Init(double baseToneFrequency, int minHalfTone, int maxHalfTone){
if(SamplesPerPeriodPerTone && abs(baseToneFrequency - BaseToneFrequency) < 1. && minHalfTone == MinHalfTone && maxHalfTone == MaxHalfTone)
return;
BaseToneFrequency = baseToneFrequency;
MinHalfTone = minHalfTone;
MaxHalfTone = maxHalfTone;
DeInit();
if(maxHalfTone <= 0)
if(minHalfTone < 0 || maxHalfTone < minHalfTone)
return;
SamplesPerPeriodPerTone = (double*) malloc((MaxHalfTone + 1) * sizeof(double));
//Init Array to avoid costly calculations
@@ -61,13 +65,14 @@ namespace PitchTracker{
}
}
static double _AnalyzeToneFunc(double *samples, int sampleCt, int toneIndex){
template<typename T>
static T _AnalyzeToneFunc(T *samples, int sampleCt, int toneIndex){
double samplesPerPeriodD = SamplesPerPeriodPerTone[toneIndex]; // samples in one period
int samplesPerPeriod = (int)samplesPerPeriodD;
double fHigh = samplesPerPeriodD - samplesPerPeriod;
double fLow = 1.0 - fHigh;
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
T fHigh = static_cast<T>(samplesPerPeriodD - samplesPerPeriod);
T fLow = static_cast<T>(1.0 - fHigh);
double accumDist = 0; // accumulated distances
T accumDist = 0; // accumulated distances
// compare correlating samples
int sampleIndex = 0; // index of sample to analyze
@@ -75,28 +80,30 @@ namespace PitchTracker{
for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex + 1 < sampleCt; correlatingSampleIndex++, sampleIndex++)
{
// calc distance to corresponding sample in next period (lower means more distant)
double dist = samples[sampleIndex] * (samples[correlatingSampleIndex] * fLow + samples[correlatingSampleIndex + 1] * fHigh);
T dist = samples[sampleIndex] * (samples[correlatingSampleIndex] * fLow + samples[correlatingSampleIndex + 1] * fHigh);
accumDist += dist;
}
//Using _AnalysisBufLen here makes it return correct values among all analyzed frequencies
double scaleValue = sampleCt; // Divide also by Int16.MaxValue^2
return accumDist / scaleValue;
//Using sampleCt here makes it return correct values among all analyzed frequencies
return accumDist / sampleCt;
}
int GetTone(double *samples, int sampleCt, float *weights, bool scale){
double maxVolume = 0;
template<typename T>
int GetTone(T *samples, int sampleCt, float *weights, bool scale){
if(!SamplesPerPeriodPerTone)
return -1;
T maxVolume = 0;
for(int i = 0; i < sampleCt; i++){
double vol = abs(samples[i]);
T vol = abs(samples[i]);
if(vol > maxVolume)
maxVolume = vol;
}
double maxWeight = -1;
double minWeight = 1;
T maxWeight = -1;
T minWeight = 1;
int maxTone = -1;
for (int toneIndex = MinHalfTone; toneIndex <= MaxHalfTone; toneIndex++)
{
double curWeight = _AnalyzeToneFunc(samples, sampleCt, toneIndex) / maxVolume;
T curWeight = _AnalyzeToneFunc<T>(samples, sampleCt, toneIndex) / maxVolume;
if(scale)
curWeight /= 32768.0; //maximum abs value of a short, would actually need to scale by MaxValue^2 but we already scaled by maxVolume
@@ -109,7 +116,7 @@ namespace PitchTracker{
if (curWeight < minWeight)
minWeight = curWeight;
weights[toneIndex - MinHalfTone] = (float) curWeight;
weights[toneIndex - MinHalfTone] = static_cast<float>(curWeight);
}
if(maxWeight - minWeight > 0.01){
@@ -117,4 +124,7 @@ namespace PitchTracker{
}else return -1;
}
template int GetTone(float *samples, int sampleCt, float *weights, bool scale);
template int GetTone(double *samples, int sampleCt, float *weights, bool scale);
}
+1 -1
View File
@@ -3,7 +3,7 @@
namespace PitchTracker{
void Init(double baseToneFrequency, int minHalfTone, int maxHalfTone);
void DeInit();
int GetTone(double *samples, int sampleCt, float *weights, bool scale = false);
template<typename T> int GetTone(T *samples, int sampleCt, float *weights, bool scale = false);
typedef void (__stdcall * LogCallback)(const char* text);
static LogCallback Log;