|
|
|
@@ -96,7 +96,7 @@ Visualizer::Visualizer()
|
|
|
|
memset(m_fftw_input, 0, sizeof(double)*DFT_TOTAL_SIZE);
|
|
|
|
memset(m_fftw_input, 0, sizeof(double)*DFT_TOTAL_SIZE);
|
|
|
|
m_fftw_output = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*m_fftw_results));
|
|
|
|
m_fftw_output = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*m_fftw_results));
|
|
|
|
m_fftw_plan = fftw_plan_dft_r2c_1d(DFT_TOTAL_SIZE, m_fftw_input, m_fftw_output, FFTW_ESTIMATE);
|
|
|
|
m_fftw_plan = fftw_plan_dft_r2c_1d(DFT_TOTAL_SIZE, m_fftw_input, m_fftw_output, FFTW_ESTIMATE);
|
|
|
|
m_dft_logspace.reserve(500);
|
|
|
|
m_dft_freqspace.reserve(500);
|
|
|
|
m_bar_heights.reserve(100);
|
|
|
|
m_bar_heights.reserve(100);
|
|
|
|
# endif // HAVE_FFTW3_H
|
|
|
|
# endif // HAVE_FFTW3_H
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -108,7 +108,7 @@ void Visualizer::switchTo()
|
|
|
|
m_reset_output = true;
|
|
|
|
m_reset_output = true;
|
|
|
|
drawHeader();
|
|
|
|
drawHeader();
|
|
|
|
# ifdef HAVE_FFTW3_H
|
|
|
|
# ifdef HAVE_FFTW3_H
|
|
|
|
GenLogspace();
|
|
|
|
GenFreqSpace();
|
|
|
|
m_bar_heights.reserve(w.getWidth());
|
|
|
|
m_bar_heights.reserve(w.getWidth());
|
|
|
|
# endif // HAVE_FFTW3_H
|
|
|
|
# endif // HAVE_FFTW3_H
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -122,7 +122,7 @@ void Visualizer::resize()
|
|
|
|
hasToBeResized = 0;
|
|
|
|
hasToBeResized = 0;
|
|
|
|
InitVisualization();
|
|
|
|
InitVisualization();
|
|
|
|
# ifdef HAVE_FFTW3_H
|
|
|
|
# ifdef HAVE_FFTW3_H
|
|
|
|
GenLogspace();
|
|
|
|
GenFreqSpace();
|
|
|
|
m_bar_heights.reserve(w.getWidth());
|
|
|
|
m_bar_heights.reserve(w.getWidth());
|
|
|
|
# endif // HAVE_FFTW3_H
|
|
|
|
# endif // HAVE_FFTW3_H
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -421,7 +421,7 @@ void Visualizer::DrawSoundEllipseStereo(const int16_t *buf_left, const int16_t *
|
|
|
|
auto c = toColor(sqrt(x*x + 4*y*y), radius, true);
|
|
|
|
auto c = toColor(sqrt(x*x + 4*y*y), radius, true);
|
|
|
|
w << NC::XY(left_half_width + x, top_half_height + y)
|
|
|
|
w << NC::XY(left_half_width + x, top_half_height + y)
|
|
|
|
<< c
|
|
|
|
<< c
|
|
|
|
<< Config.visualizer_chars[1]
|
|
|
|
<< Config.visualizer_chars[0]
|
|
|
|
<< NC::FormattedColor::End<>(c);
|
|
|
|
<< NC::FormattedColor::End<>(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -450,7 +450,7 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
|
|
|
|
const size_t win_width = w.getWidth();
|
|
|
|
const size_t win_width = w.getWidth();
|
|
|
|
|
|
|
|
|
|
|
|
size_t cur_bin = 0;
|
|
|
|
size_t cur_bin = 0;
|
|
|
|
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_logspace[0])
|
|
|
|
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_freqspace[0])
|
|
|
|
++cur_bin;
|
|
|
|
++cur_bin;
|
|
|
|
for (size_t x = 0; x < win_width; ++x)
|
|
|
|
for (size_t x = 0; x < win_width; ++x)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@@ -459,10 +459,10 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
|
|
|
|
// accumulate bins
|
|
|
|
// accumulate bins
|
|
|
|
size_t count = 0;
|
|
|
|
size_t count = 0;
|
|
|
|
// check right bound
|
|
|
|
// check right bound
|
|
|
|
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_logspace[x])
|
|
|
|
while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_freqspace[x])
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// check left bound if not first index
|
|
|
|
// check left bound if not first index
|
|
|
|
if (x == 0 || Bin2Hz(cur_bin) >= m_dft_logspace[x-1])
|
|
|
|
if (x == 0 || Bin2Hz(cur_bin) >= m_dft_freqspace[x-1])
|
|
|
|
{
|
|
|
|
{
|
|
|
|
bar_height += m_freq_magnitudes[cur_bin];
|
|
|
|
bar_height += m_freq_magnitudes[cur_bin];
|
|
|
|
++count;
|
|
|
|
++count;
|
|
|
|
@@ -476,8 +476,19 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
|
|
|
|
// average bins
|
|
|
|
// average bins
|
|
|
|
bar_height /= count;
|
|
|
|
bar_height /= count;
|
|
|
|
|
|
|
|
|
|
|
|
// log scale bar heights
|
|
|
|
// apply scaling to bar heights
|
|
|
|
bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE;
|
|
|
|
if (Config.visualizer_spectrum_log_scale_y) {
|
|
|
|
|
|
|
|
bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// apply gain
|
|
|
|
|
|
|
|
bar_height *= pow(10, 1.8 + GAIN / 20);
|
|
|
|
|
|
|
|
// buff higher frequencies
|
|
|
|
|
|
|
|
bar_height *= log2(2 + x) * 80.0/win_width;
|
|
|
|
|
|
|
|
// moderately normalize the heights
|
|
|
|
|
|
|
|
bar_height = pow(bar_height, 0.65);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//bar_height = pow(10, 1 + GAIN / 20) * bar_height;
|
|
|
|
|
|
|
|
}
|
|
|
|
// Scale bar height between 0 and height
|
|
|
|
// Scale bar height between 0 and height
|
|
|
|
bar_height = bar_height > 0 ? bar_height * height : 0;
|
|
|
|
bar_height = bar_height > 0 ? bar_height * height : 0;
|
|
|
|
bar_height = bar_height > height ? height : bar_height;
|
|
|
|
bar_height = bar_height > height ? height : bar_height;
|
|
|
|
@@ -499,7 +510,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
|
|
|
|
++h_idx;
|
|
|
|
++h_idx;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// data point does not exist, need to interpolate
|
|
|
|
// data point does not exist, need to interpolate
|
|
|
|
h = Interpolate(x, h_idx);
|
|
|
|
if (Config.visualizer_spectrum_log_scale_x) {
|
|
|
|
|
|
|
|
h = InterpolateCubic(x, h_idx);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
h = std::min(InterpolateLinear(x, h_idx), height / 1.0);
|
|
|
|
|
|
|
|
//h = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t j = 0; j < h; ++j)
|
|
|
|
for (size_t j = 0; j < h; ++j)
|
|
|
|
@@ -549,7 +565,7 @@ void Visualizer::DrawFrequencySpectrumStereo(const int16_t *buf_left, const int1
|
|
|
|
DrawFrequencySpectrum(buf_right, samples, height, w.getHeight() - height);
|
|
|
|
DrawFrequencySpectrum(buf_right, samples, height, w.getHeight() - height);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
double Visualizer::Interpolate(size_t x, size_t h_idx)
|
|
|
|
double Visualizer::InterpolateCubic(size_t x, size_t h_idx)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
const double x_next = m_bar_heights[h_idx].first;
|
|
|
|
const double x_next = m_bar_heights[h_idx].first;
|
|
|
|
const double h_next = m_bar_heights[h_idx].second;
|
|
|
|
const double h_next = m_bar_heights[h_idx].second;
|
|
|
|
@@ -594,6 +610,33 @@ double Visualizer::Interpolate(size_t x, size_t h_idx)
|
|
|
|
return h_next;
|
|
|
|
return h_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double Visualizer::InterpolateLinear(size_t x, size_t h_idx)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const double x_next = m_bar_heights[h_idx].first;
|
|
|
|
|
|
|
|
const double h_next = m_bar_heights[h_idx].second;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double dh = 0;
|
|
|
|
|
|
|
|
if (h_idx == 0) {
|
|
|
|
|
|
|
|
// no data points on the left, linear extrapolation
|
|
|
|
|
|
|
|
if (h_idx < m_bar_heights.size()) {
|
|
|
|
|
|
|
|
const double x_next2 = m_bar_heights[h_idx+1].first;
|
|
|
|
|
|
|
|
const double h_next2 = m_bar_heights[h_idx+1].second;
|
|
|
|
|
|
|
|
dh = (h_next2 - h_next) / (x_next2 - x_next);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return h_next - dh * (x_next - x);
|
|
|
|
|
|
|
|
} else if (h_idx < m_bar_heights.size()) {
|
|
|
|
|
|
|
|
// simple linear interpolation
|
|
|
|
|
|
|
|
const double x_prev = m_bar_heights[h_idx-1].first;
|
|
|
|
|
|
|
|
const double h_prev = m_bar_heights[h_idx-1].second;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const double m = (h_next - h_prev) / (x_next - x_prev);
|
|
|
|
|
|
|
|
return h_prev + m * (x - x_prev);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// no data points on the right: don't interpolate
|
|
|
|
|
|
|
|
return h_next;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Visualizer::ApplyWindow(double *output, const int16_t *input, ssize_t samples)
|
|
|
|
void Visualizer::ApplyWindow(double *output, const int16_t *input, ssize_t samples)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Use Blackman window for low sidelobes and fast sidelobe rolloff
|
|
|
|
// Use Blackman window for low sidelobes and fast sidelobe rolloff
|
|
|
|
@@ -622,10 +665,36 @@ void Visualizer::GenLogspace()
|
|
|
|
const size_t win_width = w.getWidth();
|
|
|
|
const size_t win_width = w.getWidth();
|
|
|
|
const size_t left_bins = (log10(HZ_MIN) - win_width*log10(HZ_MIN)) / (log10(HZ_MIN) - log10(HZ_MAX));
|
|
|
|
const size_t left_bins = (log10(HZ_MIN) - win_width*log10(HZ_MIN)) / (log10(HZ_MIN) - log10(HZ_MAX));
|
|
|
|
// Generate logspaced frequencies
|
|
|
|
// Generate logspaced frequencies
|
|
|
|
m_dft_logspace.resize(win_width);
|
|
|
|
m_dft_freqspace.resize(win_width);
|
|
|
|
const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_logspace.size() - 1);
|
|
|
|
const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_freqspace.size() - 1);
|
|
|
|
for (size_t i = left_bins; i < m_dft_logspace.size() + left_bins; ++i) {
|
|
|
|
for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) {
|
|
|
|
m_dft_logspace[i - left_bins] = pow(10, i * log_scale);
|
|
|
|
m_dft_freqspace[i - left_bins] = pow(10, i * log_scale);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate vector of linearly-spaced frequencies from HZ_MIN to HZ_MAX
|
|
|
|
|
|
|
|
void Visualizer::GenLinspace()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// Calculate number of extra bins needed between 0 HZ and HZ_MIN
|
|
|
|
|
|
|
|
const size_t win_width = w.getWidth();
|
|
|
|
|
|
|
|
const size_t left_bins = (HZ_MIN - win_width * HZ_MIN) / (HZ_MIN - HZ_MAX);
|
|
|
|
|
|
|
|
// Generate linspaced frequencies
|
|
|
|
|
|
|
|
m_dft_freqspace.resize(win_width);
|
|
|
|
|
|
|
|
const double lin_scale = HZ_MAX / (left_bins + m_dft_freqspace.size() - 1);
|
|
|
|
|
|
|
|
for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) {
|
|
|
|
|
|
|
|
m_dft_freqspace[i - left_bins] = i * lin_scale;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Generate vector of spectrum frequencies from HZ_MIN to HZ_MAX
|
|
|
|
|
|
|
|
// Frequencies are (not) log-scaled depending on
|
|
|
|
|
|
|
|
// Config.visualizer_spectrum_log_scale_x
|
|
|
|
|
|
|
|
void Visualizer::GenFreqSpace()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (Config.visualizer_spectrum_log_scale_x) {
|
|
|
|
|
|
|
|
GenLogspace();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
GenLinspace();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // HAVE_FFTW3_H
|
|
|
|
#endif // HAVE_FFTW3_H
|
|
|
|
|