Use window function

Update reference notes
Improve comparison
This commit is contained in:
Flamefire committed 2014-04-12 14:03:10 +02:00
1 parent 7c380f62f1
commit be6a40aa1d
4 files changed
+108 -73

No files matched your search

Binary file not shown.
+82 -58
View File
@@ -108,24 +108,26 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
//maxVolumeL /= 32767.;
*maxVolume = maxVolumeL;
float samplesWindowed[_SampleCt];
for(int i = 0; i < _SampleCt; i++){
samples[i] *= _Window[i];
samplesWindowed[i] = samples[i] * _Window[i];
}
// Now analyze the samples and get peaks at the most appropriate tones
SPeak peaks[_MaxPeaks];
InitPeaks(peaks);
//Attention: We have a peak at lag 0 that might stretch that far, that we detect a wrong "peak" at _MaxHalfTone
//Because of that we filter out all tones that are past the last zero crossing from below but keep tones with decreasing weights (going towards zero crossing from above)
int lastValidTone = 0;
float lastWeight = 1.f;
float maxWeight = 0.f;
for (int toneIndex = _MinHalfTone; toneIndex <= _MaxHalfTone; toneIndex++)
{
float curWeight = _AnalyzeByTone(samples, toneIndex);
float curWeight = _AnalyzeByTone(samples, samplesWindowed, toneIndex);
AddPeak(peaks, curWeight, toneIndex);
if(curWeight > maxWeight){
maxWeight = curWeight;
}
weights[toneIndex - _MinHalfTone] = curWeight;
@@ -137,7 +139,7 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
// Now clear off the lag 0 peak if required
if(peaks[_MaxPeaks - 1].toneIndex ==_MaxHalfTone){
if(maxWeight >= weights[_MaxHalfTone]){
//We might have caught the lag 0 peak so go a bit further to check for other zero crossings (or we won't be able to detect _MaxHalfTone)
float lastWeight = _AKFByTone(samples, _MaxHalfTone);
for(int toneIndex = _MaxHalfTone+1; toneIndex <= _MaxHalfTone + _HalfTonesAdd; toneIndex++){
@@ -148,40 +150,57 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
}
lastWeight = curWeight;
}
if(lastValidTone < peaks[_MaxPeaks - 1].toneIndex){
InitPeaks(peaks);
for(int toneIndex = _MinHalfTone; toneIndex <= lastValidTone; toneIndex++){
AddPeak(peaks, weights[toneIndex - _MinHalfTone], toneIndex);
}
//Set all invalid weights to 0
for(int toneIndex = lastValidTone + 1;toneIndex < _MaxHalfTone; toneIndex++){
weights[toneIndex - _MinHalfTone] = 0.f;
}
//Set all invalid weights to 0
for(int toneIndex = lastValidTone + 1;toneIndex < _MaxHalfTone; toneIndex++){
weights[toneIndex - _MinHalfTone] = 0.f;
}
}
SPeak peaks[_MaxPeaks];
InitPeaks(peaks);
int numPeaks = 0;
bool up = true;
for(int i = _MinHalfTone + 1; i <= _MaxHalfTone; i++){
if(weights[i-1] > weights[i]){
if(up){
AddPeak(peaks, weights[i - 1 - _MinHalfTone], i - 1);
numPeaks++;
up = false;
}
}else if(!up){
up = true;
}
}
if(up){
AddPeak(peaks, weights[_MaxHalfTone - _MinHalfTone], _MaxHalfTone);
numPeaks++;
}
if(numPeaks > _MaxPeaks)
numPeaks = _MaxPeaks;
// Further analyze the found peaks and find the real maximum
// 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
int maxTone = peaks[_MaxPeaks - 1].toneIndex;
int maxTone = peaks[_MaxPeaks-1].toneIndex;
int maxToneFine = -1;
float maxWeight = peaks[_MaxPeaks - 1].weight;
for(int i = 0; i < _MaxPeaks; i++){
maxWeight = peaks[_MaxPeaks-1].weight;
for(int i = _MaxPeaks - numPeaks; i < _MaxPeaks; i++){
float curWeight = peaks[i].weight;
if(curWeight * 3.f < maxWeight)
continue;
int toneIndex = peaks[i].toneIndex;
float curWeightCenter = peaks[i].weight;
float curWeightDown = _AnalyzeBySampleCt(samples, _SamplesPerPeriodPerToneFine[toneIndex * 2]);
float curWeight;
float curWeightDown = _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerToneFine[toneIndex * 2]);
int otherToneIndex;
int otherToneFineIndex;
if(curWeightDown > curWeightCenter){
if(curWeightDown > curWeight){
otherToneIndex = toneIndex - 1;
otherToneFineIndex = 1;
curWeight = curWeightDown;
}else{
float curWeightUp = _AnalyzeBySampleCt(samples, _SamplesPerPeriodPerToneFine[toneIndex * 2 + 1]);
if(curWeightUp > curWeightCenter){
otherToneIndex = (toneIndex < _MaxHalfTone)? toneIndex + 1: -1; // If the other tone is invalid just set it to -1 which will be skipped below
float curWeightUp = _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerToneFine[toneIndex * 2 + 1]);
if(curWeightUp > curWeight){
otherToneIndex = (toneIndex < _MaxHalfTone) ? toneIndex + 1: -1; // If the other tone is invalid just set it to -1 which will be skipped below
otherToneFineIndex = 0;
curWeight = curWeightUp;
}else
@@ -196,7 +215,7 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
// Now check also neighbouring tone
if(otherToneIndex > 0){
float curWeightOther = _AnalyzeBySampleCt(samples, _SamplesPerPeriodPerToneFine[otherToneIndex * 2 + otherToneFineIndex]);
float curWeightOther = _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerToneFine[otherToneIndex * 2 + otherToneFineIndex]);
if(curWeightOther > weights[otherToneIndex - _MinHalfTone]){
weights[otherToneIndex - _MinHalfTone] = curWeightOther;
if(curWeightOther > maxWeight){
@@ -209,45 +228,26 @@ int PtAKF::_GetNote(float* restrict samples, double* restrict maxVolume, float*
}
float energy = _AKFBySampleCt(samples, 0);
float maxAKF = (maxToneFine < 0) ? _AKFByTone(samples, maxTone):_AKFBySampleCt(samples, _SamplesPerPeriodPerToneFine[maxTone * 2 + maxToneFine]);
float aa = _AKFByTone(samples, maxTone);
float maxAKF = (maxToneFine < 0) ? _AKFByTone(samples, maxTone) : _AKFBySampleCt(samples, _SamplesPerPeriodPerToneFine[maxTone * 2 + maxToneFine]);
//if(maxWeight - minWeight > 0.025){
if(maxAKF >= 0.4 * energy){
if(maxAKF >= 0.35f * energy){
return maxTone;
}else return -1;
}
float PtAKF::_AnalyzeByTone(float* restrict samples, int toneIndex){
return _AnalyzeBySampleCt(samples, _SamplesPerPeriodPerTone[toneIndex]);
float PtAKF::_AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex){
return _AnalyzeBySampleCt(samples, samplesWindowed, _SamplesPerPeriodPerTone[toneIndex]);
}
float PtAKF::_AnalyzeBySampleCt(float* restrict samples, float samplesPerPeriodD){
float PtAKF::_AnalyzeBySampleCt(float* restrict samples, float* restrict samplesWindowed, 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
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;
float akf = _AKFBySampleCt(samplesWindowed, samplesPerPeriodD);
float amdf = _AMDFBySampleCt(samples, samplesPerPeriodD);
//return accumDistAKF / (accumDistAMDF * 32767.f + 32767.f * 32767.f); // Need to scale AKF by MAX^2 and AMDF by MAX, so do some maths to divide only once
float result = accumDistAKF / (accumDistAMDF + 1.f);
float result = akf / (amdf + 1.f);
return result; // Need to scale AKF by MAX^2 and AMDF by MAX, so do some maths to divide only once
//{toneIndex}: {accumDistAKF} ; {accumDistAMDF}; {result}
}
@@ -257,12 +257,11 @@ float PtAKF::_AKFByTone(float* restrict samples, int toneIndex){
}
float PtAKF::_AKFBySampleCt(float* restrict samples, float samplesPerPeriodD){
// Do a fast, non-scaled AKF (used e.g. for Lag 0 peak removal)
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
float fHigh = samplesPerPeriodD - samplesPerPeriod;
float fLow = 1.0f - fHigh;
float accumDistAKF = 0; // accumulated distances
float accumDist = 0; // accumulated distances
// compare correlating samples
int sampleIndex = 0; // index of sample to analyze
@@ -272,10 +271,35 @@ float PtAKF::_AKFBySampleCt(float* restrict samples, float samplesPerPeriodD){
// calc distance to corresponding sample in next period
float xn = samples[sampleIndex];
float xnt = samples[correlatingSampleIndex] * fLow + samples[correlatingSampleIndex + 1] * fHigh;
accumDistAKF += xn * xnt;
accumDist += xn * xnt;
}
return accumDistAKF;
return accumDist / _SampleCt;
}
float PtAKF::_AMDFByTone(float* restrict samples, int toneIndex){
return _AKFBySampleCt(samples, _SamplesPerPeriodPerTone[toneIndex]);
}
float PtAKF::_AMDFBySampleCt(float* restrict samples, float samplesPerPeriodD){
int samplesPerPeriod = static_cast<int>(samplesPerPeriodD);
float fHigh = samplesPerPeriodD - samplesPerPeriod;
float fLow = 1.0f - fHigh;
float accumDist = 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;
accumDist += abs(xn - xnt);
}
return accumDist / sampleIndex;
}
/*
+6 -4
View File
@@ -41,19 +41,21 @@ private:
static float* restrict _SamplesPerPeriodPerToneFine;
static float* restrict _Window;
static constexpr int _MinHalfTone = 0; //C2
static constexpr int _MaxHalfTone = 55;//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 _MaxPeaks = 3;
static constexpr int _MaxPeaks = 10;
constexpr static size_t _SampleCt = 2048;
RingBuffer<_SampleCt * 2> _AnalysisBuf;
unsigned _Step;
static int _GetNote(float* restrict samples, double* restrict maxVolume, float* restrict weights);
static float _AnalyzeBySampleCt(float* restrict samples, float samplesPerPeriodD);
static inline float _AnalyzeByTone(float* restrict samples, int toneIndex);
static float _AnalyzeBySampleCt(float* restrict samples, float* restrict samplesWindowed, float samplesPerPeriodD);
static inline float _AnalyzeByTone(float* restrict samples, float* restrict samplesWindowed, int toneIndex);
static float _AKFBySampleCt(float* restrict samples, float samplesPerPeriodD);
static inline float _AKFByTone(float* restrict samples, int toneIndex);
static float _AMDFBySampleCt(float* restrict samples, float samplesPerPeriodD);
static inline float _AMDFByTone(float* restrict samples, int toneIndex);
static inline void InitPeaks(SPeak peaks[_MaxPeaks]);
static inline void AddPeak(SPeak peaks[_MaxPeaks], float curWeight, int toneIndex);
};
+20 -11
View File
@@ -193,8 +193,8 @@ namespace Vocaluxe.Lib.Sound.Record
if (_TestFile("toneG4.wav", 31))
ok++;
tests++;*/
_TestFile("sClausVoc.wav", "sClausVoc.txt", ref tests, ref ok);
_TestFile("whistling3.wav", "whistling3.txt", ref tests, ref ok);
_TestFile("sClausVoc.wav", "sClausVoc.txt", ref tests, ref ok);
_GetSineWave(_BaseToneFreq * Math.Pow(_HalftoneBase, 5), 44100, ref angle, out data);
Stopwatch sw = new Stopwatch();
@@ -255,6 +255,22 @@ namespace Vocaluxe.Lib.Sound.Record
return ok >= ct - 4;
}
private bool _IsNoteValid(int note, int time, IList<STimedNote> tones)
{
const int lastNoteMaxTimeDiff = 1024 * 1000 / 44100; // old note is valid for 1024 more samples
for (int i = 0; i < tones.Count; i++)
{
if (tones[i].Time > time)
break;
if (i + 1 == tones.Count || time <= tones[i + 1].Time + lastNoteMaxTimeDiff)
{
if (tones[i].Note == note)
return true;
}
}
return false;
}
private void _TestFile(string fileName, IList<STimedNote> tones, ref int ct, ref int ok)
{
CWavFile wavFile = new CWavFile();
@@ -270,9 +286,6 @@ namespace Vocaluxe.Lib.Sound.Record
int samplesRead = 0;
int curTimeIndex = -1;
int curNote = -1;
int lastNote = -1; //It is ok not to detect a note change immediately so the old note is kept for a bit as ok
int lastNoteEndTime = 0;
const int lastNoteMaxTimeDiff = 1024 * 1000 / 44100; // old note is valid for 1024 more samples
const int maxSamplesPerBatch = 512;
CAnalyzer analyzer2 = new CAnalyzer();
CPtDyWa analyzer3 = new CPtDyWa();
@@ -290,14 +303,10 @@ namespace Vocaluxe.Lib.Sound.Record
while (curTimeIndex + 1 < tones.Count && time >= tones[curTimeIndex + 1].Time)
{
curTimeIndex++;
lastNote = curNote;
lastNoteEndTime = tones[curTimeIndex].Time;
curNote = tones[curTimeIndex].Note;
}
if (curNote < 0)
continue;
if (lastNoteEndTime + lastNoteMaxTimeDiff < time)
lastNote = curNote;
int tone1 = ToneValid ? ToneAbs : -1;
int tone2 = (int)Math.Round(analyzer2.FindNote(64, 1770));
int tone3 = (int)Math.Round(analyzer3.GetNote());
@@ -308,9 +317,9 @@ namespace Vocaluxe.Lib.Sound.Record
CBase.Log.LogDebug("Note " + _ToneToNote(curNote) + " at " + time + "ms wrongly detected as " + _ToneToNote(ToneAbs));
else
ok++;*/
bool ok1 = tone1 == curNote || tone1 == lastNote;
bool ok2 = tone2 == curNote || tone2 == lastNote;
bool ok3 = tone3 == curNote || tone3 == lastNote;
bool ok1 = _IsNoteValid(tone1, time, tones);
bool ok2 = _IsNoteValid(tone2, time, tones);
bool ok3 = _IsNoteValid(tone3, time, tones);
if (!ok1 || !ok2 || !ok3)
{
CBase.Log.LogDebug("Note " + _ToneToNote(curNote) + " at " + time + "ms detected as " + _ToneToNote(tone1) + (ok1 ? "" : "(!)") + "; " + _ToneToNote(tone2) +