Make an own class for AKF detector (tested for C2-B5)
This commit is contained in:
1 parent
4c597cb4f8
commit
f1a885b95e
10 files changed
+259
-146
No files matched your search
@@ -95,6 +95,7 @@
|
|||||||
<PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<FloatingPointModel>Fast</FloatingPointModel>
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@@ -112,6 +113,7 @@
|
|||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<FloatingPointModel>Fast</FloatingPointModel>
|
<FloatingPointModel>Fast</FloatingPointModel>
|
||||||
|
<MinimalRebuild>false</MinimalRebuild>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@@ -169,7 +171,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="compatibility.cpp" />
|
<ClCompile Include="compatibility.cpp" />
|
||||||
<ClCompile Include="performous\pitch.cc" />
|
<ClCompile Include="performous\pitch.cc" />
|
||||||
<ClCompile Include="pdaAKF.cpp" />
|
<ClCompile Include="ptAKF.cpp" />
|
||||||
<ClCompile Include="PitchWrapper.cpp" />
|
<ClCompile Include="PitchWrapper.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -177,7 +179,7 @@
|
|||||||
<ClInclude Include="Helper.h" />
|
<ClInclude Include="Helper.h" />
|
||||||
<ClInclude Include="performous\libda\fft.hpp" />
|
<ClInclude Include="performous\libda\fft.hpp" />
|
||||||
<ClInclude Include="performous\libda\sample.hpp" />
|
<ClInclude Include="performous\libda\sample.hpp" />
|
||||||
<ClInclude Include="pdaAKF.h" />
|
<ClInclude Include="ptAKF.h" />
|
||||||
<ClInclude Include="PitchWrapper.h" />
|
<ClInclude Include="PitchWrapper.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<ClCompile Include="performous\pitch.cc">
|
<ClCompile Include="performous\pitch.cc">
|
||||||
<Filter>Quelldateien\performous</Filter>
|
<Filter>Quelldateien\performous</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="pdaAKF.cpp">
|
<ClCompile Include="ptAKF.cpp">
|
||||||
<Filter>Quelldateien</Filter>
|
<Filter>Quelldateien</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<ClInclude Include="performous\libda\sample.hpp">
|
<ClInclude Include="performous\libda\sample.hpp">
|
||||||
<Filter>Headerdateien\performous\libda</Filter>
|
<Filter>Headerdateien\performous\libda</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="pdaAKF.h">
|
<ClInclude Include="ptAKF.h">
|
||||||
<Filter>Headerdateien</Filter>
|
<Filter>Headerdateien</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,58 +1,23 @@
|
|||||||
#include "PitchWrapper.h"
|
#include "PitchWrapper.h"
|
||||||
#include "pdaAKF.h"
|
|
||||||
#include "Helper.h"
|
#include "Helper.h"
|
||||||
|
|
||||||
AnalyzerExt::AnalyzerExt(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step): Analyzer(44100., "", step){
|
|
||||||
PitchTrackerAKF::Init(baseToneFrequency, minHalfTone, maxHalfTone);
|
Analyzer* Analyzer_Create(unsigned step){
|
||||||
|
return new Analyzer(44100, "", step);
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalyzerExt::~AnalyzerExt(){
|
void Analyzer_Free(Analyzer* analyzer){
|
||||||
PitchTrackerAKF::DeInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
int AnalyzerExt::GetNoteFast(double* maxVolume, float* weights){
|
|
||||||
float AnaylsisBuf[m_SampleCt];
|
|
||||||
size_t size = m_fastAnalysisBuf.size();
|
|
||||||
if(size > m_SampleCt)
|
|
||||||
m_fastAnalysisBuf.pop(size - m_SampleCt);
|
|
||||||
if(!m_fastAnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + m_SampleCt))
|
|
||||||
return -1;
|
|
||||||
return PitchTrackerAKF::GetTone(AnaylsisBuf, m_SampleCt, maxVolume, weights);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PtFast_Init(double baseToneFrequency, int minHalfTone, int maxHalfTone){
|
|
||||||
PitchTrackerAKF::Init(baseToneFrequency, minHalfTone, maxHalfTone);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PtFast_DeInit(){
|
|
||||||
PitchTrackerAKF::DeInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
int PtFast_GetTone(short* samples, int sampleCt, double* maxVolume, float* weights){
|
|
||||||
if(sampleCt <= 0)
|
|
||||||
return -1;
|
|
||||||
double* dataDouble = short2DoubleArray(samples, sampleCt, false);
|
|
||||||
int result = PitchTrackerAKF::GetTone(dataDouble, sampleCt, maxVolume, weights, true);
|
|
||||||
freeDoubleArray(dataDouble);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
AnalyzerExt* Analyzer_Create(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step){
|
|
||||||
return new AnalyzerExt(baseToneFrequency, minHalfTone, maxHalfTone, step);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Analyzer_Free(AnalyzerExt* analyzer){
|
|
||||||
if(analyzer)
|
if(analyzer)
|
||||||
delete analyzer;
|
delete analyzer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Analyzer_InputFloat(AnalyzerExt* analyzer, float* data, int sampleCt){
|
void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt){
|
||||||
if(sampleCt == 0 || !analyzer)
|
if(sampleCt == 0 || !analyzer)
|
||||||
return;
|
return;
|
||||||
analyzer->input(data, data + sampleCt);
|
analyzer->input(data, data + sampleCt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Analyzer_InputShort(AnalyzerExt* analyzer, short* data, int sampleCt){
|
void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt){
|
||||||
if(sampleCt == 0 || !analyzer)
|
if(sampleCt == 0 || !analyzer)
|
||||||
return;
|
return;
|
||||||
float* dataFloat = short2FloatArray(data, sampleCt);
|
float* dataFloat = short2FloatArray(data, sampleCt);
|
||||||
@@ -60,45 +25,64 @@ void Analyzer_InputShort(AnalyzerExt* analyzer, short* data, int sampleCt){
|
|||||||
freeFloatArray(dataFloat);
|
freeFloatArray(dataFloat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Analyzer_InputByte(AnalyzerExt* analyzer, char* data, int sampleCt){
|
void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt){
|
||||||
if(sampleCt == 0 || !analyzer)
|
if(sampleCt <= 0 || !analyzer)
|
||||||
return;
|
return;
|
||||||
float* dataFloat = short2FloatArray(static_cast<short*>(static_cast<void*>(data)), sampleCt);
|
float* dataFloat = short2FloatArray(static_cast<short*>(static_cast<void*>(data)), sampleCt);
|
||||||
analyzer->input(dataFloat, dataFloat + sampleCt);
|
analyzer->input(dataFloat, dataFloat + sampleCt);
|
||||||
freeFloatArray(dataFloat);
|
freeFloatArray(dataFloat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Analyzer_Process(AnalyzerExt* analyzer){
|
void Analyzer_Process(Analyzer* analyzer){
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return;
|
return;
|
||||||
analyzer->process();
|
analyzer->process();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Analyzer_GetPeak(AnalyzerExt* analyzer){
|
double Analyzer_GetPeak(Analyzer* analyzer){
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return -999;
|
return -999;
|
||||||
return analyzer->getPeak();
|
return analyzer->getPeak();
|
||||||
}
|
}
|
||||||
|
|
||||||
double Analyzer_FindNote(AnalyzerExt* analyzer, double minFreq, double maxFreq){
|
double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq){
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return -1;
|
return -1;
|
||||||
const Tone* tone = analyzer->findTone(minFreq, maxFreq);
|
const Tone* tone = analyzer->findTone(minFreq, maxFreq);
|
||||||
return (tone == NULL) ? -1 : FreqToNote(tone->freq);
|
return (tone == NULL) ? -1 : FreqToNote(tone->freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Analyzer_GetNoteFast(AnalyzerExt* analyzer, double* maxVolume, float* weights){
|
bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate){
|
||||||
if(!analyzer)
|
|
||||||
return -1;
|
|
||||||
return analyzer->GetNoteFast(maxVolume, weights);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Analyzer_OutputFloat(AnalyzerExt* analyzer, float* data, int sampleCt, float rate){
|
|
||||||
if(!analyzer)
|
if(!analyzer)
|
||||||
return false;
|
return false;
|
||||||
return analyzer->output(data, data + sampleCt, rate);
|
return analyzer->output(data, data + sampleCt, rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DllExport PtAKF* PtAKF_Create(){
|
||||||
|
return new PtAKF();
|
||||||
|
}
|
||||||
|
|
||||||
|
DllExport void PtAKF_Free(PtAKF* analyzer){
|
||||||
|
if(analyzer)
|
||||||
|
delete analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
DllExport int PtAKF_GetNumHalfTones(){
|
||||||
|
return PtAKF::GetNumHalfTones();
|
||||||
|
}
|
||||||
|
|
||||||
|
DllExport void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt){
|
||||||
|
if(sampleCt <= 0 || !analyzer)
|
||||||
|
return;
|
||||||
|
short* dataShort = static_cast<short*>(static_cast<void*>(data));
|
||||||
|
analyzer->input(dataShort, dataShort + sampleCt);
|
||||||
|
}
|
||||||
|
|
||||||
|
DllExport int PtAKF_GetNote(PtAKF* analyzer, double* maxVolume, float* weights){
|
||||||
|
if(!analyzer)
|
||||||
|
return -1;
|
||||||
|
return analyzer->GetNote(maxVolume, weights);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
namespace Native{
|
namespace Native{
|
||||||
|
|||||||
+16
-28
@@ -1,37 +1,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "performous/pitch.hh"
|
#include "performous/pitch.hh"
|
||||||
|
#include "ptAKF.h"
|
||||||
|
|
||||||
#define DllExport extern "C" __declspec(dllexport)
|
#define DllExport extern "C" __declspec(dllexport)
|
||||||
|
|
||||||
class AnalyzerExt: public Analyzer{
|
DllExport Analyzer* Analyzer_Create(unsigned step);
|
||||||
public:
|
DllExport void Analyzer_Free(Analyzer* analyzer);
|
||||||
AnalyzerExt(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step = 200);
|
DllExport void Analyzer_InputFloat(Analyzer* analyzer, float* data, int sampleCt);
|
||||||
~AnalyzerExt();
|
DllExport void Analyzer_InputShort(Analyzer* analyzer, short* data, int sampleCt);
|
||||||
template <typename InIt> void input(InIt begin, InIt end){
|
DllExport void Analyzer_InputByte(Analyzer* analyzer, char* data, int sampleCt);
|
||||||
m_fastAnalysisBuf.insert(begin, end);
|
DllExport void Analyzer_Process(Analyzer* analyzer);
|
||||||
Analyzer::input(begin, end);
|
DllExport double Analyzer_GetPeak(Analyzer* analyzer);
|
||||||
}
|
DllExport double Analyzer_FindNote(Analyzer* analyzer, double minFreq, double maxFreq);
|
||||||
int GetNoteFast(double* maxVolume, float* weights);
|
DllExport bool Analyzer_OutputFloat(Analyzer* analyzer, float* data, int sampleCt, float rate);
|
||||||
private:
|
|
||||||
constexpr static size_t m_SampleCt = 4096;
|
|
||||||
RingBuffer<m_SampleCt * 2> m_fastAnalysisBuf; // Buffer used for fast analysis instead of FFT
|
|
||||||
};
|
|
||||||
|
|
||||||
DllExport void PtFast_Init(double baseToneFrequency, int minHalfTone, int maxHalfTone);
|
DllExport PtAKF* PtAKF_Create();
|
||||||
DllExport void PtFast_DeInit();
|
DllExport void PtAKF_Free(PtAKF* analyzer);
|
||||||
DllExport int PtFast_GetTone(short* samples, int sampleCt, float* weights);
|
DllExport int PtAKF_GetNumHalfTones();
|
||||||
|
DllExport void PtAKF_InputByte(PtAKF* analyzer, char* data, int sampleCt);
|
||||||
DllExport AnalyzerExt* Analyzer_Create(double baseToneFrequency, int minHalfTone, int maxHalfTone, unsigned step);
|
DllExport int PtAKF_GetNote(PtAKF* analyzer, double* maxVolume, float* weights);
|
||||||
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, double* maxVolume, 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 {
|
public ref class CTone {
|
||||||
@@ -74,7 +62,7 @@ public:
|
|||||||
String^ GetId();
|
String^ GetId();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AnalyzerExt* m_analyzer;
|
Analyzer* m_analyzer;
|
||||||
String^ m_id;
|
String^ m_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// A pitch detection that is based on the AKF and AMDF (initially from original Vocaluxe/USDx)
|
|
||||||
namespace PitchTrackerAKF{
|
|
||||||
void Init(double baseToneFrequency, int minHalfTone, int maxHalfTone);
|
|
||||||
void DeInit();
|
|
||||||
template<typename T> int GetTone(T *samples, int sampleCt, double* maxVolume, float *weights, bool scale = false);
|
|
||||||
|
|
||||||
typedef void (__stdcall * LogCallback)(const char* text);
|
|
||||||
static LogCallback Log;
|
|
||||||
void SetLogCallback(LogCallback Callback);
|
|
||||||
}
|
|
||||||
@@ -13,28 +13,112 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#include "pdaAKF.h"
|
#include "ptAKF.h"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
static constexpr double BaseToneFrequency = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
|
||||||
#include <stdio.h>
|
static constexpr double HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||||
|
int PtAKF::_InitCount = 0;
|
||||||
|
float* PtAKF::_SamplesPerPeriodPerTone = NULL;
|
||||||
|
|
||||||
|
PtAKF::PtAKF(){
|
||||||
|
_InitCount++;
|
||||||
|
if(_InitCount == 1){
|
||||||
|
_SamplesPerPeriodPerTone = new float[_MaxHalfTone + 1];
|
||||||
|
//Init Array to avoid costly calculations
|
||||||
|
for (int toneIndex = 0; toneIndex <= _MaxHalfTone; toneIndex++)
|
||||||
|
{
|
||||||
|
double freq = BaseToneFrequency * pow(HalftoneBase, toneIndex);
|
||||||
|
_SamplesPerPeriodPerTone[toneIndex] = static_cast<float>(44100.0 / freq); // samples in one period
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PtAKF::~PtAKF(){
|
||||||
|
_InitCount--;
|
||||||
|
if(!_InitCount){
|
||||||
|
delete[] _SamplesPerPeriodPerTone;
|
||||||
|
_SamplesPerPeriodPerTone = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int PtAKF::GetNote(double* maxVolume, float* weights){
|
||||||
|
float AnaylsisBuf[_SampleCt];
|
||||||
|
size_t size = _AnalysisBuf.size();
|
||||||
|
if(size > _SampleCt)
|
||||||
|
_AnalysisBuf.pop(size - _SampleCt);
|
||||||
|
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt))
|
||||||
|
return -1;
|
||||||
|
return _GetNote(AnaylsisBuf, maxVolume, weights);
|
||||||
|
}
|
||||||
|
|
||||||
|
int PtAKF::_GetNote(float* samples, double* maxVolume, float* weights){
|
||||||
|
float maxVolumeL = 0;
|
||||||
|
for(int i = 0; i < _SampleCt; i++){
|
||||||
|
float vol = abs(samples[i]);
|
||||||
|
if(vol > maxVolumeL)
|
||||||
|
maxVolumeL = vol;
|
||||||
|
}
|
||||||
|
maxVolumeL /= 32767.;
|
||||||
|
*maxVolume = maxVolumeL;
|
||||||
|
|
||||||
|
float maxWeight = -1;
|
||||||
|
float minWeight = 1;
|
||||||
|
int maxTone = -1;
|
||||||
|
for (int toneIndex = _MinHalfTone; toneIndex <= _MaxHalfTone; toneIndex++)
|
||||||
|
{
|
||||||
|
float curWeight = _AnalyzeToneFunc(samples, toneIndex);
|
||||||
|
|
||||||
|
if (curWeight > maxWeight)
|
||||||
|
{
|
||||||
|
maxWeight = curWeight;
|
||||||
|
maxTone = toneIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curWeight < minWeight)
|
||||||
|
minWeight = curWeight;
|
||||||
|
|
||||||
|
weights[toneIndex - _MinHalfTone] = curWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(maxWeight - minWeight > 0.01){
|
||||||
|
return maxTone;
|
||||||
|
}else return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
float PtAKF::_AnalyzeToneFunc(float* samples, int toneIndex){
|
||||||
|
// Use method by Kobayashi and Shimamura (1995): Combine AKF and AMDF to a new f(z)=AKF(z)/(AMDF(z)+k) with k=1
|
||||||
|
|
||||||
|
float samplesPerPeriodD = _SamplesPerPeriodPerTone[toneIndex]; // samples in one period
|
||||||
|
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
|
||||||
|
float fHigh = samplesPerPeriodD - samplesPerPeriod;
|
||||||
|
float fLow = 1.0f - fHigh;
|
||||||
|
|
||||||
|
float accumDistAKF = 0; // accumulated distances
|
||||||
|
float accumDistAMDF = 0; // accumulated distances
|
||||||
|
|
||||||
|
// compare correlating samples
|
||||||
|
int sampleIndex = 0; // index of sample to analyze
|
||||||
|
// Start value= index of sample one period ahead
|
||||||
|
for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex + 1 < _SampleCt; correlatingSampleIndex++, sampleIndex++)
|
||||||
|
{
|
||||||
|
// calc distance to corresponding sample in next period
|
||||||
|
float xn = samples[sampleIndex];
|
||||||
|
float xnt = samples[correlatingSampleIndex] * fLow + samples[correlatingSampleIndex + 1] * fHigh;
|
||||||
|
accumDistAKF += xn * xnt;
|
||||||
|
accumDistAMDF += abs(xn - xnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
accumDistAKF /= _SampleCt;
|
||||||
|
accumDistAMDF /= sampleIndex;
|
||||||
|
|
||||||
|
return accumDistAKF / (accumDistAMDF * 32767.f + 32767.f); // Need to scale AKF by MAX^2 and AMDF by MAX, so do some maths to divide only once
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace PitchTrackerAKF{
|
namespace PitchTrackerAKF{
|
||||||
|
|
||||||
static void doLog(const char* msg,...){
|
|
||||||
va_list argptr = NULL;
|
|
||||||
va_start(argptr, msg);
|
|
||||||
|
|
||||||
char buffer[200];
|
|
||||||
vsprintf_s(buffer,msg, argptr);
|
|
||||||
if(Log) Log(buffer);
|
|
||||||
va_end(argptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetLogCallback(LogCallback Callback){
|
|
||||||
Log=Callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
static double *SamplesPerPeriodPerTone = NULL;
|
static double *SamplesPerPeriodPerTone = NULL;
|
||||||
static double BaseToneFrequency = -1;
|
static double BaseToneFrequency = -1;
|
||||||
static int MinHalfTone = -1, MaxHalfTone = -1;
|
static int MinHalfTone = -1, MaxHalfTone = -1;
|
||||||
@@ -138,4 +222,5 @@ namespace PitchTrackerAKF{
|
|||||||
template int GetTone(float *samples, int sampleCt, double* maxVolume, float *weights, bool scale);
|
template int GetTone(float *samples, int sampleCt, double* maxVolume, float *weights, bool scale);
|
||||||
template int GetTone(double *samples, int sampleCt, double* maxVolume, float *weights, bool scale);
|
template int GetTone(double *samples, int sampleCt, double* maxVolume, float *weights, bool scale);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "performous/pitch.hh"
|
||||||
|
|
||||||
|
// A pitch detection that is based on the AKF and AMDF (initially from original Vocaluxe/USDx)
|
||||||
|
class PtAKF{
|
||||||
|
public:
|
||||||
|
PtAKF();
|
||||||
|
~PtAKF();
|
||||||
|
|
||||||
|
void input(short* begin, short* end){
|
||||||
|
_AnalysisBuf.insert(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetNote(double* maxVolume, float* weights);
|
||||||
|
static int GetNumHalfTones(){ return _MaxHalfTone - _MinHalfTone + 1;}
|
||||||
|
private:
|
||||||
|
static int _InitCount;
|
||||||
|
static float* _SamplesPerPeriodPerTone;
|
||||||
|
static constexpr int _MinHalfTone = 0; //C2
|
||||||
|
static constexpr int _MaxHalfTone = 47; //B5
|
||||||
|
|
||||||
|
constexpr static size_t _SampleCt = 4096;
|
||||||
|
RingBuffer<_SampleCt * 2> _AnalysisBuf;
|
||||||
|
|
||||||
|
static int _GetNote(float* samples, double* maxVolume, float* weights);
|
||||||
|
static float _AnalyzeToneFunc(float* samples, int toneIndex);
|
||||||
|
};
|
||||||
@@ -3,11 +3,15 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace Vocaluxe.Lib.Sound.Record
|
namespace Vocaluxe.Lib.Sound.Record
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Analyzer from Performous
|
||||||
|
/// Should be very reliable but needs some proof (artificial tests show some errors)
|
||||||
|
/// </summary>
|
||||||
class CAnalyzer : IDisposable
|
class CAnalyzer : IDisposable
|
||||||
{
|
{
|
||||||
#region Imports
|
#region Imports
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern IntPtr Analyzer_Create(double baseToneFrequency, int minHalfTone, int maxHalfTone, uint step = 200);
|
private static extern IntPtr Analyzer_Create(uint step = 200);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern void Analyzer_Free(IntPtr analyzer);
|
private static extern void Analyzer_Free(IntPtr analyzer);
|
||||||
@@ -27,9 +31,6 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern double Analyzer_GetPeak(IntPtr analyzer);
|
private static extern double Analyzer_GetPeak(IntPtr analyzer);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
private static extern int Analyzer_GetNoteFast(IntPtr analyzer, [Out] out double maxVolume, [Out] float[] weights);
|
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern double Analyzer_FindNote(IntPtr analyzer, double minFreq, double maxFreq);
|
private static extern double Analyzer_FindNote(IntPtr analyzer, double minFreq, double maxFreq);
|
||||||
|
|
||||||
@@ -39,9 +40,9 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
|
|
||||||
private IntPtr _Instance;
|
private IntPtr _Instance;
|
||||||
|
|
||||||
public CAnalyzer(double baseToneFrequency, int minHalfTone, int maxHalfTone, uint step = 200)
|
public CAnalyzer(uint step = 200)
|
||||||
{
|
{
|
||||||
_Instance = Analyzer_Create(baseToneFrequency, minHalfTone, maxHalfTone, step);
|
_Instance = Analyzer_Create(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
~CAnalyzer()
|
~CAnalyzer()
|
||||||
@@ -79,11 +80,6 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
return Analyzer_FindNote(_Instance, minFreq, maxFreq);
|
return Analyzer_FindNote(_Instance, minFreq, maxFreq);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetNoteFast(out double maxVolume, float[] weights)
|
|
||||||
{
|
|
||||||
return Analyzer_GetNoteFast(_Instance, out maxVolume, weights);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Output(float[] data, float rate)
|
public bool Output(float[] data, float rate)
|
||||||
{
|
{
|
||||||
return Analyzer_OutputFloat(_Instance, data, data.Length, rate);
|
return Analyzer_OutputFloat(_Instance, data, data.Length, rate);
|
||||||
@@ -104,22 +100,63 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class CFastAnalyzer
|
/// <summary>
|
||||||
|
/// Pitchtracker (Pt) that uses autocorrelation (AKF) and AMDF
|
||||||
|
/// Quite fast and perfect in artificial tests, but may fail in real world scenarios (e.g. missing fundamental)
|
||||||
|
/// </summary>
|
||||||
|
class CPtAKF : IDisposable
|
||||||
{
|
{
|
||||||
#region Imports
|
#region Imports
|
||||||
[DllImport("PitchTracker.dll", EntryPoint = "PtFast_Init", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void Init(double baseToneFrequency, int minHalfTone, int maxHalfTone);
|
private static extern IntPtr PtAKF_Create();
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", EntryPoint = "PtFast_DeInit", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
public static extern void DeInit();
|
private static extern void PtAKF_Free(IntPtr analyzer);
|
||||||
|
|
||||||
[DllImport("PitchTracker.dll", EntryPoint = "PtFast_GetTone", CallingConvention = CallingConvention.Cdecl)]
|
[DllImport("PitchTracker.dll", EntryPoint = "PtAKF_GetNumHalfTones", CallingConvention = CallingConvention.Cdecl)]
|
||||||
private static extern int PtFast_GetTone([In] short[] samples, int sampleCt, [Out] out double maxVolume, [Out] float[] weights);
|
public static extern int GetNumHalfTones();
|
||||||
|
|
||||||
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern void PtAKF_InputByte(IntPtr analyzer, [In] byte[] data, int sampleCt);
|
||||||
|
|
||||||
|
[DllImport("PitchTracker.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
private static extern int PtAKF_GetNote(IntPtr analyzer, [Out] out double maxVolume, [Out] float[] weights);
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public static int GetTone(short[] samples, out double maxVolume, float[] weights)
|
private IntPtr _Instance;
|
||||||
|
|
||||||
|
public CPtAKF()
|
||||||
{
|
{
|
||||||
return PtFast_GetTone(samples, samples.Length, out maxVolume, weights);
|
_Instance = PtAKF_Create();
|
||||||
|
}
|
||||||
|
|
||||||
|
~CPtAKF()
|
||||||
|
{
|
||||||
|
_Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Input(byte[] data)
|
||||||
|
{
|
||||||
|
PtAKF_InputByte(_Instance, data, data.Length / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetNote(out double maxVolume, float[] weights)
|
||||||
|
{
|
||||||
|
return PtAKF_GetNote(_Instance, out maxVolume, weights);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_Instance == IntPtr.Zero)
|
||||||
|
throw new ObjectDisposedException(GetType().Name);
|
||||||
|
PtAKF_Free(_Instance);
|
||||||
|
_Instance = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -29,22 +29,19 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
{
|
{
|
||||||
class CBuffer : IDisposable
|
class CBuffer : IDisposable
|
||||||
{
|
{
|
||||||
|
#if TEST_PITCH
|
||||||
//Half tones: C C♯ D D# E F F♯ G G♯ A A# B
|
//Half tones: C C♯ D D# E F F♯ G G♯ A A# B
|
||||||
private const double _BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
|
private const double _BaseToneFreq = 65.4064; // lowest (half-)tone to analyze (C2 = 65.4064 Hz)
|
||||||
#if TEST_PITCH
|
|
||||||
private const double _HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
private const double _HalftoneBase = 1.05946309436; // 2^(1/12) -> HalftoneBase^12 = 2 (one octave)
|
||||||
#endif
|
#endif
|
||||||
private const int _HalfToneMin = 0; // C2
|
private CPtAKF _Analyzer = new CPtAKF();
|
||||||
private const int _HalfToneMax = 38; //45; // inclusive, 38=D5; 45=A5
|
|
||||||
public const int NumHalfTones = _HalfToneMax - _HalfToneMin + 1; //Number of halftones to analyze
|
|
||||||
private CAnalyzer _Analyzer = new CAnalyzer(_BaseToneFreq, _HalfToneMin, _HalfToneMax);
|
|
||||||
|
|
||||||
private double _MaxVolume;
|
private double _MaxVolume;
|
||||||
|
|
||||||
public CBuffer()
|
public CBuffer()
|
||||||
{
|
{
|
||||||
MinVolume = 0.02f;
|
MinVolume = 0.02f;
|
||||||
ToneWeigths = new float[NumHalfTones];
|
ToneWeigths = new float[GetNumHalfTones()];
|
||||||
Reset();
|
Reset();
|
||||||
#if TEST_PITCH
|
#if TEST_PITCH
|
||||||
_TestPitchDetection();
|
_TestPitchDetection();
|
||||||
@@ -56,6 +53,11 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
_Dispose();
|
_Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int GetNumHalfTones()
|
||||||
|
{
|
||||||
|
return CPtAKF.GetNumHalfTones();
|
||||||
|
}
|
||||||
|
|
||||||
public int ToneAbs { get; private set; }
|
public int ToneAbs { get; private set; }
|
||||||
|
|
||||||
public int Tone { get; set; }
|
public int Tone { get; set; }
|
||||||
@@ -98,7 +100,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
{
|
{
|
||||||
//_Analyzer.Process();
|
//_Analyzer.Process();
|
||||||
//_MaxVolume = _Analyzer.GetPeak() / 43 + 1;
|
//_MaxVolume = _Analyzer.GetPeak() / 43 + 1;
|
||||||
int tone = _Analyzer.GetNoteFast(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); //
|
int tone = _Analyzer.GetNote(out _MaxVolume, ToneWeigths); //(int)Math.Round(_Analyzer.FindNote()); //
|
||||||
if (tone >= 0 && _MaxVolume >= MinVolume)
|
if (tone >= 0 && _MaxVolume >= MinVolume)
|
||||||
{
|
{
|
||||||
ToneAbs = tone;
|
ToneAbs = tone;
|
||||||
@@ -142,7 +144,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
if (_PitchTestRun)
|
if (_PitchTestRun)
|
||||||
return;
|
return;
|
||||||
_PitchTestRun = true;
|
_PitchTestRun = true;
|
||||||
int toneFrom = Math.Max(0, _HalfToneMin);
|
int toneFrom = 0;
|
||||||
const int toneTo = 47; //B5
|
const int toneTo = 47; //B5
|
||||||
Console.WriteLine("Testing notes " + _ToneToNote(toneFrom) + " - " + _ToneToNote(toneTo));
|
Console.WriteLine("Testing notes " + _ToneToNote(toneFrom) + " - " + _ToneToNote(toneTo));
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ namespace Vocaluxe.Lib.Sound.Record
|
|||||||
|
|
||||||
public int NumHalfTones()
|
public int NumHalfTones()
|
||||||
{
|
{
|
||||||
return CBuffer.NumHalfTones;
|
return CBuffer.GetNumHalfTones();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float[] ToneWeigth(int player)
|
public float[] ToneWeigth(int player)
|
||||||
|
|||||||
Reference in new issue
Block a user