From 0e6306849c43734a1aa1f030445c87f3c3877d9c Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 9 Nov 2011 17:46:20 +0100 Subject: [PATCH] new feature: support for stereo visualization --- doc/config | 13 +++++++-- doc/ncmpcpp.1 | 5 +++- src/settings.cpp | 5 ++++ src/settings.h | 1 + src/visualizer.cpp | 71 ++++++++++++++++++++++++++++------------------ src/visualizer.h | 14 ++++----- 6 files changed, 68 insertions(+), 41 deletions(-) diff --git a/doc/config b/doc/config index 78b540fd..58ac479f 100644 --- a/doc/config +++ b/doc/config @@ -23,17 +23,24 @@ ## ## Note: In order to make music visualizer work you'll ## need to use mpd fifo output, whose format parameter -## has to be set to 44100:16:1. Example configuration: -## (it has to be put into mpd.conf) +## has to be set to 44100:16:1 for mono visualization +## or 44100:16:2 for stereo visualization. Example +## configuration (it has to be put into mpd.conf): ## ## audio_output { ## type "fifo" ## name "My FIFO" ## path "/tmp/mpd.fifo" -## format "44100:16:1" +## format "44100:16:2" ## } ## # +## +## If you set format to 44100:16:2, make it 'yes'. +## +# +#visualizer_in_stereo = "no" +# #visualizer_fifo_path = "" # ## diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index 03662057..850027d9 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -75,8 +75,11 @@ Default number of seconds to crossfade, if enabled by ncmpcpp. .B mpd_communication_mode = MODE If set to 'polling', ncmpcpp will constantly poll mpd for its status. If set to 'notifications', ncmppcp will make use of 'idle' command and wait for events. This is more efficient and responsive, but may cause some trouble with (fftw_malloc(sizeof(double)*Samples)); - itsOutput = static_cast(fftw_malloc(sizeof(fftw_complex)*FFTResults)); - itsPlan = fftw_plan_dft_r2c_1d(Samples, itsInput, itsOutput, FFTW_ESTIMATE); + itsFFTResults = itsSamples/2+1; + itsFreqsMagnitude = new unsigned[itsFFTResults]; + itsInput = static_cast(fftw_malloc(sizeof(double)*itsSamples)); + itsOutput = static_cast(fftw_malloc(sizeof(fftw_complex)*itsFFTResults)); + itsPlan = fftw_plan_dft_r2c_1d(itsSamples, itsInput, itsOutput, FFTW_ESTIMATE); # endif // HAVE_FFTW3_H FindOutputID(); @@ -106,8 +103,8 @@ void Visualizer::Update() if (itsFifo < 0) return; - // it supports only PCM in format 44100:16:1 - static int16_t buf[Samples]; + // PCM in format 44100:16:1 (for mono visualization) and 44100:16:2 (for stereo visualization) is supported + int16_t buf[itsSamples]; ssize_t data = read(itsFifo, buf, sizeof(buf)); if (data < 0) // no data available in fifo return; @@ -120,12 +117,30 @@ void Visualizer::Update() gettimeofday(&itsTimer, 0); } - w->Clear(); + void (Visualizer::*draw)(int16_t *, ssize_t, size_t, size_t); # ifdef HAVE_FFTW3_H - Config.visualizer_use_wave ? DrawSoundWave(buf, data) : DrawFrequencySpectrum(buf, data); -# else - DrawSoundWave(buf, data); + if (!Config.visualizer_use_wave) + draw = &Visualizer::DrawFrequencySpectrum; + else # endif // HAVE_FFTW3_H + draw = &Visualizer::DrawSoundWave; + + w->Clear(); + if (Config.visualizer_in_stereo) + { + ssize_t bytes_read = data/sizeof(int16_t); + int16_t buf_left[bytes_read/2], buf_right[bytes_read/2]; + for (ssize_t i = 0, j = 0; i < bytes_read; i += 2, ++j) + { + buf_left[j] = buf[i]; + buf_right[j] = buf[i+1]; + } + size_t half_height = MainHeight/2; + (this->*draw)(buf_left, data/2, 0, half_height); + (this->*draw)(buf_right, data/2, half_height+(draw == &Visualizer::DrawSoundWave ? 1 : 0), half_height+(draw != &Visualizer::DrawSoundWave ? 1 : 0)); + } + else + (this->*draw)(buf, data, 0, MainHeight); w->Refresh(); } @@ -137,10 +152,10 @@ void Visualizer::SpacePressed() # endif // HAVE_FFTW3_H } -void Visualizer::DrawSoundWave(int16_t *buf, ssize_t data) +void Visualizer::DrawSoundWave(int16_t *buf, ssize_t data, size_t y_offset, size_t height) { const int samples_per_col = data/sizeof(int16_t)/COLS; - const int half_height = MainHeight/2; + const int half_height = height/2; *w << fmtAltCharset; double prev_point_pos = 0; for (int i = 0; i < COLS; ++i) @@ -151,7 +166,7 @@ void Visualizer::DrawSoundWave(int16_t *buf, ssize_t data) point_pos /= samples_per_col; point_pos /= std::numeric_limits::max(); point_pos *= half_height; - *w << XY(i, half_height+point_pos) << '`'; + *w << XY(i, y_offset+half_height+point_pos) << '`'; if (i && abs(prev_point_pos-point_pos) > 2) { // if gap is too big. intermediate values are needed @@ -159,7 +174,7 @@ void Visualizer::DrawSoundWave(int16_t *buf, ssize_t data) const int breakpoint = std::max(prev_point_pos, point_pos); const int half = (prev_point_pos+point_pos)/2; for (int k = std::min(prev_point_pos, point_pos)+1; k < breakpoint; k += 2) - *w << XY(i-(k < half), half_height+k) << '`'; + *w << XY(i-(k < half), y_offset+half_height+k) << '`'; } prev_point_pos = point_pos; } @@ -167,27 +182,27 @@ void Visualizer::DrawSoundWave(int16_t *buf, ssize_t data) } #ifdef HAVE_FFTW3_H -void Visualizer::DrawFrequencySpectrum(int16_t *buf, ssize_t data) +void Visualizer::DrawFrequencySpectrum(int16_t *buf, ssize_t data, size_t y_offset, size_t height) { // zero old values - std::fill(buf+data/sizeof(int16_t), buf+Samples, 0); - for (unsigned i = 0; i < Samples; ++i) + std::fill(buf+data/sizeof(int16_t), buf+data/2, 0); + for (unsigned i = 0; i < data/2; ++i) itsInput[i] = buf[i]; fftw_execute(itsPlan); // count magnitude of each frequency and scale it to fit the screen - for (unsigned i = 0; i < FFTResults; ++i) - itsFreqsMagnitude[i] = sqrt(itsOutput[i][0]*itsOutput[i][0] + itsOutput[i][1]*itsOutput[i][1])/1e5*LINES/5; + for (unsigned i = 0; i < itsFFTResults; ++i) + itsFreqsMagnitude[i] = sqrt(itsOutput[i][0]*itsOutput[i][0] + itsOutput[i][1]*itsOutput[i][1])/1e5*height/5; - const int freqs_per_col = FFTResults/COLS /* cut bandwidth a little to achieve better look */ * 4/5; + const int freqs_per_col = itsFFTResults/COLS /* cut bandwidth a little to achieve better look */ * 4/5; for (int i = 0; i < COLS; ++i) { size_t bar_height = 0; for (int j = 0; j < freqs_per_col; ++j) bar_height += itsFreqsMagnitude[i*freqs_per_col+j]; - bar_height = std::min(bar_height/freqs_per_col, MainHeight); - mvwvline(w->Raw(), MainHeight-bar_height, i, 0, bar_height); + bar_height = std::min(bar_height/freqs_per_col, height); + mvwvline(w->Raw(), y_offset > 0 ? y_offset : height-bar_height, i, 0, bar_height); } } #endif // HAVE_FFTW3_H diff --git a/src/visualizer.h b/src/visualizer.h index 47ad2774..d2c19f3c 100644 --- a/src/visualizer.h +++ b/src/visualizer.h @@ -58,33 +58,29 @@ class Visualizer : public Screen void ResetFD(); void FindOutputID(); - static int WindowTimeout; + static const int WindowTimeout; protected: virtual void Init(); private: - void DrawSoundWave(int16_t *, ssize_t); + void DrawSoundWave(int16_t *, ssize_t, size_t, size_t); # ifdef HAVE_FFTW3_H - void DrawFrequencySpectrum(int16_t *, ssize_t); + void DrawFrequencySpectrum(int16_t *, ssize_t, size_t, size_t); # endif // HAVE_FFTW3_H int itsOutputID; timeval itsTimer; int itsFifo; + unsigned itsSamples; # ifdef HAVE_FFTW3_H + unsigned itsFFTResults; unsigned *itsFreqsMagnitude; double *itsInput; fftw_complex *itsOutput; fftw_plan itsPlan; # endif // HAVE_FFTW3_H - - static const unsigned Samples; -# ifdef HAVE_FFTW3_H - static const unsigned FFTResults; -# endif // HAVE_FFTW3_H - }; extern Visualizer *myVisualizer;