From 1f2daaa08cbfce8d0a0cec995d7dad2f55987a67 Mon Sep 17 00:00:00 2001 From: Darby Payne Date: Mon, 20 Oct 2014 23:09:55 -0700 Subject: [PATCH] visualizer: add filled wave visualizer --- src/enums.h | 2 + src/settings.cpp | 11 ++--- src/settings.h | 33 ++++++------- src/utility/type_conversions.cpp | 12 +++++ src/utility/type_conversions.h | 2 + src/visualizer.cpp | 82 +++++++++++++++++++++++++++----- src/visualizer.h | 23 +++++---- 7 files changed, 119 insertions(+), 46 deletions(-) diff --git a/src/enums.h b/src/enums.h index 11dd13c9..ecbf0587 100644 --- a/src/enums.h +++ b/src/enums.h @@ -39,4 +39,6 @@ enum class Design { Classic, Alternative }; std::ostream &operator<<(std::ostream &os, Design ui); std::istream &operator>>(std::istream &is, Design &ui); +enum class VisualizerType { Wave, WaveFilled, Spectrum }; + #endif // NCMPCPP_ENUMS_H diff --git a/src/settings.cpp b/src/settings.cpp index f13f8993..bd805ef4 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -231,13 +231,8 @@ bool Configuration::read(const std::string &config_path) return boost::posix_time::seconds(v); })); p.add("visualizer_type", option_parser::worker([this](std::string &&v) { - if (v == "wave") - visualizer_use_wave = true; - else if (v == "spectrum") - visualizer_use_wave = false; - else - throw std::runtime_error("invalid argument: " + v); - }, defaults_to(visualizer_use_wave, true) + visualizer_type = stringToVisualizerType( v ); + }, defaults_to(visualizer_type, VisualizerType::Wave) )); p.add("visualizer_look", assign_default( visualizer_chars, "●▮", [](std::string &&s) { @@ -649,3 +644,5 @@ bool Configuration::read(const std::string &config_path) std::ifstream f(config_path); return p.run(f); } + +/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ diff --git a/src/settings.h b/src/settings.h index 552e59dc..8ca8f894 100644 --- a/src/settings.h +++ b/src/settings.h @@ -34,7 +34,7 @@ struct Column { Column() : stretch_limit(-1), right_alignment(0), display_empty_tag(1) { } - + std::wstring name; std::string type; int width; @@ -50,12 +50,12 @@ struct Configuration Configuration() : playlist_disable_highlight_delay(0), visualizer_sync_interval(0) { } - + bool read(const std::string &config_path); - + std::string ncmpcpp_directory; std::string lyrics_directory; - + std::string mpd_music_dir; std::string visualizer_fifo_path; std::string visualizer_output_name; @@ -78,7 +78,7 @@ struct Configuration std::string lastfm_preferred_language; std::wstring progressbar; std::wstring visualizer_chars; - + std::string pattern; std::vector columns; @@ -94,7 +94,7 @@ struct Configuration NC::Buffer now_playing_prefix; NC::Buffer now_playing_suffix; NC::Buffer modified_item_prefix; - + NC::Color color1; NC::Color color2; NC::Color empty_tags_color; @@ -109,17 +109,19 @@ struct Configuration NC::Color statusbar_color; NC::Color alternative_ui_separator_color; NC::Color active_column_color; + std::vector visualizer_colors; - + VisualizerType visualizer_type; + NC::Border window_border; NC::Border active_window_border; - + Design design; SpaceAddMode space_add_mode; mpd_tag_type media_lib_primary_tag; - + bool colors_enabled; bool playlist_show_remaining_time; bool playlist_shorten_total_times; @@ -151,7 +153,6 @@ struct Configuration bool ask_before_clearing_playlists; bool mouse_support; bool mouse_list_scroll_whole_page; - bool visualizer_use_wave; bool visualizer_in_stereo; bool data_fetching_delay; bool media_library_sort_by_mtime; @@ -162,7 +163,7 @@ struct Configuration bool ask_for_locked_screen_width_part; bool allow_for_physical_item_deletion; bool progressbar_boldness; - + unsigned mpd_connection_timeout; unsigned crossfade_time; unsigned seek_time; @@ -173,21 +174,21 @@ struct Configuration unsigned search_engine_default_search_mode; boost::regex::flag_type regex_type; - + boost::posix_time::seconds playlist_disable_highlight_delay; boost::posix_time::seconds visualizer_sync_interval; - + double visualizer_sample_multiplier; double locked_screen_width_part; - + size_t selected_item_prefix_length; size_t selected_item_suffix_length; size_t now_playing_prefix_length; size_t now_playing_suffix_length; - + ScreenType startup_screen_type; std::list screen_sequence; - + SortMode browser_sort_mode; }; diff --git a/src/utility/type_conversions.cpp b/src/utility/type_conversions.cpp index 398bab7e..44217381 100644 --- a/src/utility/type_conversions.cpp +++ b/src/utility/type_conversions.cpp @@ -43,6 +43,18 @@ NC::Color stringToColor(const std::string &color) 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 result = NC::Border::None; diff --git a/src/utility/type_conversions.h b/src/utility/type_conversions.h index 765a80d9..e9a580d7 100644 --- a/src/utility/type_conversions.h +++ b/src/utility/type_conversions.h @@ -24,8 +24,10 @@ #include "mpdpp.h" #include "mutable_song.h" #include "window.h" +#include "enums.h" NC::Color stringToColor(const std::string &color); +VisualizerType stringToVisualizerType(const std::string &visualizerType); NC::Border stringToBorder(const std::string &border); std::string tagTypeToString(mpd_tag_type tag); diff --git a/src/visualizer.cpp b/src/visualizer.cpp index 083643c2..3d98fc30 100644 --- a/src/visualizer.cpp +++ b/src/visualizer.cpp @@ -37,6 +37,7 @@ #include "title.h" #include "screen_switcher.h" #include "status.h" +#include "enums.h" using Global::MainStartY; using Global::MainHeight; @@ -92,13 +93,13 @@ void Visualizer::update() { if (m_fifo < 0) return; - + // PCM in format 44100:16:1 (for mono visualization) and 44100:16:2 (for stereo visualization) is supported int16_t buf[m_samples]; ssize_t data = read(m_fifo, buf, sizeof(buf)); if (data < 0) // no data available in fifo return; - + if (m_output_id != -1 && Global::Timer - m_timer > Config.visualizer_sync_interval) { Mpd.DisableOutput(m_output_id); @@ -106,13 +107,16 @@ void Visualizer::update() Mpd.EnableOutput(m_output_id); m_timer = Global::Timer; } - + void (Visualizer::*draw)(int16_t *, ssize_t, size_t, size_t); # ifdef HAVE_FFTW3_H - if (!Config.visualizer_use_wave) + if (Config.visualizer_type == VisualizerType::Spectrum) draw = &Visualizer::DrawFrequencySpectrum; else # endif // HAVE_FFTW3_H + if (Config.visualizer_type == VisualizerType::WaveFilled) + draw = &Visualizer::DrawSoundWaveFill; + else draw = &Visualizer::DrawSoundWave; const ssize_t samples_read = data/sizeof(int16_t); @@ -137,7 +141,7 @@ void Visualizer::update() } size_t half_height = MainHeight/2; (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 (this->*draw)(buf, samples_read, 0, MainHeight); @@ -154,12 +158,63 @@ int Visualizer::windowTimeout() 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 - Config.visualizer_use_wave = !Config.visualizer_use_wave; - Statusbar::printf("Visualization type: %1%", - Config.visualizer_use_wave ? "sound wave" : "frequency spectrum" - ); + else if (Config.visualizer_type == VisualizerType::WaveFilled) + { + Config.visualizer_type = VisualizerType::Spectrum; + visualizerName = "frequency spectrum"; + } # 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::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) @@ -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_chars[0] << NC::Color::End; - + if (i && abs(prev_point_pos-point_pos) > 2) { // 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 m_fftw_input[i] = 0; } - + fftw_execute(m_fftw_plan); - + // count magnitude of each frequency and scale it to fit the screen 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; - + const size_t win_width = w.getWidth(); // cut bandwidth a little to achieve better look const int freqs_per_col = m_fftw_results/win_width * 7/10; @@ -282,3 +337,4 @@ void Visualizer::FindOutputID() #endif // ENABLE_VISUALIZER +/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */ diff --git a/src/visualizer.h b/src/visualizer.h index f050fad7..51a5d728 100644 --- a/src/visualizer.h +++ b/src/visualizer.h @@ -37,41 +37,43 @@ struct Visualizer: Screen, Tabbable { Visualizer(); - + virtual void switchTo() OVERRIDE; virtual void resize() OVERRIDE; - + virtual std::wstring title() OVERRIDE; virtual ScreenType type() OVERRIDE { return ScreenType::Visualizer; } - + virtual void update() OVERRIDE; virtual void scroll(NC::Scroll) OVERRIDE { } virtual int windowTimeout() OVERRIDE; - + virtual void enterPressed() OVERRIDE { } virtual void spacePressed() OVERRIDE; virtual void mouseButtonPressed(MEVENT) OVERRIDE { } - + virtual bool isMergable() OVERRIDE { return true; } - + // private members void SetFD(); void ResetFD(); void FindOutputID(); - + protected: virtual bool isLockable() OVERRIDE { return true; } - + private: + NC::Color toColor(int, int); void DrawSoundWave(int16_t *, ssize_t, size_t, size_t); + void DrawSoundWaveFill(int16_t *, ssize_t, size_t, size_t); # ifdef HAVE_FFTW3_H void DrawFrequencySpectrum(int16_t *, ssize_t, size_t, size_t); # endif // HAVE_FFTW3_H - + int m_output_id; boost::posix_time::ptime m_timer; - + int m_fifo; unsigned m_samples; # ifdef HAVE_FFTW3_H @@ -89,3 +91,4 @@ extern Visualizer *myVisualizer; #endif // NCMPCPP_VISUALIZER_H +/* vim: set tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab : */