new screen: music visualizer

This commit is contained in:
Andrzej Rybczak
2009-08-28 00:38:29 +02:00
parent bc1a8a2d1a
commit 75f9f840f7
11 changed files with 276 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ AC_PROG_CXX
AM_PROG_LIBTOOL AM_PROG_LIBTOOL
AC_ARG_ENABLE(outputs, AS_HELP_STRING([--enable-outputs], [Enable outputs screen @<:@default=no@:>@]), [outputs=$enableval], [outputs=no]) 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(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_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]) 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 fi
AC_CHECK_HEADERS([curses.h], , AC_MSG_ERROR([missing ncurses.h header])) 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 =================================
dnl = checking for curl and pthread = dnl = checking for curl and pthread =
dnl ================================= dnl =================================

View File

@@ -17,6 +17,23 @@
# #
#mpd_crossfade_time = "5" #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 ##### ##### system encoding #####
## ##
## if you use encoding other than utf8, set it in ## if you use encoding other than utf8, set it in

View File

@@ -58,6 +58,8 @@
# #
#key_outputs = '8' 272 #key_outputs = '8' 272
# #
#key_music_visualizer = '9' 273
#
#key_clock = '0' 274 #key_clock = '0' 274
# #
#key_stop = 's' #key_stop = 's'

View File

@@ -66,6 +66,9 @@ Set connection timeout to MPD to given value.
.B mpd_crossfade_time = SECONDS .B mpd_crossfade_time = SECONDS
Default number of seconds to crossfade, if enabled by ncmpcpp. Default number of seconds to crossfade, if enabled by ncmpcpp.
.TP .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 .B system_encoding = ENCODING
If you use encoding other than utf8, set it in order to handle utf8 encoded strings properly. If you use encoding other than utf8, set it in order to handle utf8 encoded strings properly.
.TP .TP

View File

@@ -2,7 +2,8 @@ bin_PROGRAMS = ncmpcpp
ncmpcpp_SOURCES = browser.cpp charset.cpp clock.cpp display.cpp help.cpp \ 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 \ 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 \ 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 # set the include path found by configure
INCLUDES= $(all_includes) INCLUDES= $(all_includes)
@@ -11,4 +12,5 @@ INCLUDES= $(all_includes)
ncmpcpp_LDFLAGS = $(all_libraries) ncmpcpp_LDFLAGS = $(all_libraries)
noinst_HEADERS = browser.h charset.h clock.h display.h global.h help.h \ 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 \ 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

View File

@@ -147,6 +147,9 @@ void Help::GetKeybindings()
# ifdef ENABLE_OUTPUTS # ifdef ENABLE_OUTPUTS
*w << DisplayKeys(Key.Outputs) << "Outputs\n"; *w << DisplayKeys(Key.Outputs) << "Outputs\n";
# endif // ENABLE_OUTPUTS # endif // ENABLE_OUTPUTS
# ifdef ENABLE_VISUALIZER
*w << DisplayKeys(Key.Visualizer) << "Music visualizer\n";
# endif // ENABLE_VISUALIZER
# ifdef ENABLE_CLOCK # ifdef ENABLE_CLOCK
*w << DisplayKeys(Key.Clock) << "Clock screen\n"; *w << DisplayKeys(Key.Clock) << "Clock screen\n";
# endif // ENABLE_CLOCK # endif // ENABLE_CLOCK

View File

@@ -48,6 +48,7 @@
#include "outputs.h" #include "outputs.h"
#include "status.h" #include "status.h"
#include "tag_editor.h" #include "tag_editor.h"
#include "visualizer.h"
#define CHECK_PLAYLIST_FOR_FILTERING \ #define CHECK_PLAYLIST_FOR_FILTERING \
if (myPlaylist->Main()->isFiltered()) \ if (myPlaylist->Main()->isFiltered()) \
@@ -248,6 +249,9 @@ int main(int argc, char *argv[])
# ifdef HAVE_TAGLIB_H # ifdef HAVE_TAGLIB_H
*wHeader << " " << fmtBold << char(Key.TagEditor[0]) << fmtBoldEnd << ":Tag editor"; *wHeader << " " << fmtBold << char(Key.TagEditor[0]) << fmtBoldEnd << ":Tag editor";
# endif // HAVE_TAGLIB_H # endif // HAVE_TAGLIB_H
# ifdef ENABLE_VISUALIZER
*wHeader << " " << fmtBold << char(Key.Visualizer[0]) << fmtBoldEnd << ":Music visualizer";
# endif // ENABLE_VISUALIZER
# ifdef ENABLE_CLOCK # ifdef ENABLE_CLOCK
*wHeader << " " << fmtBold << char(Key.Clock[0]) << fmtBoldEnd << ":Clock"; *wHeader << " " << fmtBold << char(Key.Clock[0]) << fmtBoldEnd << ":Clock";
# endif // ENABLE_CLOCK # endif // ENABLE_CLOCK
@@ -435,6 +439,14 @@ int main(int argc, char *argv[])
myTagEditor->hasToBeResized = 1; myTagEditor->hasToBeResized = 1;
# endif // HAVE_TAGLIB_H # 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 # ifdef ENABLE_CLOCK
myClock->hasToBeResized = 1; myClock->hasToBeResized = 1;
# endif // ENABLE_CLOCK # endif // ENABLE_CLOCK
@@ -1983,6 +1995,12 @@ int main(int argc, char *argv[])
myOutputs->SwitchTo(); myOutputs->SwitchTo();
} }
# endif // ENABLE_OUTPUTS # endif // ENABLE_OUTPUTS
# ifdef ENABLE_VISUALIZER
else if (Keypressed(input, Key.Visualizer))
{
myVisualizer->SwitchTo();
}
# endif // ENABLE_VISUALIZER
# ifdef ENABLE_CLOCK # ifdef ENABLE_CLOCK
else if (Keypressed(input, Key.Clock)) else if (Keypressed(input, Key.Clock))
{ {

View File

@@ -117,6 +117,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
keys.PlaylistEditor[0] = '6'; keys.PlaylistEditor[0] = '6';
keys.TagEditor[0] = '7'; keys.TagEditor[0] = '7';
keys.Outputs[0] = '8'; keys.Outputs[0] = '8';
keys.Visualizer[0] = '9';
keys.Clock[0] = '0'; keys.Clock[0] = '0';
keys.Stop[0] = 's'; keys.Stop[0] = 's';
keys.Pause[0] = 'P'; keys.Pause[0] = 'P';
@@ -187,6 +188,7 @@ void DefaultKeys(ncmpcpp_keys &keys)
keys.PlaylistEditor[1] = 270; keys.PlaylistEditor[1] = 270;
keys.TagEditor[1] = 271; keys.TagEditor[1] = 271;
keys.Outputs[1] = 272; keys.Outputs[1] = 272;
keys.Visualizer[1] = 273;
keys.Clock[1] = 274; keys.Clock[1] = 274;
keys.Stop[1] = null_key; keys.Stop[1] = null_key;
keys.Pause[1] = null_key; keys.Pause[1] = null_key;
@@ -369,6 +371,8 @@ void ReadKeys(ncmpcpp_keys &keys)
GetKeys(key, keys.TagEditor); GetKeys(key, keys.TagEditor);
else if (key.find("key_outputs ") != std::string::npos) else if (key.find("key_outputs ") != std::string::npos)
GetKeys(key, keys.Outputs); 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) else if (key.find("key_clock ") != std::string::npos)
GetKeys(key, keys.Clock); GetKeys(key, keys.Clock);
else if (key.find("key_stop ") != std::string::npos) else if (key.find("key_stop ") != std::string::npos)
@@ -496,6 +500,11 @@ void ReadConfiguration(ncmpcpp_config &conf)
conf.mpd_music_dir = v + "/"; 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) else if (cl.find("mpd_port") != std::string::npos)
{ {
if (StrToInt(v)) if (StrToInt(v))

View File

@@ -71,6 +71,7 @@ struct ncmpcpp_keys
int PlaylistEditor[2]; int PlaylistEditor[2];
int TagEditor[2]; int TagEditor[2];
int Outputs[2]; int Outputs[2];
int Visualizer[2];
int Clock[2]; int Clock[2];
int Stop[2]; int Stop[2];
int Pause[2]; int Pause[2];
@@ -126,6 +127,7 @@ struct ncmpcpp_config
{ {
std::string mpd_host; std::string mpd_host;
std::string mpd_music_dir; std::string mpd_music_dir;
std::string visualizer_fifo_path;
std::string empty_tag; std::string empty_tag;
std::string song_list_columns_format; std::string song_list_columns_format;
std::string song_list_format; std::string song_list_format;

131
src/visualizer.cpp Normal file
View 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
View 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