new feature: support for stereo visualization
This commit is contained in:
13
doc/config
13
doc/config
@@ -23,17 +23,24 @@
|
|||||||
##
|
##
|
||||||
## Note: In order to make music visualizer work you'll
|
## Note: In order to make music visualizer work you'll
|
||||||
## need to use mpd fifo output, whose format parameter
|
## need to use mpd fifo output, whose format parameter
|
||||||
## has to be set to 44100:16:1. Example configuration:
|
## has to be set to 44100:16:1 for mono visualization
|
||||||
## (it has to be put into mpd.conf)
|
## or 44100:16:2 for stereo visualization. Example
|
||||||
|
## configuration (it has to be put into mpd.conf):
|
||||||
##
|
##
|
||||||
## audio_output {
|
## audio_output {
|
||||||
## type "fifo"
|
## type "fifo"
|
||||||
## name "My FIFO"
|
## name "My FIFO"
|
||||||
## path "/tmp/mpd.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 = ""
|
#visualizer_fifo_path = ""
|
||||||
#
|
#
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -75,8 +75,11 @@ Default number of seconds to crossfade, if enabled by ncmpcpp.
|
|||||||
.B mpd_communication_mode = MODE
|
.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 <mpd-0.15, so if you run such version and encounter strange bugs (e.g. current track time not being updated), you will either have to use 'polling' or upgrade your mpd.
|
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 <mpd-0.15, so if you run such version and encounter strange bugs (e.g. current track time not being updated), you will either have to use 'polling' or upgrade your mpd.
|
||||||
.TP
|
.TP
|
||||||
|
.B visualizer_in_stereo = yes/no
|
||||||
|
Should be set to 'yes', if fifo output's format was set to 44100:16:2.
|
||||||
|
.TP
|
||||||
.B visualizer_fifo_path = PATH
|
.B visualizer_fifo_path = PATH
|
||||||
Path to mpd fifo output. This is needed to make music visualizer work (note that output sound format of this fifo has to be 44100:16:1)
|
Path to mpd fifo output. This is needed to make music visualizer work (note that output sound format of this fifo has to be either 44100:16:1 or 44100:16:2, depending on whether you want mono or stereo visualization)
|
||||||
.TP
|
.TP
|
||||||
.B visualizer_output_name = NAME
|
.B visualizer_output_name = NAME
|
||||||
Name of output that provides data for visualizer. Needed to keep sound and visualization in sync.
|
Name of output that provides data for visualizer. Needed to keep sound and visualization in sync.
|
||||||
|
|||||||
@@ -426,6 +426,7 @@ void NcmpcppConfig::SetDefaults()
|
|||||||
mouse_list_scroll_whole_page = true;
|
mouse_list_scroll_whole_page = true;
|
||||||
new_design = false;
|
new_design = false;
|
||||||
visualizer_use_wave = true;
|
visualizer_use_wave = true;
|
||||||
|
visualizer_in_stereo = false;
|
||||||
browser_sort_by_mtime = false;
|
browser_sort_by_mtime = false;
|
||||||
tag_editor_extended_numeration = false;
|
tag_editor_extended_numeration = false;
|
||||||
media_library_display_date = true;
|
media_library_display_date = true;
|
||||||
@@ -1056,6 +1057,10 @@ void NcmpcppConfig::Read()
|
|||||||
{
|
{
|
||||||
visualizer_use_wave = v == "wave";
|
visualizer_use_wave = v == "wave";
|
||||||
}
|
}
|
||||||
|
else if (cl.find("visualizer_in_stereo") != std::string::npos)
|
||||||
|
{
|
||||||
|
visualizer_in_stereo = v == "yes";
|
||||||
|
}
|
||||||
else if (cl.find("mouse_support") != std::string::npos)
|
else if (cl.find("mouse_support") != std::string::npos)
|
||||||
{
|
{
|
||||||
mouse_support = v == "yes";
|
mouse_support = v == "yes";
|
||||||
|
|||||||
@@ -248,6 +248,7 @@ struct NcmpcppConfig
|
|||||||
bool mouse_list_scroll_whole_page;
|
bool mouse_list_scroll_whole_page;
|
||||||
bool new_design;
|
bool new_design;
|
||||||
bool visualizer_use_wave;
|
bool visualizer_use_wave;
|
||||||
|
bool visualizer_in_stereo;
|
||||||
bool browser_sort_by_mtime;
|
bool browser_sort_by_mtime;
|
||||||
bool tag_editor_extended_numeration;
|
bool tag_editor_extended_numeration;
|
||||||
bool media_library_display_date;
|
bool media_library_display_date;
|
||||||
|
|||||||
@@ -37,23 +37,20 @@ using Global::MainHeight;
|
|||||||
|
|
||||||
Visualizer *myVisualizer = new Visualizer;
|
Visualizer *myVisualizer = new Visualizer;
|
||||||
|
|
||||||
const unsigned Visualizer::Samples = 2048;
|
const int Visualizer::WindowTimeout = 1000/25; /* 25 fps */
|
||||||
#ifdef HAVE_FFTW3_H
|
|
||||||
const unsigned Visualizer::FFTResults = Samples/2+1;
|
|
||||||
#endif // HAVE_FFTW3_H
|
|
||||||
|
|
||||||
int Visualizer::WindowTimeout = 1000/25; /* 25 fps */
|
|
||||||
|
|
||||||
void Visualizer::Init()
|
void Visualizer::Init()
|
||||||
{
|
{
|
||||||
w = new Window(0, MainStartY, COLS, MainHeight, "", Config.visualizer_color, brNone);
|
w = new Window(0, MainStartY, COLS, MainHeight, "", Config.visualizer_color, brNone);
|
||||||
|
|
||||||
ResetFD();
|
ResetFD();
|
||||||
|
itsSamples = Config.visualizer_in_stereo ? 4096 : 2048;
|
||||||
# ifdef HAVE_FFTW3_H
|
# ifdef HAVE_FFTW3_H
|
||||||
itsFreqsMagnitude = new unsigned[FFTResults];
|
itsFFTResults = itsSamples/2+1;
|
||||||
itsInput = static_cast<double *>(fftw_malloc(sizeof(double)*Samples));
|
itsFreqsMagnitude = new unsigned[itsFFTResults];
|
||||||
itsOutput = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*FFTResults));
|
itsInput = static_cast<double *>(fftw_malloc(sizeof(double)*itsSamples));
|
||||||
itsPlan = fftw_plan_dft_r2c_1d(Samples, itsInput, itsOutput, FFTW_ESTIMATE);
|
itsOutput = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*itsFFTResults));
|
||||||
|
itsPlan = fftw_plan_dft_r2c_1d(itsSamples, itsInput, itsOutput, FFTW_ESTIMATE);
|
||||||
# endif // HAVE_FFTW3_H
|
# endif // HAVE_FFTW3_H
|
||||||
|
|
||||||
FindOutputID();
|
FindOutputID();
|
||||||
@@ -106,8 +103,8 @@ void Visualizer::Update()
|
|||||||
if (itsFifo < 0)
|
if (itsFifo < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// it supports only PCM in format 44100:16:1
|
// PCM in format 44100:16:1 (for mono visualization) and 44100:16:2 (for stereo visualization) is supported
|
||||||
static int16_t buf[Samples];
|
int16_t buf[itsSamples];
|
||||||
ssize_t data = read(itsFifo, buf, sizeof(buf));
|
ssize_t data = read(itsFifo, buf, sizeof(buf));
|
||||||
if (data < 0) // no data available in fifo
|
if (data < 0) // no data available in fifo
|
||||||
return;
|
return;
|
||||||
@@ -120,12 +117,30 @@ void Visualizer::Update()
|
|||||||
gettimeofday(&itsTimer, 0);
|
gettimeofday(&itsTimer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
w->Clear();
|
void (Visualizer::*draw)(int16_t *, ssize_t, size_t, size_t);
|
||||||
# ifdef HAVE_FFTW3_H
|
# ifdef HAVE_FFTW3_H
|
||||||
Config.visualizer_use_wave ? DrawSoundWave(buf, data) : DrawFrequencySpectrum(buf, data);
|
if (!Config.visualizer_use_wave)
|
||||||
# else
|
draw = &Visualizer::DrawFrequencySpectrum;
|
||||||
DrawSoundWave(buf, data);
|
else
|
||||||
# endif // HAVE_FFTW3_H
|
# 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();
|
w->Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,10 +152,10 @@ void Visualizer::SpacePressed()
|
|||||||
# endif // HAVE_FFTW3_H
|
# 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 samples_per_col = data/sizeof(int16_t)/COLS;
|
||||||
const int half_height = MainHeight/2;
|
const int half_height = height/2;
|
||||||
*w << fmtAltCharset;
|
*w << fmtAltCharset;
|
||||||
double prev_point_pos = 0;
|
double prev_point_pos = 0;
|
||||||
for (int i = 0; i < COLS; ++i)
|
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 /= samples_per_col;
|
||||||
point_pos /= std::numeric_limits<int16_t>::max();
|
point_pos /= std::numeric_limits<int16_t>::max();
|
||||||
point_pos *= half_height;
|
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 (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
|
||||||
@@ -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 breakpoint = std::max(prev_point_pos, point_pos);
|
||||||
const int half = (prev_point_pos+point_pos)/2;
|
const int half = (prev_point_pos+point_pos)/2;
|
||||||
for (int k = std::min(prev_point_pos, point_pos)+1; k < breakpoint; k += 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;
|
prev_point_pos = point_pos;
|
||||||
}
|
}
|
||||||
@@ -167,27 +182,27 @@ void Visualizer::DrawSoundWave(int16_t *buf, ssize_t data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAVE_FFTW3_H
|
#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
|
// zero old values
|
||||||
std::fill(buf+data/sizeof(int16_t), buf+Samples, 0);
|
std::fill(buf+data/sizeof(int16_t), buf+data/2, 0);
|
||||||
for (unsigned i = 0; i < Samples; ++i)
|
for (unsigned i = 0; i < data/2; ++i)
|
||||||
itsInput[i] = buf[i];
|
itsInput[i] = buf[i];
|
||||||
|
|
||||||
fftw_execute(itsPlan);
|
fftw_execute(itsPlan);
|
||||||
|
|
||||||
// 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 < FFTResults; ++i)
|
for (unsigned i = 0; i < itsFFTResults; ++i)
|
||||||
itsFreqsMagnitude[i] = sqrt(itsOutput[i][0]*itsOutput[i][0] + itsOutput[i][1]*itsOutput[i][1])/1e5*LINES/5;
|
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)
|
for (int i = 0; i < COLS; ++i)
|
||||||
{
|
{
|
||||||
size_t bar_height = 0;
|
size_t bar_height = 0;
|
||||||
for (int j = 0; j < freqs_per_col; ++j)
|
for (int j = 0; j < freqs_per_col; ++j)
|
||||||
bar_height += itsFreqsMagnitude[i*freqs_per_col+j];
|
bar_height += itsFreqsMagnitude[i*freqs_per_col+j];
|
||||||
bar_height = std::min(bar_height/freqs_per_col, MainHeight);
|
bar_height = std::min(bar_height/freqs_per_col, height);
|
||||||
mvwvline(w->Raw(), MainHeight-bar_height, i, 0, bar_height);
|
mvwvline(w->Raw(), y_offset > 0 ? y_offset : height-bar_height, i, 0, bar_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // HAVE_FFTW3_H
|
#endif // HAVE_FFTW3_H
|
||||||
|
|||||||
@@ -58,33 +58,29 @@ class Visualizer : public Screen<Window>
|
|||||||
void ResetFD();
|
void ResetFD();
|
||||||
void FindOutputID();
|
void FindOutputID();
|
||||||
|
|
||||||
static int WindowTimeout;
|
static const int WindowTimeout;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void Init();
|
virtual void Init();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DrawSoundWave(int16_t *, ssize_t);
|
void DrawSoundWave(int16_t *, ssize_t, size_t, size_t);
|
||||||
# ifdef HAVE_FFTW3_H
|
# ifdef HAVE_FFTW3_H
|
||||||
void DrawFrequencySpectrum(int16_t *, ssize_t);
|
void DrawFrequencySpectrum(int16_t *, ssize_t, size_t, size_t);
|
||||||
# endif // HAVE_FFTW3_H
|
# endif // HAVE_FFTW3_H
|
||||||
|
|
||||||
int itsOutputID;
|
int itsOutputID;
|
||||||
timeval itsTimer;
|
timeval itsTimer;
|
||||||
|
|
||||||
int itsFifo;
|
int itsFifo;
|
||||||
|
unsigned itsSamples;
|
||||||
# ifdef HAVE_FFTW3_H
|
# ifdef HAVE_FFTW3_H
|
||||||
|
unsigned itsFFTResults;
|
||||||
unsigned *itsFreqsMagnitude;
|
unsigned *itsFreqsMagnitude;
|
||||||
double *itsInput;
|
double *itsInput;
|
||||||
fftw_complex *itsOutput;
|
fftw_complex *itsOutput;
|
||||||
fftw_plan itsPlan;
|
fftw_plan itsPlan;
|
||||||
# endif // HAVE_FFTW3_H
|
# endif // HAVE_FFTW3_H
|
||||||
|
|
||||||
static const unsigned Samples;
|
|
||||||
# ifdef HAVE_FFTW3_H
|
|
||||||
static const unsigned FFTResults;
|
|
||||||
# endif // HAVE_FFTW3_H
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Visualizer *myVisualizer;
|
extern Visualizer *myVisualizer;
|
||||||
|
|||||||
Reference in New Issue
Block a user