@ -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_log space . reserve ( 500 ) ;
m_dft_freq space . reserve ( 500 ) ;
m_bar_heights . reserve ( 100 ) ;
# endif // HAVE_FFTW3_H
}
@ -108,7 +108,7 @@ void Visualizer::switchTo()
m_reset_output = true ;
drawHeader ( ) ;
# ifdef HAVE_FFTW3_H
GenLogs pace ( ) ;
GenFreqS pace ( ) ;
m_bar_heights . reserve ( w . getWidth ( ) ) ;
# endif // HAVE_FFTW3_H
}
@ -122,7 +122,7 @@ void Visualizer::resize()
hasToBeResized = 0 ;
InitVisualization ( ) ;
# ifdef HAVE_FFTW3_H
GenLogs pace ( ) ;
GenFreqS pace ( ) ;
m_bar_heights . reserve ( w . getWidth ( ) ) ;
# 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 ) ;
w < < NC : : XY ( left_half_width + x , top_half_height + y )
< < c
< < Config . visualizer_chars [ 1 ]
< < Config . visualizer_chars [ 0 ]
< < 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 ( ) ;
size_t cur_bin = 0 ;
while ( cur_bin < m_fftw_results & & Bin2Hz ( cur_bin ) < m_dft_log space [ 0 ] )
while ( cur_bin < m_fftw_results & & Bin2Hz ( cur_bin ) < m_dft_freq space [ 0 ] )
+ + cur_bin ;
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
size_t count = 0 ;
// check right bound
while ( cur_bin < m_fftw_results & & Bin2Hz ( cur_bin ) < m_dft_log space [ x ] )
while ( cur_bin < m_fftw_results & & Bin2Hz ( cur_bin ) < m_dft_freq space [ x ] )
{
// check left bound if not first index
if ( x = = 0 | | Bin2Hz ( cur_bin ) > = m_dft_log space [ x - 1 ] )
if ( x = = 0 | | Bin2Hz ( cur_bin ) > = m_dft_freq space [ x - 1 ] )
{
bar_height + = m_freq_magnitudes [ cur_bin ] ;
+ + count ;
@ -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 ;
@ -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 )
@ -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 ;
@ -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
@ -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