visualizer: add filled wave visualizer

This commit is contained in:
Darby Payne
2014-10-20 23:09:55 -07:00
committed by Andrzej Rybczak
parent 9bab03e4b2
commit 1f2daaa08c
7 changed files with 119 additions and 46 deletions

View File

@@ -39,4 +39,6 @@ enum class Design { Classic, Alternative };
std::ostream &operator<<(std::ostream &os, Design ui); std::ostream &operator<<(std::ostream &os, Design ui);
std::istream &operator>>(std::istream &is, Design &ui); std::istream &operator>>(std::istream &is, Design &ui);
enum class VisualizerType { Wave, WaveFilled, Spectrum };
#endif // NCMPCPP_ENUMS_H #endif // NCMPCPP_ENUMS_H

View File

@@ -231,13 +231,8 @@ bool Configuration::read(const std::string &config_path)
return boost::posix_time::seconds(v); return boost::posix_time::seconds(v);
})); }));
p.add("visualizer_type", option_parser::worker([this](std::string &&v) { p.add("visualizer_type", option_parser::worker([this](std::string &&v) {
if (v == "wave") visualizer_type = stringToVisualizerType( v );
visualizer_use_wave = true; }, defaults_to(visualizer_type, VisualizerType::Wave)
else if (v == "spectrum")
visualizer_use_wave = false;
else
throw std::runtime_error("invalid argument: " + v);
}, defaults_to(visualizer_use_wave, true)
)); ));
p.add("visualizer_look", assign_default<std::string>( p.add("visualizer_look", assign_default<std::string>(
visualizer_chars, "●▮", [](std::string &&s) { visualizer_chars, "●▮", [](std::string &&s) {
@@ -649,3 +644,5 @@ bool Configuration::read(const std::string &config_path)
std::ifstream f(config_path); std::ifstream f(config_path);
return p.run(f); return p.run(f);
} }
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */

View File

@@ -34,7 +34,7 @@
struct Column struct Column
{ {
Column() : stretch_limit(-1), right_alignment(0), display_empty_tag(1) { } Column() : stretch_limit(-1), right_alignment(0), display_empty_tag(1) { }
std::wstring name; std::wstring name;
std::string type; std::string type;
int width; int width;
@@ -50,12 +50,12 @@ struct Configuration
Configuration() Configuration()
: playlist_disable_highlight_delay(0), visualizer_sync_interval(0) : playlist_disable_highlight_delay(0), visualizer_sync_interval(0)
{ } { }
bool read(const std::string &config_path); bool read(const std::string &config_path);
std::string ncmpcpp_directory; std::string ncmpcpp_directory;
std::string lyrics_directory; std::string lyrics_directory;
std::string mpd_music_dir; std::string mpd_music_dir;
std::string visualizer_fifo_path; std::string visualizer_fifo_path;
std::string visualizer_output_name; std::string visualizer_output_name;
@@ -78,7 +78,7 @@ struct Configuration
std::string lastfm_preferred_language; std::string lastfm_preferred_language;
std::wstring progressbar; std::wstring progressbar;
std::wstring visualizer_chars; std::wstring visualizer_chars;
std::string pattern; std::string pattern;
std::vector<Column> columns; std::vector<Column> columns;
@@ -94,7 +94,7 @@ struct Configuration
NC::Buffer now_playing_prefix; NC::Buffer now_playing_prefix;
NC::Buffer now_playing_suffix; NC::Buffer now_playing_suffix;
NC::Buffer modified_item_prefix; NC::Buffer modified_item_prefix;
NC::Color color1; NC::Color color1;
NC::Color color2; NC::Color color2;
NC::Color empty_tags_color; NC::Color empty_tags_color;
@@ -109,17 +109,19 @@ struct Configuration
NC::Color statusbar_color; NC::Color statusbar_color;
NC::Color alternative_ui_separator_color; NC::Color alternative_ui_separator_color;
NC::Color active_column_color; NC::Color active_column_color;
std::vector<NC::Color> visualizer_colors; std::vector<NC::Color> visualizer_colors;
VisualizerType visualizer_type;
NC::Border window_border; NC::Border window_border;
NC::Border active_window_border; NC::Border active_window_border;
Design design; Design design;
SpaceAddMode space_add_mode; SpaceAddMode space_add_mode;
mpd_tag_type media_lib_primary_tag; mpd_tag_type media_lib_primary_tag;
bool colors_enabled; bool colors_enabled;
bool playlist_show_remaining_time; bool playlist_show_remaining_time;
bool playlist_shorten_total_times; bool playlist_shorten_total_times;
@@ -151,7 +153,6 @@ struct Configuration
bool ask_before_clearing_playlists; bool ask_before_clearing_playlists;
bool mouse_support; bool mouse_support;
bool mouse_list_scroll_whole_page; bool mouse_list_scroll_whole_page;
bool visualizer_use_wave;
bool visualizer_in_stereo; bool visualizer_in_stereo;
bool data_fetching_delay; bool data_fetching_delay;
bool media_library_sort_by_mtime; bool media_library_sort_by_mtime;
@@ -162,7 +163,7 @@ struct Configuration
bool ask_for_locked_screen_width_part; bool ask_for_locked_screen_width_part;
bool allow_for_physical_item_deletion; bool allow_for_physical_item_deletion;
bool progressbar_boldness; bool progressbar_boldness;
unsigned mpd_connection_timeout; unsigned mpd_connection_timeout;
unsigned crossfade_time; unsigned crossfade_time;
unsigned seek_time; unsigned seek_time;
@@ -173,21 +174,21 @@ struct Configuration
unsigned search_engine_default_search_mode; unsigned search_engine_default_search_mode;
boost::regex::flag_type regex_type; boost::regex::flag_type regex_type;
boost::posix_time::seconds playlist_disable_highlight_delay; boost::posix_time::seconds playlist_disable_highlight_delay;
boost::posix_time::seconds visualizer_sync_interval; boost::posix_time::seconds visualizer_sync_interval;
double visualizer_sample_multiplier; double visualizer_sample_multiplier;
double locked_screen_width_part; double locked_screen_width_part;
size_t selected_item_prefix_length; size_t selected_item_prefix_length;
size_t selected_item_suffix_length; size_t selected_item_suffix_length;
size_t now_playing_prefix_length; size_t now_playing_prefix_length;
size_t now_playing_suffix_length; size_t now_playing_suffix_length;
ScreenType startup_screen_type; ScreenType startup_screen_type;
std::list<ScreenType> screen_sequence; std::list<ScreenType> screen_sequence;
SortMode browser_sort_mode; SortMode browser_sort_mode;
}; };

View File

@@ -43,6 +43,18 @@ NC::Color stringToColor(const std::string &color)
return result; return result;
} }
VisualizerType stringToVisualizerType(const std::string &visualizerType)
{
VisualizerType result = VisualizerType::Wave;
if (visualizerType == "wave")
result = VisualizerType::Wave;
else if (visualizerType == "spectrum")
result = VisualizerType::Spectrum;
else if (visualizerType == "wave_filled")
result = VisualizerType::WaveFilled;
return result;
}
NC::Border stringToBorder(const std::string &border) NC::Border stringToBorder(const std::string &border)
{ {
NC::Border result = NC::Border::None; NC::Border result = NC::Border::None;

View File

@@ -24,8 +24,10 @@
#include "mpdpp.h" #include "mpdpp.h"
#include "mutable_song.h" #include "mutable_song.h"
#include "window.h" #include "window.h"
#include "enums.h"
NC::Color stringToColor(const std::string &color); NC::Color stringToColor(const std::string &color);
VisualizerType stringToVisualizerType(const std::string &visualizerType);
NC::Border stringToBorder(const std::string &border); NC::Border stringToBorder(const std::string &border);
std::string tagTypeToString(mpd_tag_type tag); std::string tagTypeToString(mpd_tag_type tag);

View File

@@ -37,6 +37,7 @@
#include "title.h" #include "title.h"
#include "screen_switcher.h" #include "screen_switcher.h"
#include "status.h" #include "status.h"
#include "enums.h"
using Global::MainStartY; using Global::MainStartY;
using Global::MainHeight; using Global::MainHeight;
@@ -92,13 +93,13 @@ void Visualizer::update()
{ {
if (m_fifo < 0) if (m_fifo < 0)
return; return;
// PCM in format 44100:16:1 (for mono visualization) and 44100:16:2 (for stereo visualization) is supported // PCM in format 44100:16:1 (for mono visualization) and 44100:16:2 (for stereo visualization) is supported
int16_t buf[m_samples]; int16_t buf[m_samples];
ssize_t data = read(m_fifo, buf, sizeof(buf)); ssize_t data = read(m_fifo, buf, sizeof(buf));
if (data < 0) // no data available in fifo if (data < 0) // no data available in fifo
return; return;
if (m_output_id != -1 && Global::Timer - m_timer > Config.visualizer_sync_interval) if (m_output_id != -1 && Global::Timer - m_timer > Config.visualizer_sync_interval)
{ {
Mpd.DisableOutput(m_output_id); Mpd.DisableOutput(m_output_id);
@@ -106,13 +107,16 @@ void Visualizer::update()
Mpd.EnableOutput(m_output_id); Mpd.EnableOutput(m_output_id);
m_timer = Global::Timer; m_timer = Global::Timer;
} }
void (Visualizer::*draw)(int16_t *, ssize_t, size_t, size_t); void (Visualizer::*draw)(int16_t *, ssize_t, size_t, size_t);
# ifdef HAVE_FFTW3_H # ifdef HAVE_FFTW3_H
if (!Config.visualizer_use_wave) if (Config.visualizer_type == VisualizerType::Spectrum)
draw = &Visualizer::DrawFrequencySpectrum; draw = &Visualizer::DrawFrequencySpectrum;
else else
# endif // HAVE_FFTW3_H # endif // HAVE_FFTW3_H
if (Config.visualizer_type == VisualizerType::WaveFilled)
draw = &Visualizer::DrawSoundWaveFill;
else
draw = &Visualizer::DrawSoundWave; draw = &Visualizer::DrawSoundWave;
const ssize_t samples_read = data/sizeof(int16_t); const ssize_t samples_read = data/sizeof(int16_t);
@@ -137,7 +141,7 @@ void Visualizer::update()
} }
size_t half_height = MainHeight/2; size_t half_height = MainHeight/2;
(this->*draw)(buf_left, samples_read/2, 0, half_height); (this->*draw)(buf_left, samples_read/2, 0, half_height);
(this->*draw)(buf_right, samples_read/2, half_height+(draw == &Visualizer::DrawSoundWave ? 1 : 0), half_height+(draw != &Visualizer::DrawSoundWave ? 1 : 0)); (this->*draw)(buf_right, samples_read/2, half_height+(draw == &Visualizer::DrawFrequencySpectrum ? 0 : 1), half_height+(draw != &Visualizer::DrawFrequencySpectrum ? 0 : 1));
} }
else else
(this->*draw)(buf, samples_read, 0, MainHeight); (this->*draw)(buf, samples_read, 0, MainHeight);
@@ -154,12 +158,63 @@ int Visualizer::windowTimeout()
void Visualizer::spacePressed() void Visualizer::spacePressed()
{ {
std::string visualizerName;
if (Config.visualizer_type == VisualizerType::Wave)
{
Config.visualizer_type = VisualizerType::WaveFilled;
visualizerName = "sound wave filled";
}
# ifdef HAVE_FFTW3_H # ifdef HAVE_FFTW3_H
Config.visualizer_use_wave = !Config.visualizer_use_wave; else if (Config.visualizer_type == VisualizerType::WaveFilled)
Statusbar::printf("Visualization type: %1%", {
Config.visualizer_use_wave ? "sound wave" : "frequency spectrum" Config.visualizer_type = VisualizerType::Spectrum;
); visualizerName = "frequency spectrum";
}
# endif // HAVE_FFTW3_H # endif // HAVE_FFTW3_H
else
{
Config.visualizer_type = VisualizerType::Wave;
visualizerName = "sound wave";
}
Statusbar::printf("Visualization type: %1%", visualizerName.c_str());
}
NC::Color Visualizer::toColor( int number, int max )
{
const int colorMapSize = Config.visualizer_colors.size();
const int normalizedNumber = ( ( number * colorMapSize ) / max ) % colorMapSize;
return Config.visualizer_colors[normalizedNumber];
}
void Visualizer::DrawSoundWaveFill(int16_t *buf, ssize_t samples, size_t y_offset, size_t height)
{
const int samples_per_col = samples/w.getWidth();
const int half_height = height/2;
double prev_point_pos = 0;
const size_t win_width = w.getWidth();
const bool left = y_offset > 0;
int x = 0;
for (size_t i = 0; i < win_width; ++i)
{
double point_pos = 0;
for (int j = 0; j < samples_per_col; ++j)
point_pos += buf[i*samples_per_col+j];
point_pos /= samples_per_col;
point_pos /= std::numeric_limits<int16_t>::max();
point_pos *= half_height;
for (int k = 0; k < point_pos * 2; k += 1)
{
x = left ? height + k : height - k;
if ( x > 0 && x < w.getHeight() && (i-(k < half_height + point_pos)) > 0 && (i-(k < half_height + point_pos)) < w.getWidth() )
{
w << toColor( k, height )
<< NC::XY(i-(k < half_height + point_pos), x)
<< Config.visualizer_chars[1]
<< NC::Color::End;
}
}
}
} }
void Visualizer::DrawSoundWave(int16_t *buf, ssize_t samples, size_t y_offset, size_t height) void Visualizer::DrawSoundWave(int16_t *buf, ssize_t samples, size_t y_offset, size_t height)
@@ -183,7 +238,7 @@ void Visualizer::DrawSoundWave(int16_t *buf, ssize_t samples, size_t y_offset, s
Config.visualizer_colors.size()), Config.visualizer_colors.size() - 1)] Config.visualizer_colors.size()), Config.visualizer_colors.size() - 1)]
<< Config.visualizer_chars[0] << Config.visualizer_chars[0]
<< NC::Color::End; << NC::Color::End;
if (i && abs(prev_point_pos-point_pos) > 2) if (i && abs(prev_point_pos-point_pos) > 2)
{ {
// if gap is too big. intermediate values are needed // if gap is too big. intermediate values are needed
@@ -211,13 +266,13 @@ void Visualizer::DrawFrequencySpectrum(int16_t *buf, ssize_t samples, size_t y_o
else else
m_fftw_input[i] = 0; m_fftw_input[i] = 0;
} }
fftw_execute(m_fftw_plan); fftw_execute(m_fftw_plan);
// count magnitude of each frequency and scale it to fit the screen // count magnitude of each frequency and scale it to fit the screen
for (unsigned i = 0; i < m_fftw_results; ++i) for (unsigned i = 0; i < m_fftw_results; ++i)
m_freq_magnitudes[i] = sqrt(m_fftw_output[i][0]*m_fftw_output[i][0] + m_fftw_output[i][1]*m_fftw_output[i][1])/2e4*height; m_freq_magnitudes[i] = sqrt(m_fftw_output[i][0]*m_fftw_output[i][0] + m_fftw_output[i][1]*m_fftw_output[i][1])/2e4*height;
const size_t win_width = w.getWidth(); const size_t win_width = w.getWidth();
// cut bandwidth a little to achieve better look // cut bandwidth a little to achieve better look
const int freqs_per_col = m_fftw_results/win_width * 7/10; const int freqs_per_col = m_fftw_results/win_width * 7/10;
@@ -282,3 +337,4 @@ void Visualizer::FindOutputID()
#endif // ENABLE_VISUALIZER #endif // ENABLE_VISUALIZER
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */

View File

@@ -37,41 +37,43 @@
struct Visualizer: Screen<NC::Window>, Tabbable struct Visualizer: Screen<NC::Window>, Tabbable
{ {
Visualizer(); Visualizer();
virtual void switchTo() OVERRIDE; virtual void switchTo() OVERRIDE;
virtual void resize() OVERRIDE; virtual void resize() OVERRIDE;
virtual std::wstring title() OVERRIDE; virtual std::wstring title() OVERRIDE;
virtual ScreenType type() OVERRIDE { return ScreenType::Visualizer; } virtual ScreenType type() OVERRIDE { return ScreenType::Visualizer; }
virtual void update() OVERRIDE; virtual void update() OVERRIDE;
virtual void scroll(NC::Scroll) OVERRIDE { } virtual void scroll(NC::Scroll) OVERRIDE { }
virtual int windowTimeout() OVERRIDE; virtual int windowTimeout() OVERRIDE;
virtual void enterPressed() OVERRIDE { } virtual void enterPressed() OVERRIDE { }
virtual void spacePressed() OVERRIDE; virtual void spacePressed() OVERRIDE;
virtual void mouseButtonPressed(MEVENT) OVERRIDE { } virtual void mouseButtonPressed(MEVENT) OVERRIDE { }
virtual bool isMergable() OVERRIDE { return true; } virtual bool isMergable() OVERRIDE { return true; }
// private members // private members
void SetFD(); void SetFD();
void ResetFD(); void ResetFD();
void FindOutputID(); void FindOutputID();
protected: protected:
virtual bool isLockable() OVERRIDE { return true; } virtual bool isLockable() OVERRIDE { return true; }
private: private:
NC::Color toColor(int, int);
void DrawSoundWave(int16_t *, ssize_t, size_t, size_t); void DrawSoundWave(int16_t *, ssize_t, size_t, size_t);
void DrawSoundWaveFill(int16_t *, ssize_t, size_t, size_t);
# ifdef HAVE_FFTW3_H # ifdef HAVE_FFTW3_H
void DrawFrequencySpectrum(int16_t *, ssize_t, size_t, size_t); void DrawFrequencySpectrum(int16_t *, ssize_t, size_t, size_t);
# endif // HAVE_FFTW3_H # endif // HAVE_FFTW3_H
int m_output_id; int m_output_id;
boost::posix_time::ptime m_timer; boost::posix_time::ptime m_timer;
int m_fifo; int m_fifo;
unsigned m_samples; unsigned m_samples;
# ifdef HAVE_FFTW3_H # ifdef HAVE_FFTW3_H
@@ -89,3 +91,4 @@ extern Visualizer *myVisualizer;
#endif // NCMPCPP_VISUALIZER_H #endif // NCMPCPP_VISUALIZER_H
/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */