|
|
|
|
@ -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) |
|
|
|
|
|