Compare commits
4
Commits
ce6169551a
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82c31965ba | ||
|
|
1f8e5d3eeb | ||
|
|
0a3947ebf2 | ||
|
|
2440d0aa3f |
+49
-13
@@ -67,27 +67,42 @@ float Measurer::Measure(float duration)
|
|||||||
float Measurer::ProcessData(QByteArray data)
|
float Measurer::ProcessData(QByteArray data)
|
||||||
{
|
{
|
||||||
// convert byte data to sample array
|
// convert byte data to sample array
|
||||||
vector<float> samples;
|
vector<float> read_samples;
|
||||||
// note that we fixed format to be little-endian, two-byte, signed
|
// note that we fixed format to be little-endian, two-byte, signed
|
||||||
for (int i = 0; i < data.count(); i += 2)
|
for (int i = 0; i < data.count(); i += 2)
|
||||||
{
|
{
|
||||||
int t = qFromLittleEndian<qint16>(data.data() + i);
|
int t = qFromLittleEndian<qint16>(data.data() + i);
|
||||||
samples.push_back(t / 32767.0);
|
read_samples.push_back(t / 32767.0);
|
||||||
}
|
}
|
||||||
if (debug)
|
if (debug)
|
||||||
{
|
{
|
||||||
ostringstream oss;
|
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";
|
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<float> 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++)
|
||||||
|
{
|
||||||
|
float q = abs(samples[i]);
|
||||||
|
if (q > max_sample) max_sample = q;
|
||||||
|
}
|
||||||
|
float silence_level = SILENCE_LEVEL * max_sample;
|
||||||
// cut off starting and ending silence
|
// cut off starting and ending silence
|
||||||
int left_pos = 0;
|
int left_pos = 0;
|
||||||
while (left_pos < (int)samples.size() && abs(samples[left_pos]) < 1e-4) left_pos++;
|
while (left_pos < (int)samples.size() && abs(samples[left_pos]) < silence_level) left_pos++;
|
||||||
int right_pos = (int)samples.size() - 1;
|
int right_pos = (int)samples.size() - 1;
|
||||||
while (right_pos >= 0 && abs(samples[right_pos]) < 1e-4) right_pos--;
|
while (right_pos >= 0 && abs(samples[right_pos]) < silence_level) right_pos--;
|
||||||
int len = right_pos - left_pos + 1;
|
int len = right_pos - left_pos + 1;
|
||||||
int pow = 1, m = 0;
|
int pow = 1, m = 0;
|
||||||
while (2 * pow <= len)
|
while (2 * pow <= len)
|
||||||
@@ -101,24 +116,45 @@ float Measurer::ProcessData(QByteArray data)
|
|||||||
d[i] = Complex(samples[left_pos + i], 0);
|
d[i] = Complex(samples[left_pos + i], 0);
|
||||||
}
|
}
|
||||||
FFT(d, pow, false);
|
FFT(d, pow, false);
|
||||||
float max = -1;
|
return ProcessSpectrum (pow, d);
|
||||||
int argmax = 0;
|
}
|
||||||
|
|
||||||
|
float Measurer::ProcessSpectrum(int pow, Complex *d)
|
||||||
|
{
|
||||||
|
if (debug)
|
||||||
|
{
|
||||||
|
ostringstream oss;
|
||||||
for (int i = 0; i < pow; i++)
|
for (int i = 0; i < pow; i++)
|
||||||
|
{
|
||||||
|
float amp = (float)sqrt(d[i].real() * d[i].real() + d[i].imag() * d[i].imag());
|
||||||
|
oss << amp << " ";
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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());
|
float amp = (float)sqrt(d[i].real() * d[i].real() + d[i].imag() * d[i].imag());
|
||||||
if (amp > max)
|
if (amp > max)
|
||||||
{
|
{
|
||||||
max = amp;
|
max = amp;
|
||||||
argmax = i;
|
|
||||||
}
|
}
|
||||||
|
avg += amp;
|
||||||
}
|
}
|
||||||
float desired = TONE_FREQUENCY * pow / SAMPLE_RATE;
|
avg /= (find_right - find_left);
|
||||||
if (debug)
|
if (debug)
|
||||||
{
|
{
|
||||||
cerr << "Measure: desired = " << desired << " argmax = " << argmax << " max = "
|
cerr << "ProcessSpectrum: desired = " << desired << " avg = " << avg << " max = " << max << "\n";
|
||||||
<< max << " left_pos = " << left_pos << " right_pos = " << right_pos << "\n";
|
|
||||||
}
|
}
|
||||||
if (abs(desired - argmax) < 1e-3)
|
if (abs(max / avg) - 1 > PEAK_LEVEL)
|
||||||
{
|
{
|
||||||
return max / pow;
|
return max / pow;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-1
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <QAudioDeviceInfo>
|
#include <QAudioDeviceInfo>
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
|
||||||
enum MeasureChannel
|
enum MeasureChannel
|
||||||
{
|
{
|
||||||
Left,
|
Left,
|
||||||
@@ -11,14 +13,24 @@ enum MeasureChannel
|
|||||||
|
|
||||||
class Measurer
|
class Measurer
|
||||||
{
|
{
|
||||||
const int TONE_FREQUENCY = 6000;
|
// frequency for generating tone
|
||||||
|
const int TONE_FREQUENCY = 1000;
|
||||||
|
// 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 int SAMPLE_RATE = 44100;
|
||||||
|
const float SILENCE_LEVEL = 0.3;
|
||||||
|
const float INITIAL_DELAY_PERCENTAGE = 0.3;
|
||||||
|
|
||||||
MeasureChannel channel;
|
MeasureChannel channel;
|
||||||
QAudioDeviceInfo input, output;
|
QAudioDeviceInfo input, output;
|
||||||
bool debug;
|
bool debug;
|
||||||
|
|
||||||
float ProcessData(QByteArray data);
|
float ProcessData(QByteArray data);
|
||||||
|
float ProcessSpectrum(int pow, Complex* d);
|
||||||
public:
|
public:
|
||||||
Measurer(QAudioDeviceInfo input, QAudioDeviceInfo output, MeasureChannel channel, bool debug);
|
Measurer(QAudioDeviceInfo input, QAudioDeviceInfo output, MeasureChannel channel, bool debug);
|
||||||
float Measure(float duration);
|
float Measure(float duration);
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#include <complex>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -11,63 +11,4 @@ using namespace std;
|
|||||||
|
|
||||||
void FFT(Complex *a, int n, bool inv = false);
|
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
|
#endif
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "Measurer.h"
|
#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);
|
auto output = GetOutputDevice(output_device_name);
|
||||||
Measurer left(input, output, MeasureChannel::Left, debug);
|
Measurer left(input, output, MeasureChannel::Left, debug);
|
||||||
float left_result = left.Measure(duration);
|
float left_result = left.Measure(duration);
|
||||||
|
//usleep(duration * 1000000);
|
||||||
Measurer right(input, output, MeasureChannel::Right, debug);
|
Measurer right(input, output, MeasureChannel::Right, debug);
|
||||||
float right_result = right.Measure(duration);
|
float right_result = right.Measure(duration);
|
||||||
if (debug)
|
if (debug)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
Generator.cpp \
|
Generator.cpp \
|
||||||
Measurer.cpp \
|
Measurer.cpp \
|
||||||
|
fft.cpp \
|
||||||
main.cpp
|
main.cpp
|
||||||
|
|
||||||
# Default rules for deployment.
|
# Default rules for deployment.
|
||||||
|
|||||||
Reference in New Issue
Block a user