From 0a3947ebf26d54450503b78a8df24d88a186e8d2 Mon Sep 17 00:00:00 2001 From: Felix The Cat Date: Wed, 6 Jan 2021 12:13:14 +0300 Subject: [PATCH] Improved handling of audio subsystem delays --- Measurer.cpp | 40 +++++++++++++++++++++-------- Measurer.h | 11 ++++++++ fft.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++ fft.h | 61 +-------------------------------------------- main.cpp | 2 ++ soundthermo-cpp.pro | 1 + 6 files changed, 104 insertions(+), 71 deletions(-) create mode 100755 fft.cpp diff --git a/Measurer.cpp b/Measurer.cpp index a6d7db7..e502e91 100644 --- a/Measurer.cpp +++ b/Measurer.cpp @@ -67,22 +67,29 @@ float Measurer::Measure(float duration) float Measurer::ProcessData(QByteArray data) { // convert byte data to sample array - vector samples; + vector read_samples; // note that we fixed format to be little-endian, two-byte, signed for (int i = 0; i < data.count(); i += 2) { int t = qFromLittleEndian(data.data() + i); - samples.push_back(t / 32767.0); + read_samples.push_back(t / 32767.0); } if (debug) { ostringstream oss; - for (int i = 0; i < (int)samples.size(); i++) + for (int i = 0; i < (int)read_samples.size(); i++) { - oss << samples[i] << " "; + oss << read_samples[i] << " "; } cerr << "Samples read: " << oss.str() << "\n"; } + // cut off some initial samples because of delays in sound subsystem + // (otherwise we can hear some older sound which is not finished playing yet) + vector samples; + for (int i = (int)(read_samples.size() * INITIAL_DELAY_PERCENTAGE); i < (int)read_samples.size(); i++) + { + samples.push_back(read_samples[i]); + } // determine silence threshold (will consider signal less than SILENCE of maximum as silence) float max_sample = abs(samples[0]); for (int i = 1; i < (int)samples.size(); i++) @@ -109,6 +116,11 @@ float Measurer::ProcessData(QByteArray data) d[i] = Complex(samples[left_pos + i], 0); } FFT(d, pow, false); + return ProcessSpectrum (pow, d); +} + +float Measurer::ProcessSpectrum(int pow, Complex *d) +{ if (debug) { ostringstream oss; @@ -119,24 +131,30 @@ float Measurer::ProcessData(QByteArray data) } cerr << "Spectrum: " << oss.str() << "\n"; } + // will search for maximum value near our frequency + // also will + float desired = TONE_FREQUENCY * pow / SAMPLE_RATE; + int find_left = (int)((TONE_FREQUENCY - FREQUENCY_GAP) * pow / SAMPLE_RATE); + if (find_left < 0) find_left = 0; + int find_right = (int)((TONE_FREQUENCY + FREQUENCY_GAP) * pow / SAMPLE_RATE); + if (find_right > pow) find_right = pow; float max = -1; - int argmax = 0; - for (int i = 0; i < pow; i++) + float avg = 0; + for (int i = find_left; i < find_right; i++) { float amp = (float)sqrt(d[i].real() * d[i].real() + d[i].imag() * d[i].imag()); if (amp > max) { max = amp; - argmax = i; } + avg += amp; } - float desired = TONE_FREQUENCY * pow / SAMPLE_RATE; + avg /= (find_right - find_left); if (debug) { - cerr << "Measure: desired = " << desired << " argmax = " << argmax << " max = " - << max << " left_pos = " << left_pos << " right_pos = " << right_pos << "\n"; + cerr << "ProcessSpectrum: desired = " << desired << " avg = " << avg << " max = " << max << "\n"; } - if (abs(desired - argmax) < 1e-3) + if (abs(max / avg) - 1 > PEAK_LEVEL) { return max / pow; } diff --git a/Measurer.h b/Measurer.h index 37aa04a..2d9768a 100644 --- a/Measurer.h +++ b/Measurer.h @@ -3,6 +3,8 @@ #include +#include "fft.h" + enum MeasureChannel { Left, @@ -11,15 +13,24 @@ enum MeasureChannel class Measurer { + // frequency for generating tone const int TONE_FREQUENCY = 6000; + // frequency window width to search for resulting tone + const int FREQUENCY_GAP = 100; + // our resulting amplitude should differ at least this times from average + // to be considered as measured signal + const float PEAK_LEVEL = 1.0; + // const int SAMPLE_RATE = 44100; const float SILENCE_LEVEL = 0.3; + const float INITIAL_DELAY_PERCENTAGE = 0.3; MeasureChannel channel; QAudioDeviceInfo input, output; bool debug; float ProcessData(QByteArray data); + float ProcessSpectrum(int pow, Complex* d); public: Measurer(QAudioDeviceInfo input, QAudioDeviceInfo output, MeasureChannel channel, bool debug); float Measure(float duration); diff --git a/fft.cpp b/fft.cpp new file mode 100755 index 0000000..8ceb905 --- /dev/null +++ b/fft.cpp @@ -0,0 +1,60 @@ +#include +#include "fft.h" + +using namespace std; + +const long double PI = 3.1415926535897932384626433832795; + +int Rev(int i, int l) +{ + int res = 0; + int j; + for (j = 0; j < l; j++) + { + res <<= 1; + if (i & (1 << j)) res++; + } + return res; +} + +void FFT(Complex *a, int n, bool inv) +{ + int i, j, k; + int logn = 0; + while ((1 << logn) < n) logn++; + for (i = 0; i < n; i++) + { + j = Rev(i, logn); + if (j > i) + { + Complex t = a[i]; + a[i] = a[j]; + a[j] = t; + } + } + Complex u, v; + for (i = 1; i <= logn; i++) + { + long double ang = 2 * PI / (1 << i); + if (inv) ang = -ang; + Complex w(cos(ang), sin(ang)); + for (j = 0; j < n; j += (1 << i)) + { + Complex wk(1, 0); + for (k = j; k < j + (1 << (i - 1)); k++) + { + u = a[k]; + v = a[k + (1 << (i - 1))] * wk; + a[k] = u + v; + a[k + (1 << (i - 1))] = u - v; + wk *= w; + } + wk *= w; + } + } + if (inv) + { + for (i = 0; i < n; i++) a[i] /= n; + } +} + diff --git a/fft.h b/fft.h index baf7038..00bbdff 100755 --- a/fft.h +++ b/fft.h @@ -10,64 +10,5 @@ using namespace std; /* Defines Fast Fourier Transform */ void FFT(Complex *a, int n, bool inv = false); - -/* - IMPLEMENTATION -*/ - -const long double PI = 3.1415926535897932384626433832795; - -int Rev(int i, int l) -{ - int res = 0; - int j; - for (j = 0; j < l; j++) - { - res <<= 1; - if (i & (1 << j)) res++; - } - return res; -} - -void FFT(Complex *a, int n, bool inv) -{ - int i, j, k; - int logn = 0; - while ((1 << logn) < n) logn++; - for (i = 0; i < n; i++) - { - j = Rev(i, logn); - if (j > i) - { - Complex t = a[i]; - a[i] = a[j]; - a[j] = t; - } - } - Complex u, v; - for (i = 1; i <= logn; i++) - { - long double ang = 2 * PI / (1 << i); - if (inv) ang = -ang; - Complex w(cos(ang), sin(ang)); - for (j = 0; j < n; j += (1 << i)) - { - Complex wk(1, 0); - for (k = j; k < j + (1 << (i - 1)); k++) - { - u = a[k]; - v = a[k + (1 << (i - 1))] * wk; - a[k] = u + v; - a[k + (1 << (i - 1))] = u - v; - wk *= w; - } - wk *= w; - } - } - if (inv) - { - for (i = 0; i < n; i++) a[i] /= n; - } -} - + #endif diff --git a/main.cpp b/main.cpp index 291efc5..95f9b71 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "Measurer.h" @@ -55,6 +56,7 @@ void Measure(QString input_device_name, QString output_device_name, float durati auto output = GetOutputDevice(output_device_name); Measurer left(input, output, MeasureChannel::Left, debug); float left_result = left.Measure(duration); + //usleep(duration * 1000000); Measurer right(input, output, MeasureChannel::Right, debug); float right_result = right.Measure(duration); if (debug) diff --git a/soundthermo-cpp.pro b/soundthermo-cpp.pro index 4f25174..3079821 100644 --- a/soundthermo-cpp.pro +++ b/soundthermo-cpp.pro @@ -18,6 +18,7 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ Generator.cpp \ Measurer.cpp \ + fft.cpp \ main.cpp # Default rules for deployment.