Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions doc/config
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@
#
#visualizer_spectrum_hz_max = 20000
#
## Use log scaling for the frequency spectrum axes
#
#visualizer_spectrum_log_scale_x = yes
#visualizer_spectrum_log_scale_y = yes
#
##### system encoding #####
##
## ncmpcpp should detect your charset encoding but if it failed to do so, you
Expand Down
99 changes: 84 additions & 15 deletions src/screens/visualizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Visualizer::Visualizer()
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_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);
# endif // HAVE_FFTW3_H
}
Expand All @@ -108,7 +108,7 @@ void Visualizer::switchTo()
m_reset_output = true;
drawHeader();
# ifdef HAVE_FFTW3_H
GenLogspace();
GenFreqSpace();
m_bar_heights.reserve(w.getWidth());
# endif // HAVE_FFTW3_H
}
Expand All @@ -122,7 +122,7 @@ void Visualizer::resize()
hasToBeResized = 0;
InitVisualization();
# ifdef HAVE_FFTW3_H
GenLogspace();
GenFreqSpace();
m_bar_heights.reserve(w.getWidth());
# endif // HAVE_FFTW3_H
}
Expand Down Expand Up @@ -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);
w << NC::XY(left_half_width + x, top_half_height + y)
<< c
<< Config.visualizer_chars[1]
<< Config.visualizer_chars[0]
<< NC::FormattedColor::End<>(c);
}
}
Expand Down Expand Up @@ -450,7 +450,7 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
const size_t win_width = w.getWidth();

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;
for (size_t x = 0; x < win_width; ++x)
{
Expand All @@ -459,10 +459,10 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
// accumulate bins
size_t count = 0;
// 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
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];
++count;
Expand All @@ -476,8 +476,19 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
// average bins
bar_height /= count;

// log scale bar heights
bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE;
// apply scaling to bar heights
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
bar_height = bar_height > 0 ? bar_height * height : 0;
bar_height = bar_height > height ? height : bar_height;
Expand All @@ -499,7 +510,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
++h_idx;
} else {
// 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)
Expand Down Expand Up @@ -549,7 +565,7 @@ void Visualizer::DrawFrequencySpectrumStereo(const int16_t *buf_left, const int1
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 h_next = m_bar_heights[h_idx].second;
Expand Down Expand Up @@ -594,6 +610,33 @@ double Visualizer::Interpolate(size_t x, size_t h_idx)
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)
{
// Use Blackman window for low sidelobes and fast sidelobe rolloff
Expand Down Expand Up @@ -622,10 +665,36 @@ void Visualizer::GenLogspace()
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));
// Generate logspaced frequencies
m_dft_logspace.resize(win_width);
const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_logspace.size() - 1);
for (size_t i = left_bins; i < m_dft_logspace.size() + left_bins; ++i) {
m_dft_logspace[i - left_bins] = pow(10, i * log_scale);
m_dft_freqspace.resize(win_width);
const double log_scale = log10(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] = 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
Expand Down
7 changes: 5 additions & 2 deletions src/screens/visualizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ struct Visualizer: Screen<NC::Window>, Tabbable
void DrawFrequencySpectrumStereo(const int16_t *, const int16_t *, ssize_t, size_t);
void ApplyWindow(double *, const int16_t *, ssize_t);
void GenLogspace();
void GenLinspace();
void GenFreqSpace();
double Bin2Hz(size_t);
double Interpolate(size_t, size_t);
double InterpolateCubic(size_t, size_t);
double InterpolateLinear(size_t, size_t);
# endif // HAVE_FFTW3_H

void InitDataSource();
Expand Down Expand Up @@ -114,7 +117,7 @@ struct Visualizer: Screen<NC::Window>, Tabbable
const double GAIN;
const std::wstring SMOOTH_CHARS;
const std::wstring SMOOTH_CHARS_FLIPPED;
std::vector<double> m_dft_logspace;
std::vector<double> m_dft_freqspace;
std::vector<std::pair<size_t, double>> m_bar_heights;

std::vector<double> m_freq_magnitudes;
Expand Down
2 changes: 2 additions & 0 deletions src/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
lowerBoundCheck<double>(result, Config.visualizer_spectrum_hz_min+1);
return result;
});
p.add("visualizer_spectrum_log_scale_x", &visualizer_spectrum_log_scale_x, "yes", yes_no);
p.add("visualizer_spectrum_log_scale_y", &visualizer_spectrum_log_scale_y, "yes", yes_no);
p.add("visualizer_color", &visualizer_colors,
"blue, cyan, green, yellow, magenta, red", list_of<NC::FormattedColor>);
p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
Expand Down
2 changes: 2 additions & 0 deletions src/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ struct Configuration
double visualizer_spectrum_gain;
double visualizer_spectrum_hz_min;
double visualizer_spectrum_hz_max;
bool visualizer_spectrum_log_scale_x;
bool visualizer_spectrum_log_scale_y;

std::string pattern;

Expand Down