Smooth pitch result

This commit is contained in:
Flamefire committed 2014-04-25 16:11:40 +02:00
1 parent 29d2dce3fc
commit a85cdaf615
4 files changed
+82 -44

No files matched your search

+1 -1
View File
@@ -3,7 +3,7 @@
#include <cmath> #include <cmath>
float* short2FloatArray(short* in, size_t len){ float* short2FloatArray(short* in, size_t len){
const float maxShort = 32767.0; const float maxShort = 32767.0f;
float* result = static_cast<float*>(malloc(len * sizeof(float))); float* result = static_cast<float*>(malloc(len * sizeof(float)));
float* curOut = result; float* curOut = result;
+67 -33
View File
@@ -43,12 +43,15 @@ PtAKF::PtAKF(unsigned step){
_Window = new float[_SampleCt]; _Window = new float[_SampleCt];
double cosMult = 2. * M_PI / (_SampleCt - 1.); //To simplify and speed up double cosMult = 2. * M_PI / (_SampleCt - 1.); //To simplify and speed up
for (int i = 0; i < _SampleCt; i++) { for (int i = 0; i < _SampleCt; i++) {
_Window[i] = static_cast<float>(0.54 - 0.46 * cos(i * cosMult)); _Window[i] = static_cast<float>(0.64 - 0.36 * cos(i * cosMult));
} }
} }
_Step = step; _Step = step;
_VolTreshold = 0.01f; _VolTreshold = 0.01f;
_LastMaxVol = 0.f; _LastMaxVol = 0.f;
for(int i = 0; i < _SmoothCt; i++)
_LastTones[i] = -1;
_LastToneIndex = 0;
} }
PtAKF::~PtAKF(){ PtAKF::~PtAKF(){
@@ -67,15 +70,42 @@ void PtAKF::SetVolumeThreshold(float threshold){
int PtAKF::GetNote(float* restrict maxVolume, float* restrict weights){ int PtAKF::GetNote(float* restrict maxVolume, float* restrict weights){
float AnaylsisBuf[_SampleCt]; float AnaylsisBuf[_SampleCt];
size_t size = _AnalysisBuf.size(); int note = _LastTones[_LastToneIndex];
if(size > _SampleCt) *maxVolume = _LastMaxVol * 0.85f;
_AnalysisBuf.pop(size - _SampleCt); if(_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt)){
if(!_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt)){ do{
*maxVolume = _LastMaxVol * 0.85f; _AnalysisBuf.pop(_Step);
return -1; note = _GetNote(AnaylsisBuf, maxVolume, weights);
if(++_LastToneIndex >= _SmoothCt)
_LastToneIndex = 0;
_LastTones[_LastToneIndex] = note;
}while(_AnalysisBuf.read(AnaylsisBuf, AnaylsisBuf + _SampleCt));
note = _GetSmoothTone();
} }
_AnalysisBuf.pop(_Step); return note;
return _GetNote(AnaylsisBuf, maxVolume, weights); }
int PtAKF::_GetSmoothTone(){
int tones[_SmoothCt];
int ct = 0;
for(int i=0; i < _SmoothCt; i++){
int j;
int tone = _LastTones[i];
if(tone < 0)
continue;
for(j = ct; j > 0; j--){
if(tones[j-1] > tone)
tones[j] = tones[j-1];
else break;
}
tones[j] = tone;
ct++;
}
if(ct == 0)
return -1; //Nothing detected
if(ct % 2 == 0)
return (tones[ct/2] + tones[ct/2-1]) / 2; //For even cts get the mean of the 2 middle tones
return tones[ct/2]; //For odd cts get the median (middle tone)
} }
void PtAKF::InitPeaks(SPeak peaks[_MaxPeaks]){ void PtAKF::InitPeaks(SPeak peaks[_MaxPeaks]){
@@ -104,7 +134,7 @@ void PtAKF::AddPeak(SPeak peaks[], float curWeight, int toneIndex){
} }
} }
int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* restrict weights){ int PtAKF::_GetNote(float samples[_SampleCt], float* restrict maxVolume, float weights[_MaxHalfTone+1]){
// Calculate maximum volume // Calculate maximum volume
float maxVolumeL = 0; float maxVolumeL = 0;
@@ -132,15 +162,15 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
int lastValidTone = 0; int lastValidTone = 0;
float lastWeight = 1.f; float lastWeight = 1.f;
float maxWeight = 0.f; float maxWeight = 0.f;
for (int toneIndex = _MinHalfTone; toneIndex <= _MaxHalfTone; toneIndex++)
{ for (int toneIndex = 0; toneIndex <= _MaxHalfTone; toneIndex++){
float curWeight = _AnalyzeByTone(samples, samplesWindowed, toneIndex); float curWeight = _AnalyzeByTone(samples, samplesWindowed, toneIndex);
if(curWeight > maxWeight){ if(curWeight > maxWeight){
maxWeight = curWeight; maxWeight = curWeight;
} }
weights[toneIndex - _MinHalfTone] = curWeight; weights[toneIndex] = curWeight;
//Filter: //Filter:
if(lastWeight > curWeight || (lastWeight > 0.f && curWeight <= 0.f)) if(lastWeight > curWeight || (lastWeight > 0.f && curWeight <= 0.f))
@@ -161,9 +191,11 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
} }
lastWeight = curWeight; lastWeight = curWeight;
} }
if(lastValidTone < 0)
return -1;
//Set all invalid weights to 0 //Set all invalid weights to 0
for(int toneIndex = lastValidTone + 1;toneIndex <= _MaxHalfTone; toneIndex++){ for(int toneIndex = lastValidTone + 1;toneIndex <= _MaxHalfTone; toneIndex++){
weights[toneIndex - _MinHalfTone] = 0.f; weights[toneIndex] = 0.f;
} }
} }
@@ -171,10 +203,10 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
InitPeaks(peaks); InitPeaks(peaks);
int numPeaks = 0; int numPeaks = 0;
bool up = true; bool up = true;
for(int i = _MinHalfTone + 1; i <= _MaxHalfTone; i++){ for(int i = 1; i <= _MaxHalfTone; i++){
if(weights[i-1] > weights[i]){ if(weights[i - 1] > weights[i]){
if(up){ if(up){
AddPeak(peaks, weights[i - 1 - _MinHalfTone], i - 1); AddPeak(peaks, weights[i - 1], i - 1);
numPeaks++; numPeaks++;
up = false; up = false;
} }
@@ -182,8 +214,8 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
up = true; up = true;
} }
} }
if(up){ if(up && weights[_MaxHalfTone] > 0.001f){
AddPeak(peaks, weights[_MaxHalfTone - _MinHalfTone], _MaxHalfTone); AddPeak(peaks, weights[_MaxHalfTone], _MaxHalfTone);
numPeaks++; numPeaks++;
} }
if(numPeaks > _MaxPeaks) if(numPeaks > _MaxPeaks)
@@ -193,9 +225,11 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
// This is necessary because we may have our real peak a bit off the exact tone frequency // This is necessary because we may have our real peak a bit off the exact tone frequency
// and a 'wrong' peak that is exactly at another tone which might become higher than the one at the 'right' tone // and a 'wrong' peak that is exactly at another tone which might become higher than the one at the 'right' tone
maxWeight = peaks[_MaxPeaks-1].weight;
if(maxWeight < 0.00001f) // Just a small number to check for zero values
return -1;
int maxTone = peaks[_MaxPeaks-1].toneIndex; int maxTone = peaks[_MaxPeaks-1].toneIndex;
int maxToneFine = -1; int maxToneFine = -1;
maxWeight = peaks[_MaxPeaks-1].weight;
for(int i = _MaxPeaks - numPeaks; i < _MaxPeaks; i++){ for(int i = _MaxPeaks - numPeaks; i < _MaxPeaks; i++){
float curWeight = peaks[i].weight; float curWeight = peaks[i].weight;
if(curWeight * 3.f < maxWeight) if(curWeight * 3.f < maxWeight)
@@ -217,7 +251,7 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
}else }else
continue; continue;
} }
weights[toneIndex - _MinHalfTone] = curWeight; weights[toneIndex] = curWeight;
if(curWeight > maxWeight){ if(curWeight > maxWeight){
maxTone = toneIndex; maxTone = toneIndex;
maxToneFine = 1 - otherToneFineIndex; maxToneFine = 1 - otherToneFineIndex;
@@ -225,10 +259,10 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
} }
// Now check also neighbouring tone // Now check also neighbouring tone
if(otherToneIndex > 0){ if(otherToneIndex >= 0){
float curWeightOther = _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerToneFine[otherToneIndex * 2 + otherToneFineIndex]); float curWeightOther = _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerToneFine[otherToneIndex * 2 + otherToneFineIndex]);
if(curWeightOther > weights[otherToneIndex - _MinHalfTone]){ if(curWeightOther > weights[otherToneIndex]){
weights[otherToneIndex - _MinHalfTone] = curWeightOther; weights[otherToneIndex] = curWeightOther;
if(curWeightOther > maxWeight){ if(curWeightOther > maxWeight){
maxTone = otherToneIndex; maxTone = otherToneIndex;
maxToneFine = otherToneFineIndex; maxToneFine = otherToneFineIndex;
@@ -238,20 +272,20 @@ int PtAKF::_GetNote(float* restrict samples, float* restrict maxVolume, float* r
} }
} }
float energy = _AKFBySampleCt(samples, 0); float energy = _AKFBySampleCt(samplesWindowed, 0);
float maxAKF = (maxToneFine < 0) ? _AKFByTone(samples, maxTone) : _AKFBySampleCt(samples, _SamplesPerPeriodPerToneFine[maxTone * 2 + maxToneFine]); float maxAKF = (maxToneFine < 0) ? _AKFByTone(samplesWindowed, maxTone) : _AKFBySampleCt(samplesWindowed, _SamplesPerPeriodPerToneFine[maxTone * 2 + maxToneFine]);
//if(maxWeight - minWeight > 0.025){ //if(maxWeight - minWeight > 0.025){
if(maxAKF >= 0.35f * energy){ if(maxAKF >= 0.33f * energy){
return maxTone; return maxTone;
}else return -1; }else return -1;
} }
float PtAKF::_AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex){ float PtAKF::_AnalyzeByTone(float samples[_SampleCt], float samplesWindowed[_SampleCt], int toneIndex){
return _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerTone[toneIndex]); return _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerTone[toneIndex]);
} }
float PtAKF::_AnalyzeBySampleCt(float* restrict samples, float* restrict samplesWindowed, float samplesPerPeriodD){ float PtAKF::_AnalyzeBySampleCt(float samples[_SampleCt], float samplesWindowed[_SampleCt], float samplesPerPeriodD){
// Use method by Kobayashi and Shimamura (2001): Combine AKF and AMDF to a new f(z)=AKF(z)/(AMDF(z)+k) with k=1 // Use method by Kobayashi and Shimamura (2001): Combine AKF and AMDF to a new f(z)=AKF(z)/(AMDF(z)+k) with k=1
float akf = _AKFBySampleCt(samplesWindowed, samplesPerPeriodD); float akf = _AKFBySampleCt(samplesWindowed, samplesPerPeriodD);
@@ -263,11 +297,11 @@ float PtAKF::_AnalyzeBySampleCt(float* restrict samples, float* restrict samples
//{toneIndex}: {accumDistAKF} ; {accumDistAMDF}; {result} //{toneIndex}: {accumDistAKF} ; {accumDistAMDF}; {result}
} }
float PtAKF::_AKFByTone(float* restrict samples, int toneIndex){ float PtAKF::_AKFByTone(float samples[_SampleCt], int toneIndex){
return _AKFBySampleCt(samples, _SamplesPerPeriodPerTone[toneIndex]); return _AKFBySampleCt(samples, _SamplesPerPeriodPerTone[toneIndex]);
} }
float PtAKF::_AKFBySampleCt(float* restrict samples, float samplesPerPeriodD){ float PtAKF::_AKFBySampleCt(float samples[_SampleCt], float samplesPerPeriodD){
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD); int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
float fHigh = samplesPerPeriodD - samplesPerPeriod; float fHigh = samplesPerPeriodD - samplesPerPeriod;
float fLow = 1.0f - fHigh; float fLow = 1.0f - fHigh;
@@ -288,11 +322,11 @@ float PtAKF::_AKFBySampleCt(float* restrict samples, float samplesPerPeriodD){
return accumDist / _SampleCt; return accumDist / _SampleCt;
} }
float PtAKF::_AMDFByTone(float* restrict samples, int toneIndex){ float PtAKF::_AMDFByTone(float samples[_SampleCt], int toneIndex){
return _AKFBySampleCt(samples, _SamplesPerPeriodPerTone[toneIndex]); return _AKFBySampleCt(samples, _SamplesPerPeriodPerTone[toneIndex]);
} }
float PtAKF::_AMDFBySampleCt(float* restrict samples, float samplesPerPeriodD){ float PtAKF::_AMDFBySampleCt(float samples[_SampleCt], float samplesPerPeriodD){
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD); int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
float fHigh = samplesPerPeriodD - samplesPerPeriod; float fHigh = samplesPerPeriodD - samplesPerPeriod;
float fLow = 1.0f - fHigh; float fLow = 1.0f - fHigh;
+13 -9
View File
@@ -32,7 +32,7 @@ public:
int GetNote(float* restrict maxVolume, float* restrict weights); int GetNote(float* restrict maxVolume, float* restrict weights);
void SetVolumeThreshold(float threshold); void SetVolumeThreshold(float threshold);
float GetVolumeThreshold(){return _VolTreshold;} float GetVolumeThreshold(){return _VolTreshold;}
static int GetNumHalfTones(){ return _MaxHalfTone - _MinHalfTone + 1;} static int GetNumHalfTones(){ return _MaxHalfTone + 1;}
private: private:
static int _InitCount; static int _InitCount;
@@ -42,25 +42,29 @@ private:
// (cannot use halves as it would be ambiguous) // (cannot use halves as it would be ambiguous)
static float* restrict _SamplesPerPeriodPerToneFine; static float* restrict _SamplesPerPeriodPerToneFine;
static float* restrict _Window; static float* restrict _Window;
static constexpr int _MinHalfTone = 0; //C2 //static constexpr int _MinHalfTone = 0; //C2
static constexpr int _MaxHalfTone = 56;//47; //B5 static constexpr int _MaxHalfTone = 56;//47; //B5
static constexpr int _HalfTonesAdd = 4; //Additonal half tones to analyze to remove the peak at lag 0 static constexpr int _HalfTonesAdd = 4; //Additonal half tones to analyze to remove the peak at lag 0
static constexpr int _MaxPeaks = 10; static constexpr int _MaxPeaks = 10;
static constexpr int _SmoothCt = 3; //Number of samples used for smoothing the result
constexpr static size_t _SampleCt = 2048; constexpr static size_t _SampleCt = 2048;
RingBuffer<_SampleCt * 2> _AnalysisBuf; RingBuffer<_SampleCt * 2> _AnalysisBuf;
unsigned _Step; unsigned _Step;
float _VolTreshold; float _VolTreshold;
float _LastMaxVol; float _LastMaxVol;
int _LastTones[_SmoothCt];
int _LastToneIndex;
int _GetNote(float* restrict samples, float* restrict maxVolume, float* restrict weights); int _GetNote(float samples[_SampleCt], float* restrict maxVolume, float weights[_MaxHalfTone+1]);
int _GetSmoothTone();
static float _AnalyzeBySampleCt(float* restrict samples, float* restrict samplesWindowed, float samplesPerPeriodD); static float _AnalyzeBySampleCt(float samples[_SampleCt], float samplesWindowed[_SampleCt], float samplesPerPeriodD);
static inline float _AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex); static inline float _AnalyzeByTone(float samples[_SampleCt], float samplesWindowed[_SampleCt], int toneIndex);
static float _AKFBySampleCt(float* restrict samples, float samplesPerPeriodD); static float _AKFBySampleCt(float samples[_SampleCt], float samplesPerPeriodD);
static inline float _AKFByTone(float* restrict samples, int toneIndex); static inline float _AKFByTone(float samples[_SampleCt], int toneIndex);
static float _AMDFBySampleCt(float* restrict samples, float samplesPerPeriodD); static float _AMDFBySampleCt(float samples[_SampleCt], float samplesPerPeriodD);
static inline float _AMDFByTone(float* restrict samples, int toneIndex); static inline float _AMDFByTone(float samples[_SampleCt], int toneIndex);
static inline void InitPeaks(SPeak peaks[_MaxPeaks]); static inline void InitPeaks(SPeak peaks[_MaxPeaks]);
static inline void AddPeak(SPeak peaks[_MaxPeaks], float curWeight, int toneIndex); static inline void AddPeak(SPeak peaks[_MaxPeaks], float curWeight, int toneIndex);
}; };
@@ -34,7 +34,7 @@ namespace Vocaluxe.Lib.Sound.Record.PitchTracker
private IntPtr _Instance; private IntPtr _Instance;
public CPtAKF(uint step = 200) public CPtAKF(uint step = 400)
{ {
_Instance = PtAKF_Create(step); _Instance = PtAKF_Create(step);
} }