diff --git a/src/audio/ZeroCross.cpp b/src/audio/ZeroCross.cpp index 1644d7c..56011d5 100644 --- a/src/audio/ZeroCross.cpp +++ b/src/audio/ZeroCross.cpp @@ -5,40 +5,52 @@ #include using namespace std; +using namespace patterns; /// Maximum rest of division to be a multiple of #define EPS_DIVISOR 0.1 /// Consider it is a correct pattern to avoid multiples of -#define CORRECT_DEVIATION 1 +#define CORRECT_TIME_DEVIATION 1 +/// max factor of deviation to consider multiple to be dropped +#define MAX_TIME_DEVIATION_FACTOR_MULTIPLE 2 +//#define CORRECT_ENERGY_DEVIATION 1 // local functions /** * Return average and standart deviation of pattern approx. interval in values */ -pair IsPattern(const vector &values, double interval) +PatternMatch IsPattern(const vector &values, double interval) { - double sum = 0, deviation = 0, current = 0; + double current = 0; int nb = 0; double diff = interval; + double sum_energy = 0, first_energy = -1; + PatternMatch match; - for (double v : values) { - if (fabs(diff - v) < fabs(diff)) { - current += v; + for (const Pattern &v : values) { + if (fabs(diff - v.time) < fabs(diff)) { + current += v.time; + sum_energy += v.energy; diff = interval - current; } else { - sum += current; - deviation += diff * diff; - current = 0; - diff = interval; + if (first_energy == -1) first_energy = sum_energy; + + match.time += current; + match.time_deviation += diff * diff; + match.energy_deviation += pow(first_energy - sum_energy, 2); + current = v.time; + sum_energy = v.energy; + diff = interval - current; nb++; } } - if (nb < 2) return pair(0,0); - sum /= nb; - deviation = sqrt(deviation) / nb; - return pair(sum, deviation); + if (nb < 2) return PatternMatch(); // empty pattern + match.time /= nb; // time is average + match.time_deviation = sqrt(match.time_deviation) / nb; + match.energy_deviation = sqrt(match.energy_deviation) / match.time; + return match; } /** @@ -47,21 +59,21 @@ pair IsPattern(const vector &values, double interval) * * @return best interval pattern, standart deviation for best pattern */ -pair FindPattern(const vector &values, double pattern_min, double pattern_max) +PatternMatch FindPattern(const vector &values, double pattern_min, double pattern_max) { - auto best = pair(0,0); + PatternMatch res, best; double interval = 0; - for (double v : values) { - interval += v; + for (const Pattern &v : values) { + interval += v.time; if (interval < pattern_min) continue; if (interval > pattern_max) break; - auto res = IsPattern(values, interval); -// cout << " " << res.first << " " << res.second << endl; - if (res.first && (res.second < best.second || best.first == 0)) { - if (best.first && best.second < CORRECT_DEVIATION) { - double div = res.first / best.first; + res = IsPattern(values, interval); + // cout << " " << res.time << " " << 16000/res.time << " " << res.time_deviation << " " << res.energy_deviation << endl; + if (res.time && (res.time_deviation < best.time_deviation || best.time == 0)) { + if (best.time && best.time_deviation < CORRECT_TIME_DEVIATION && best.time_deviation / res.time_deviation < MAX_TIME_DEVIATION_FACTOR_MULTIPLE) { + double div = res.time / best.time; // it is a multiple of previous if (fabs(div - round(div)) < EPS_DIVISOR) continue; // cout << "... " << div - floor(div) << endl; @@ -70,7 +82,7 @@ pair FindPattern(const vector &values, double pattern_min } } //cerr << " -> " << best.first << " " << best.second << endl; - if (best.second > CORRECT_DEVIATION) return pair(0,0); + if (best.time_deviation > CORRECT_TIME_DEVIATION) return PatternMatch(); // empty pattern return best; } @@ -88,7 +100,7 @@ template ZeroCross::ZeroCross(const Config &config) template void ZeroCross::Clear() { - nb_frame_current = -1; + pattern_current.time = -1; nb_frame_analysed = 0; freq = 0; last_zero = false; @@ -100,18 +112,22 @@ template void ZeroCross::ComputeFrame(sample_t x) double delta; // count frame only if ever crossed - if (nb_frame_current != -1) nb_frame_current++; + if (pattern_current.time != -1) { + pattern_current.time++; + pattern_current.energy += x * x; + } // get only from negative to positive if (x > 0 && last_sample < 0) { if (last_zero) delta = 1; else delta = (double) x / (x - last_sample); - if (nb_frame_current > 0) { - nb_frame_current -= delta; - pattern.push_back(nb_frame_current); + if (pattern_current.time > 0) { + pattern_current.time -= delta; + pattern.push_back(pattern_current); //cout << nb_frame_current << " "; } - nb_frame_current = delta; + pattern_current.time = delta; + pattern_current.energy = x * x; } // drop 0 values to know sign change if (x) last_sample = x, last_zero = false; @@ -119,7 +135,7 @@ template void ZeroCross::ComputeFrame(sample_t x) // compute if window suffisent if (nb_frame_analysed++ == nb_frame_to_analyse) { auto res = FindPattern(pattern, sample_min, sample_max); - if (res.first) freq = rate / res.first; + if (res.time) freq = rate / res.time; else freq = 0; nb_frame_analysed = 0; pattern.clear(); diff --git a/src/audio/ZeroCross.hpp b/src/audio/ZeroCross.hpp index 91f26d3..6eed09b 100644 --- a/src/audio/ZeroCross.hpp +++ b/src/audio/ZeroCross.hpp @@ -4,6 +4,18 @@ #include #include +namespace patterns { + + struct Pattern { + double time, energy; + }; + struct PatternMatch { + double time, time_deviation, energy_deviation; + PatternMatch() : time(0), time_deviation(0), energy_deviation(0) {} + }; + +}; + template class ZeroCross { public: struct Config { @@ -14,11 +26,11 @@ template class ZeroCross { private: int rate, nb_frame_to_analyse, nb_frame_analysed, diff_x; - double nb_frame_current; + patterns::Pattern pattern_current; int16_t last_sample; double freq, sample_min, sample_max; bool last_zero; - std::vector pattern; + std::vector pattern; inline void ComputeFrame(sample_t x);