diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 02fa9848..232813d2 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -137,8 +137,7 @@ void Visualizer::update() } else if (Config.visualizer_type == VisualizerType::Ellipse) { - //Ellipse only works with stereo - draw = &Visualizer::DrawSoundWave; + draw = &Visualizer::DrawSoundEllipse; drawStereo = &Visualizer::DrawSoundEllipseStereo; } else @@ -207,49 +206,67 @@ void Visualizer::spacePressed() Statusbar::printf("Visualization type: %1%", Config.visualizer_type); } -void Visualizer::DrawSoundWaveStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) -{ - DrawSoundWave(buf_left, samples, 0, height); - DrawSoundWave(buf_right, samples, height, w.getHeight() - height); -} +/**********************************************************************/ -void Visualizer::DrawSoundWaveFillStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) +void Visualizer::DrawSoundWave(int16_t *buf, ssize_t samples, size_t y_offset, size_t height) { - DrawSoundWaveFill(buf_left, samples, 0, height); - DrawSoundWaveFill(buf_right, samples, height, w.getHeight() - height); -} + const size_t half_height = height/2; + const size_t base_y = y_offset+half_height; + const size_t win_width = w.getWidth(); + const int samples_per_column = samples/win_width; -// DrawSoundEllipseStereo: This visualizer only works in stereo. The colors form concentric -// rings originating from the center (width/2, height/2). For any given point, the width is -// scaled with the left channel and height is scaled with the right channel. For example, -// if a song is entirely in the right channel, then it would just be a vertical line. -// -// Since every font/terminal is different, the visualizer is never a perfect circle. This -// visualizer assume the font height is twice the length of the font's width. If the font -// is skinner or wider than this, instead of a circle it will be an ellipse. -void Visualizer::DrawSoundEllipseStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) -{ - const long width = w.getWidth()/2; + // too little samples + if (samples_per_column == 0) + return; - // Makes the radius of the color circle proportional to max of height or width. - // Divide by colors size so that there are multiple color rings instead of just a few. - const long scaledRadius = std::max(pow(width,2), pow(height,2))/pow(Config.visualizer_colors.size(),2); - for (size_t i = 0; i < samples; ++i) + auto draw_point = [&](size_t x, int32_t y) { + w << NC::XY(x, base_y+y) + << toColor(std::abs(y), half_height, false) + << Config.visualizer_chars[0] + << NC::Color::End; + }; + + int32_t point_y, prev_point_y = 0; + for (size_t x = 0; x < win_width; ++x) { - long x = width + ((double) buf_left[i] * 2 * ((double)width / 65536.0)); - long y = height + ((double) buf_right[i] * 2 * ((double)height / 65536.0)); + point_y = 0; + // calculate mean from the relevant points + for (int j = 0; j < samples_per_column; ++j) + point_y += buf[x*samples_per_column+j]; + point_y /= samples_per_column; + // normalize it to fit the screen + point_y *= height / 65536.0; - // The arguments to the toColor function roughly follow a circle equation where - // the center is not centered around (0,0). For example (x - w)^2 + (y-h)+2 = r^2 - // centers the circle around the point (w,h). Because fonts are not all the same - // size, this will not always generate a perfect circle. - w << toColor(pow((x - width)*1, 2) + pow((y - ((long)height)) * 2,2), scaledRadius) - << NC::XY(x, y) - << Config.visualizer_chars[1] - << NC::Color::End; + draw_point(x, point_y); + + // if the gap between two consecutive points is too big, + // intermediate values are needed for the wave to be watchable. + if (x > 0 && std::abs(prev_point_y-point_y) > 1) + { + const int32_t half = (prev_point_y+point_y)/2; + if (prev_point_y < point_y) + { + for (auto y = prev_point_y; y < point_y; ++y) + draw_point(x-(y < half), y); + } + else + { + for (auto y = prev_point_y; y > point_y; --y) + draw_point(x-(y > half), y); + } + } + prev_point_y = point_y; } } +void Visualizer::DrawSoundWaveStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) +{ + DrawSoundWave(buf_left, samples, 0, height); + DrawSoundWave(buf_right, samples, height, w.getHeight() - height); +} + +/**********************************************************************/ + // DrawSoundWaveFill: This visualizer is very similar to DrawSoundWave, but instead of // a single line the entire height is filled. In stereo mode, the top half of the screen // is dedicated to the right channel, the bottom the left channel. @@ -287,64 +304,53 @@ void Visualizer::DrawSoundWaveFill(int16_t *buf, ssize_t samples, size_t y_offse } } -void Visualizer::DrawSoundWave(int16_t *buf, ssize_t samples, size_t y_offset, size_t height) +void Visualizer::DrawSoundWaveFillStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) { - const size_t half_height = height/2; - const size_t base_y = y_offset+half_height; - const size_t win_width = w.getWidth(); - const int samples_per_column = samples/win_width; + DrawSoundWaveFill(buf_left, samples, 0, height); + DrawSoundWaveFill(buf_right, samples, height, w.getHeight() - height); +} - // too little samples - if (samples_per_column == 0) - return; +/**********************************************************************/ - auto draw_point = [&](size_t x, int32_t y) { - w << NC::XY(x, base_y+y) - << toColor(std::abs(y), half_height, false) - << Config.visualizer_chars[0] - << NC::Color::End; - }; +void Visualizer::DrawSoundEllipse(int16_t *, ssize_t, size_t, size_t) +{ - int32_t point_y, prev_point_y = 0; - for (size_t x = 0; x < win_width; ++x) - { - point_y = 0; - // calculate mean from the relevant points - for (int j = 0; j < samples_per_column; ++j) - point_y += buf[x*samples_per_column+j]; - point_y /= samples_per_column; - // normalize it to fit the screen - point_y *= height / 65536.0; +} - draw_point(x, point_y); +// DrawSoundEllipseStereo: This visualizer only works in stereo. The colors form concentric +// rings originating from the center (width/2, height/2). For any given point, the width is +// scaled with the left channel and height is scaled with the right channel. For example, +// if a song is entirely in the right channel, then it would just be a vertical line. +// +// Since every font/terminal is different, the visualizer is never a perfect circle. This +// visualizer assume the font height is twice the length of the font's width. If the font +// is skinner or wider than this, instead of a circle it will be an ellipse. +void Visualizer::DrawSoundEllipseStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) +{ + const long width = w.getWidth()/2; - // if the gap between two consecutive points is too big, - // intermediate values are needed for the wave to be watchable. - if (x > 0 && std::abs(prev_point_y-point_y) > 1) - { - const int32_t half = (prev_point_y+point_y)/2; - if (prev_point_y < point_y) - { - for (auto y = prev_point_y; y < point_y; ++y) - draw_point(x-(y < half), y); - } - else - { - for (auto y = prev_point_y; y > point_y; --y) - draw_point(x-(y > half), y); - } - } - prev_point_y = point_y; + // Makes the radius of the color circle proportional to max of height or width. + // Divide by colors size so that there are multiple color rings instead of just a few. + const long scaledRadius = std::max(pow(width,2), pow(height,2))/pow(Config.visualizer_colors.size(),2); + for (ssize_t i = 0; i < samples; ++i) + { + long x = width + ((double) buf_left[i] * 2 * ((double)width / 65536.0)); + long y = height + ((double) buf_right[i] * 2 * ((double)height / 65536.0)); + + // The arguments to the toColor function roughly follow a circle equation where + // the center is not centered around (0,0). For example (x - w)^2 + (y-h)+2 = r^2 + // centers the circle around the point (w,h). Because fonts are not all the same + // size, this will not always generate a perfect circle. + w << toColor(pow((x - width)*1, 2) + pow((y - ((long)height)) * 2,2), scaledRadius) + << NC::XY(x, y) + << Config.visualizer_chars[1] + << NC::Color::End; } } -#ifdef HAVE_FFTW3_H -void Visualizer::DrawFrequencySpectrumStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) -{ - DrawFrequencySpectrum(buf_left, samples, 0, height); - DrawFrequencySpectrum(buf_right, samples, height, w.getHeight() - height); -} +/**********************************************************************/ +#ifdef HAVE_FFTW3_H void Visualizer::DrawFrequencySpectrum(int16_t *buf, ssize_t samples, size_t y_offset, size_t height) { // if right channel is drawn, bars descend from the top to the bottom @@ -388,8 +394,16 @@ void Visualizer::DrawFrequencySpectrum(int16_t *buf, ssize_t samples, size_t y_o } } } + +void Visualizer::DrawFrequencySpectrumStereo(int16_t *buf_left, int16_t *buf_right, ssize_t samples, size_t height) +{ + DrawFrequencySpectrum(buf_left, samples, 0, height); + DrawFrequencySpectrum(buf_right, samples, height, w.getHeight() - height); +} #endif // HAVE_FFTW3_H +/**********************************************************************/ + void Visualizer::SetFD() { if (m_fifo < 0 && (m_fifo = open(Config.visualizer_fifo_path.c_str(), O_RDONLY | O_NONBLOCK)) < 0) diff --git a/src/visualizer.h b/src/visualizer.h index 830d7623..f6e028df 100644 --- a/src/visualizer.h +++ b/src/visualizer.h @@ -65,9 +65,10 @@ protected: private: void DrawSoundWave(int16_t *, ssize_t, size_t, size_t); - void DrawSoundWaveFill(int16_t *, ssize_t, size_t, size_t); void DrawSoundWaveStereo(int16_t *, int16_t *, ssize_t, size_t); + void DrawSoundWaveFill(int16_t *, ssize_t, size_t, size_t); void DrawSoundWaveFillStereo(int16_t *, int16_t *, ssize_t, size_t); + void DrawSoundEllipse(int16_t *, ssize_t, size_t, size_t); void DrawSoundEllipseStereo(int16_t *, int16_t *, ssize_t, size_t); # ifdef HAVE_FFTW3_H void DrawFrequencySpectrum(int16_t *, ssize_t, size_t, size_t);