new screen: music visualizer
This commit is contained in:
14
configure.in
14
configure.in
@@ -10,6 +10,7 @@ AC_PROG_CXX
|
||||
AM_PROG_LIBTOOL
|
||||
|
||||
AC_ARG_ENABLE(outputs, AS_HELP_STRING([--enable-outputs], [Enable outputs screen @<:@default=no@:>@]), [outputs=$enableval], [outputs=no])
|
||||
AC_ARG_ENABLE(visualizer, AS_HELP_STRING([--enable-visualizer], [Enable music visualizer screen @<:@default=no@:>@]), [visualizer=$enableval], [visualizer=no])
|
||||
AC_ARG_ENABLE(clock, AS_HELP_STRING([--enable-clock], [Enable clock screen @<:@default=no@:>@]), [clock=$enableval], [clock=no])
|
||||
AC_ARG_ENABLE(unicode, AS_HELP_STRING([--enable-unicode], [Enable utf8 support @<:@default=yes@:>@]), [unicode=$enableval], [unicode=yes])
|
||||
AC_ARG_WITH(curl, AS_HELP_STRING([--with-curl], [Enable fetching lyrics from the Internet @<:@default=auto@:>@]), [curl=$withval], [curl=auto])
|
||||
@@ -88,6 +89,19 @@ if test "$pdcurses" != "no" ; then
|
||||
fi
|
||||
AC_CHECK_HEADERS([curses.h], , AC_MSG_ERROR([missing ncurses.h header]))
|
||||
|
||||
dnl ======================
|
||||
dnl = checking for fftw3 =
|
||||
dnl ======================
|
||||
if test "$visualizer" = "yes" ; then
|
||||
PKG_CHECK_MODULES([fftw3], [fftw3 >= 3], , AC_MSG_ERROR([fftw3 library is required!]))
|
||||
AC_SUBST(fftw3_LIBS)
|
||||
AC_SUBST(fftw3_CFLAGS)
|
||||
CPPFLAGS="$CPPFLAGS $fftw3_CFLAGS"
|
||||
LDFLAGS="$LDFLAGS $fftw3_LIBS"
|
||||
AC_CHECK_HEADERS([fftw3.h], , AC_MSG_ERROR([missing fftw3.h header]))
|
||||
AC_DEFINE([ENABLE_VISUALIZER], [1], [enables music visualizer screen])
|
||||
fi
|
||||
|
||||
dnl =================================
|
||||
dnl = checking for curl and pthread =
|
||||
dnl =================================
|
||||
|
||||
17
doc/config
17
doc/config
@@ -17,6 +17,23 @@
|
||||
#
|
||||
#mpd_crossfade_time = "5"
|
||||
#
|
||||
##### music visualizer #####
|
||||
##
|
||||
## 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)
|
||||
##
|
||||
## audio_output {
|
||||
## type "fifo"
|
||||
## name "My FIFO"
|
||||
## path "/tmp/mpd.fifo"
|
||||
## format "44100:16:1"
|
||||
## }
|
||||
##
|
||||
#
|
||||
#visualizer_fifo_path = ""
|
||||
#
|
||||
##### system encoding #####
|
||||
##
|
||||
## if you use encoding other than utf8, set it in
|
||||
|
||||
2
doc/keys
2
doc/keys
@@ -58,6 +58,8 @@
|
||||
#
|
||||
#key_outputs = '8' 272
|
||||
#
|
||||
#key_music_visualizer = '9' 273
|
||||
#
|
||||
#key_clock = '0' 274
|
||||
#
|
||||
#key_stop = 's'
|
||||
|
||||
@@ -66,6 +66,9 @@ Set connection timeout to MPD to given value.
|
||||
.B mpd_crossfade_time = SECONDS
|
||||
Default number of seconds to crossfade, if enabled by ncmpcpp.
|
||||
.TP
|
||||
.B fifo_visualizer_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)
|
||||
.TP
|
||||
.B system_encoding = ENCODING
|
||||
If you use encoding other than utf8, set it in order to handle utf8 encoded strings properly.
|
||||
.TP
|
||||
|
||||
@@ -2,7 +2,8 @@ bin_PROGRAMS = ncmpcpp
|
||||
ncmpcpp_SOURCES = browser.cpp charset.cpp clock.cpp display.cpp help.cpp \
|
||||
helpers.cpp info.cpp libmpdclient.c lyrics.cpp media_library.cpp menu.cpp misc.cpp \
|
||||
mpdpp.cpp ncmpcpp.cpp outputs.cpp playlist.cpp playlist_editor.cpp scrollpad.cpp \
|
||||
search_engine.cpp settings.cpp song.cpp status.cpp str_pool.c tag_editor.cpp window.cpp
|
||||
search_engine.cpp settings.cpp song.cpp status.cpp str_pool.c tag_editor.cpp \
|
||||
visualizer.cpp window.cpp
|
||||
|
||||
# set the include path found by configure
|
||||
INCLUDES= $(all_includes)
|
||||
@@ -11,4 +12,5 @@ INCLUDES= $(all_includes)
|
||||
ncmpcpp_LDFLAGS = $(all_libraries)
|
||||
noinst_HEADERS = browser.h charset.h clock.h display.h global.h help.h \
|
||||
helpers.h home.h info.h lyrics.h media_library.h menu.h mpdpp.h outputs.h \
|
||||
playlist_editor.h screen.h scrollpad.h search_engine.h settings.h song.h tag_editor.h window.h
|
||||
playlist_editor.h screen.h scrollpad.h search_engine.h settings.h song.h tag_editor.h \
|
||||
visualizer.h window.h
|
||||
|
||||
@@ -147,6 +147,9 @@ void Help::GetKeybindings()
|
||||
# ifdef ENABLE_OUTPUTS
|
||||
*w << DisplayKeys(Key.Outputs) << "Outputs\n";
|
||||
# endif // ENABLE_OUTPUTS
|
||||
# ifdef ENABLE_VISUALIZER
|
||||
*w << DisplayKeys(Key.Visualizer) << "Music visualizer\n";
|
||||
# endif // ENABLE_VISUALIZER
|
||||
# ifdef ENABLE_CLOCK
|
||||
*w << DisplayKeys(Key.Clock) << "Clock screen\n";
|
||||
# endif // ENABLE_CLOCK
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "outputs.h"
|
||||
#include "status.h"
|
||||
#include "tag_editor.h"
|
||||
#include "visualizer.h"
|
||||
|
||||
#define CHECK_PLAYLIST_FOR_FILTERING \
|
||||
if (myPlaylist->Main()->isFiltered()) \
|
||||
@@ -248,6 +249,9 @@ int main(int argc, char *argv[])
|
||||
# ifdef HAVE_TAGLIB_H
|
||||
*wHeader << " " << fmtBold << char(Key.TagEditor[0]) << fmtBoldEnd << ":Tag editor";
|
||||
# endif // HAVE_TAGLIB_H
|
||||
# ifdef ENABLE_VISUALIZER
|
||||
*wHeader << " " << fmtBold << char(Key.Visualizer[0]) << fmtBoldEnd << ":Music visualizer";
|
||||
# endif // ENABLE_VISUALIZER
|
||||
# ifdef ENABLE_CLOCK
|
||||
*wHeader << " " << fmtBold << char(Key.Clock[0]) << fmtBoldEnd << ":Clock";
|
||||
# endif // ENABLE_CLOCK
|
||||
@@ -435,6 +439,14 @@ int main(int argc, char *argv[])
|
||||
myTagEditor->hasToBeResized = 1;
|
||||
# endif // HAVE_TAGLIB_H
|
||||
|
||||
# ifdef ENABLE_VISUALIZER
|
||||
myVisualizer->hasToBeResized = 1;
|
||||
# endif // ENABLE_VISUALIZER
|
||||
|
||||
# ifdef ENABLE_OUTPUTS
|
||||
myOutputs->hasToBeResized = 1;
|
||||
# endif // ENABLE_OUTPUTS
|
||||
|
||||
# ifdef ENABLE_CLOCK
|
||||
myClock->hasToBeResized = 1;
|
||||
# endif // ENABLE_CLOCK
|
||||
@@ -1983,6 +1995,12 @@ int main(int argc, char *argv[])
|
||||
myOutputs->SwitchTo();
|
||||
}
|
||||
# endif // ENABLE_OUTPUTS
|
||||
# ifdef ENABLE_VISUALIZER
|
||||
else if (Keypressed(input, Key.Visualizer))
|
||||
{
|
||||
myVisualizer->SwitchTo();
|
||||
}
|
||||
# endif // ENABLE_VISUALIZER
|
||||
# ifdef ENABLE_CLOCK
|
||||
else if (Keypressed(input, Key.Clock))
|
||||
{
|
||||
|
||||
@@ -117,6 +117,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
|
||||
keys.PlaylistEditor[0] = '6';
|
||||
keys.TagEditor[0] = '7';
|
||||
keys.Outputs[0] = '8';
|
||||
keys.Visualizer[0] = '9';
|
||||
keys.Clock[0] = '0';
|
||||
keys.Stop[0] = 's';
|
||||
keys.Pause[0] = 'P';
|
||||
@@ -187,6 +188,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
|
||||
keys.PlaylistEditor[1] = 270;
|
||||
keys.TagEditor[1] = 271;
|
||||
keys.Outputs[1] = 272;
|
||||
keys.Visualizer[1] = 273;
|
||||
keys.Clock[1] = 274;
|
||||
keys.Stop[1] = null_key;
|
||||
keys.Pause[1] = null_key;
|
||||
@@ -369,6 +371,8 @@ void ReadKeys(ncmpcpp_keys &keys)
|
||||
GetKeys(key, keys.TagEditor);
|
||||
else if (key.find("key_outputs ") != std::string::npos)
|
||||
GetKeys(key, keys.Outputs);
|
||||
else if (key.find("key_music_visualizer ") != std::string::npos)
|
||||
GetKeys(key, keys.Visualizer);
|
||||
else if (key.find("key_clock ") != std::string::npos)
|
||||
GetKeys(key, keys.Clock);
|
||||
else if (key.find("key_stop ") != std::string::npos)
|
||||
@@ -496,6 +500,11 @@ void ReadConfiguration(ncmpcpp_config &conf)
|
||||
conf.mpd_music_dir = v + "/";
|
||||
}
|
||||
}
|
||||
if (cl.find("visualizer_fifo_path") != std::string::npos)
|
||||
{
|
||||
if (!v.empty())
|
||||
conf.visualizer_fifo_path = v;
|
||||
}
|
||||
else if (cl.find("mpd_port") != std::string::npos)
|
||||
{
|
||||
if (StrToInt(v))
|
||||
|
||||
@@ -71,6 +71,7 @@ struct ncmpcpp_keys
|
||||
int PlaylistEditor[2];
|
||||
int TagEditor[2];
|
||||
int Outputs[2];
|
||||
int Visualizer[2];
|
||||
int Clock[2];
|
||||
int Stop[2];
|
||||
int Pause[2];
|
||||
@@ -126,6 +127,7 @@ struct ncmpcpp_config
|
||||
{
|
||||
std::string mpd_host;
|
||||
std::string mpd_music_dir;
|
||||
std::string visualizer_fifo_path;
|
||||
std::string empty_tag;
|
||||
std::string song_list_columns_format;
|
||||
std::string song_list_format;
|
||||
|
||||
131
src/visualizer.cpp
Normal file
131
src/visualizer.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008-2009 by Andrzej Rybczak *
|
||||
* electricityispower@gmail.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#include "visualizer.h"
|
||||
|
||||
#ifdef ENABLE_VISUALIZER
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <fcntl.h>
|
||||
|
||||
using Global::myScreen;
|
||||
using Global::MainStartY;
|
||||
using Global::MainHeight;
|
||||
|
||||
Visualizer *myVisualizer = new Visualizer;
|
||||
|
||||
const unsigned Visualizer::Samples = 2048;
|
||||
const unsigned Visualizer::FFTResults = Samples/2+1;
|
||||
|
||||
void Visualizer::Init()
|
||||
{
|
||||
w = new Window(0, MainStartY, COLS, MainHeight, "", Config.main_color, brNone);
|
||||
w->SetTimeout(Config.visualizer_fifo_path.empty() ? ncmpcpp_window_timeout : 40 /* this gives us 25 fps */);
|
||||
|
||||
itsFifo = -1;
|
||||
itsFreqsMagnitude = new unsigned[FFTResults];
|
||||
itsInput = static_cast<double *>(fftw_malloc(sizeof(double)*Samples));
|
||||
itsOutput = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*FFTResults));
|
||||
itsPlan = fftw_plan_dft_r2c_1d(Samples, itsInput, itsOutput, FFTW_ESTIMATE);
|
||||
|
||||
isInitialized = 1;
|
||||
}
|
||||
|
||||
void Visualizer::SwitchTo()
|
||||
{
|
||||
if (myScreen == this)
|
||||
return;
|
||||
|
||||
if (!isInitialized)
|
||||
Init();
|
||||
|
||||
if (hasToBeResized)
|
||||
Resize();
|
||||
|
||||
myScreen = this;
|
||||
w->Clear();
|
||||
|
||||
if (itsFifo < 0 && (itsFifo = open(Config.visualizer_fifo_path.c_str(), O_RDONLY | O_NONBLOCK)) < 0)
|
||||
ShowMessage("Couldn't open fifo for reading PCM data: %s", strerror(errno));
|
||||
|
||||
Global::RedrawHeader = 1;
|
||||
}
|
||||
|
||||
void Visualizer::Resize()
|
||||
{
|
||||
w->Resize(COLS, MainHeight);
|
||||
w->MoveTo(0, MainStartY);
|
||||
hasToBeResized = 0;
|
||||
}
|
||||
|
||||
std::basic_string<my_char_t> Visualizer::Title()
|
||||
{
|
||||
return U("Music visualizer");
|
||||
}
|
||||
|
||||
void Visualizer::Update()
|
||||
{
|
||||
if (itsFifo < 0)
|
||||
return;
|
||||
|
||||
// if mpd is stopped, clear the screen
|
||||
if (Mpd.GetState() < MPD::psPlay)
|
||||
{
|
||||
w->Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// it supports only PCM in format 44100:16:1
|
||||
static int16_t buf[Samples];
|
||||
ssize_t data = read(itsFifo, buf, sizeof(buf));
|
||||
if (data < 0) // no data available in fifo
|
||||
return;
|
||||
|
||||
// zero old values
|
||||
std::fill(buf+data/sizeof(int16_t), buf+Samples, 0);
|
||||
for (unsigned i = 0; i < Samples; ++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;
|
||||
|
||||
w->Clear(0);
|
||||
const int freqs_per_col = FFTResults/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, Global::MainHeight);
|
||||
mvwvline(w->Raw(), Global::MainHeight-bar_height, i, 0, bar_height);
|
||||
}
|
||||
w->Refresh();
|
||||
}
|
||||
|
||||
#endif // ENABLE_VISUALIZER
|
||||
|
||||
73
src/visualizer.h
Normal file
73
src/visualizer.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008-2009 by Andrzej Rybczak *
|
||||
* electricityispower@gmail.com *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef _VISUALIZER_H
|
||||
#define _VISUALIZER_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VISUALIZER
|
||||
|
||||
#include "window.h"
|
||||
#include "screen.h"
|
||||
|
||||
#include <fftw3.h>
|
||||
|
||||
class Visualizer : public Screen<Window>
|
||||
{
|
||||
public:
|
||||
virtual void SwitchTo();
|
||||
virtual void Resize();
|
||||
|
||||
virtual std::basic_string<my_char_t> Title();
|
||||
|
||||
virtual void Update();
|
||||
virtual void Scroll(Where, const int *) { }
|
||||
|
||||
virtual void EnterPressed() { }
|
||||
virtual void SpacePressed() { }
|
||||
virtual void MouseButtonPressed(MEVENT) { }
|
||||
|
||||
virtual NCurses::List *GetList() { return 0; }
|
||||
|
||||
virtual bool allowsSelection() { return false; }
|
||||
|
||||
protected:
|
||||
virtual void Init();
|
||||
|
||||
private:
|
||||
int itsFifo;
|
||||
unsigned *itsFreqsMagnitude;
|
||||
double *itsInput;
|
||||
fftw_complex *itsOutput;
|
||||
fftw_plan itsPlan;
|
||||
|
||||
static const unsigned Samples;
|
||||
static const unsigned FFTResults;
|
||||
};
|
||||
|
||||
extern Visualizer *myVisualizer;
|
||||
|
||||
#endif // ENABLE_VISUALIZER
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user