summaryrefslogtreecommitdiffstats
path: root/noatun/library
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commite2de64d6f1beb9e492daf5b886e19933c1fa41dd (patch)
tree9047cf9e6b5c43878d5bf82660adae77ceee097a /noatun/library
downloadtdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.tar.gz
tdemultimedia-e2de64d6f1beb9e492daf5b886e19933c1fa41dd.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdemultimedia@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'noatun/library')
-rw-r--r--noatun/library/Makefile.am57
-rw-r--r--noatun/library/app.cpp530
-rw-r--r--noatun/library/cmodule.cpp131
-rw-r--r--noatun/library/cmodule.h43
-rw-r--r--noatun/library/controls.cpp114
-rw-r--r--noatun/library/conversion.cpp149
-rw-r--r--noatun/library/downloader.cpp236
-rw-r--r--noatun/library/effects.cpp285
-rw-r--r--noatun/library/effectview.cpp284
-rw-r--r--noatun/library/effectview.h83
-rw-r--r--noatun/library/engine.cpp589
-rw-r--r--noatun/library/equalizer.cpp339
-rw-r--r--noatun/library/equalizerview.cpp325
-rw-r--r--noatun/library/equalizerview.h91
-rw-r--r--noatun/library/equalizerwidget.ui337
-rw-r--r--noatun/library/gentable.cpp1037
-rw-r--r--noatun/library/globalvideo.h26
-rw-r--r--noatun/library/ksaver.cpp184
-rw-r--r--noatun/library/ksaver.h102
-rw-r--r--noatun/library/mimetypetree.cpp64
-rw-r--r--noatun/library/mimetypetree.h35
-rw-r--r--noatun/library/noatun/Makefile.am10
-rw-r--r--noatun/library/noatun/app.h271
-rw-r--r--noatun/library/noatun/controls.h79
-rw-r--r--noatun/library/noatun/conversion.h117
-rw-r--r--noatun/library/noatun/downloader.h120
-rw-r--r--noatun/library/noatun/effects.h186
-rw-r--r--noatun/library/noatun/engine.h120
-rw-r--r--noatun/library/noatun/equalizer.h238
-rw-r--r--noatun/library/noatun/mocs.cpp37
-rw-r--r--noatun/library/noatun/player.h270
-rw-r--r--noatun/library/noatun/playlist.h531
-rw-r--r--noatun/library/noatun/playlistsaver.h102
-rw-r--r--noatun/library/noatun/plugin.h467
-rw-r--r--noatun/library/noatun/pluginloader.h99
-rw-r--r--noatun/library/noatun/pref.h91
-rw-r--r--noatun/library/noatun/scrollinglabel.h78
-rw-r--r--noatun/library/noatun/stdaction.h204
-rw-r--r--noatun/library/noatun/stereobuttonaction.h31
-rw-r--r--noatun/library/noatun/vequalizer.h455
-rw-r--r--noatun/library/noatun/video.h60
-rw-r--r--noatun/library/noatunarts/Equalizer.mcopclass4
-rw-r--r--noatun/library/noatunarts/EqualizerSSE.mcopclass4
-rw-r--r--noatun/library/noatunarts/Equalizer_impl.cpp472
-rw-r--r--noatun/library/noatunarts/FFTScope.mcopclass4
-rw-r--r--noatun/library/noatunarts/FFTScopeStereo.mcopclass4
-rw-r--r--noatun/library/noatunarts/FFTScopes.cpp422
-rw-r--r--noatun/library/noatunarts/Listener.mcopclass4
-rw-r--r--noatun/library/noatunarts/Makefile.am33
-rw-r--r--noatun/library/noatunarts/RawScope.mcopclass4
-rw-r--r--noatun/library/noatunarts/RawScopeStereo.mcopclass4
-rw-r--r--noatun/library/noatunarts/Session.mcopclass4
-rw-r--r--noatun/library/noatunarts/Session_impl.cpp82
-rw-r--r--noatun/library/noatunarts/StereoEffectStack.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoEffectStack_impl.cpp253
-rw-r--r--noatun/library/noatunarts/StereoVolumeControl.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass4
-rw-r--r--noatun/library/noatunarts/StereoVolumeControl_impl.cpp181
-rw-r--r--noatun/library/noatunarts/fft.c384
-rw-r--r--noatun/library/noatunarts/fft.h88
-rw-r--r--noatun/library/noatunarts/noatunarts.idl91
-rw-r--r--noatun/library/noatunlistview.h5
-rw-r--r--noatun/library/noatunstdaction.cpp362
-rw-r--r--noatun/library/noatuntags/Makefile.am14
-rw-r--r--noatun/library/noatuntags/tags.cpp229
-rw-r--r--noatun/library/noatuntags/tags.h35
-rw-r--r--noatun/library/noatuntags/tagsgetter.h56
-rw-r--r--noatun/library/noatunui.cpp12
-rw-r--r--noatun/library/player.cpp379
-rw-r--r--noatun/library/playlist.cpp442
-rw-r--r--noatun/library/playlistsaver.cpp771
-rw-r--r--noatun/library/plugin.cpp578
-rw-r--r--noatun/library/plugin_deps.h44
-rw-r--r--noatun/library/pluginloader.cpp366
-rw-r--r--noatun/library/pluginmodule.cpp406
-rw-r--r--noatun/library/pluginmodule.h100
-rw-r--r--noatun/library/pref.cpp95
-rw-r--r--noatun/library/scrollinglabel.cpp196
-rw-r--r--noatun/library/spline.cpp194
-rw-r--r--noatun/library/spline.h74
-rw-r--r--noatun/library/stereobuttonaction.cpp47
-rw-r--r--noatun/library/titleproxy.cpp376
-rw-r--r--noatun/library/titleproxy.h129
-rw-r--r--noatun/library/vequalizer.cpp936
-rw-r--r--noatun/library/video.cpp162
85 files changed, 16665 insertions, 0 deletions
diff --git a/noatun/library/Makefile.am b/noatun/library/Makefile.am
new file mode 100644
index 00000000..f3e1dfd9
--- /dev/null
+++ b/noatun/library/Makefile.am
@@ -0,0 +1,57 @@
+INCLUDES = -I$(top_srcdir)/noatun/library/noatun \
+ -I$(top_srcdir)/noatun/library \
+ -I$(top_srcdir)/arts/gui/kde -I$(top_builddir)/arts/gui/kde \
+ -I$(top_srcdir)/arts/gui/common -I$(top_builddir)/arts/gui/common \
+ -I$(kde_includes)/arts \
+ -I$(top_srcdir)/noatun/library/noatunarts \
+ -I$(top_builddir)/noatun/library/noatunarts \
+ $(all_includes)
+
+SUBDIRS= noatunarts . noatuntags noatun
+
+lib_LTLIBRARIES = libnoatun.la libnoatuncontrols.la
+
+libnoatun_la_SOURCES = pluginmodule.cpp cmodule.cpp downloader.cpp engine.cpp \
+ playlist.cpp pref.cpp \
+ player.cpp playlistsaver.cpp app.cpp \
+ pluginloader.cpp plugin.cpp \
+ noatunstdaction.cpp conversion.cpp\
+ noatunui.cpp effectview.cpp \
+ equalizerwidget.ui equalizerview.cpp equalizer.cpp \
+ effects.cpp mimetypetree.cpp stereobuttonaction.cpp ksaver.cpp \
+ video.cpp vequalizer.cpp spline.cpp titleproxy.cpp
+
+include_HEADERS = \
+ cmodule.h plugin_deps.h equalizerview.h effectview.h mimetypetree.h \
+ ksaver.h
+
+libnoatun_la_LDFLAGS = \
+ -version-info 3:0:2 $(KDE_RPATH) $(all_libraries)
+libnoatun_la_LIBADD = $(top_builddir)/arts/gui/common/libartsgui.la \
+ $(top_builddir)/arts/gui/kde/libartsgui_kde.la $(top_builddir)/arts/modules/libartsmodules.la \
+ -lkio -lqtmcop -lkmedia2_idl $(top_builddir)/noatun/library/noatunarts/libnoatunarts.la \
+ -lartsflow -lsoundserver_idl -lartskde $(LIBDL)
+
+libnoatuncontrols_la_SOURCES = controls.cpp scrollinglabel.cpp
+libnoatuncontrols_la_LDFLAGS = -version-info 3:0:2 $(KDE_RPATH) $(all_libraries) -no-undefined
+libnoatuncontrols_la_LIBADD = $(LIBDL) $(LIB_KDEUI)
+
+METASOURCES = AUTO
+META_INCLUDES = $(srcdir)/noatun
+
+noinst_PROGRAMS = gentable
+gentable_SOURCES = gentable.cpp
+gentable_LDFLAGS = $(KDE_EXTRA_RPATH)
+
+magictable: gentable
+ $(top_builddir)/noatun/library/gentable > magictable
+
+magictabledir = $(kde_datadir)/noatun
+magictable_DATA = magictable
+
+effects.lo: noatunarts/noatunarts.h ../../arts/gui/common/artsgui.h
+engine.lo: noatunarts/noatunarts.h
+equalizer.lo: noatunarts/noatunarts.h
+plugin.lo: noatunarts/noatunarts.h
+vequalizer.lo: noatunarts/noatunarts.h
+
diff --git a/noatun/library/app.cpp b/noatun/library/app.cpp
new file mode 100644
index 00000000..04167fe7
--- /dev/null
+++ b/noatun/library/app.cpp
@@ -0,0 +1,530 @@
+#include "cmodule.h"
+#include "downloader.h"
+#include "effects.h"
+#include "effectview.h"
+#include "engine.h"
+#include "equalizer.h"
+#include "equalizerview.h"
+#include "vequalizer.h"
+#include "app.h"
+#include "playlist.h"
+#include "pref.h"
+#include "player.h"
+#include "plugin.h"
+#include "pluginloader.h"
+#include "globalvideo.h"
+#include "stdaction.h"
+
+#include <common.h>
+#include <dcopobject.h>
+#include <dispatcher.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kglobal.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmainwindow.h>
+#include <kmessagebox.h>
+#include <knotifyclient.h>
+#include <kpopupmenu.h>
+#include <qfile.h>
+#include <qimage.h>
+#include <qiomanager.h>
+#include <qsessionmanager.h>
+#include <qtextstream.h>
+#include <signal.h>
+#include <kmimetype.h>
+
+
+using std::string;
+using std::vector;
+
+// forgot the d pointer in Player TODO
+static GlobalVideo *globalVideo;
+struct NoatunApp::Private
+{
+ Effects *effects;
+ VEqualizer *vequalizer;
+};
+
+
+NoatunApp::NoatunApp()
+ : KUniqueApplication(true, true, true), mPluginMenu(0), mPluginActionMenu(0), mEqualizer(0)
+{
+ d = new Private;
+ d->vequalizer=0;
+ d->effects=0;
+
+ Visualization::internalVis=true;
+
+ mDownloader=new Downloader;
+
+ Visualization::initDispatcher(); // 316
+
+ showingInterfaces = true;
+
+ // set the default config data
+ // TODO: Maybe a first time wizard instead?
+ KConfig *config=KGlobal::config(); // +
+ config->setGroup(QString::null); // 1
+ if (!config->readEntry("Modules").length())
+ {
+ QStringList modules;
+ modules.append("excellent.plugin");
+ modules.append("splitplaylist.plugin");
+ modules.append("marquis.plugin");
+ modules.append("systray.plugin");
+ modules.append("metatag.plugin");
+ config->writeEntry("Modules", modules);
+ config->sync();
+ } // 1
+
+ mPref=new NoatunPreferences(0L); // 115
+ mPref->hide(); // 1
+ mLibraryLoader = new LibraryLoader; // 0
+
+ mLibraryLoader->add("dcopiface.plugin");
+
+ new General(this); // 25
+ new Plugins(this); // 149
+// new Types(this);
+
+ mPlayer=new Player; // 139
+ d->effects=new Effects; // 1
+ d->vequalizer = new VEqualizer;
+ d->vequalizer->init();
+ mEqualizer=new Equalizer; // 0
+ mEqualizer->init(); // 41
+ mEffectView=new EffectView; // 859
+ mEqualizerView=new EqualizerView; // 24
+
+ QTimer::singleShot(0, mDownloader, SLOT(start()));
+
+ ::globalVideo = new GlobalVideo;
+
+ if(isRestored())
+ {
+ mLibraryLoader->add("marquis.plugin");
+ static_cast<SessionManagement*>(mLibraryLoader->plugins().first())->restore();
+ }
+ else
+ {
+ loadPlugins(); // 1531
+ if (!playlist())
+ {
+ KMessageBox::error(0,i18n("No playlist plugin was found. " \
+ "Please make sure that Noatun was installed correctly."));
+ KApplication::quit();
+ delete this;
+ }
+ else
+ {
+ config->setGroup(QString::null); // 0
+ player()->setVolume(config->readNumEntry("Volume", 100)); // 10
+ player()->loop(config->readNumEntry("LoopStyle", (int)Player::None));
+ mPlayer->engine()->setInitialized(); // 0
+
+ switch (startupPlayMode())
+ {
+ case Restore:
+ restoreEngineState();
+ break;
+ case Play:
+ player()->play();
+ break;
+ case DontPlay:
+ default:
+ break;
+ }
+ }
+ }
+}
+
+NoatunApp::~NoatunApp()
+{
+ saveEngineState();
+ KConfig *config = KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("Volume", player()->volume());
+ config->writeEntry("LoopStyle", player()->loopStyle());
+ // for version continuity in the future
+ config->writeEntry("Version", QString(version())); // 1
+ config->sync(); // 40
+
+ mPlayer->stop();
+ delete ::globalVideo;
+ delete mLibraryLoader;
+ delete mEffectView;
+ delete mDownloader;
+ delete mEqualizer;
+ delete d->vequalizer;
+ delete mEqualizerView;
+ delete d->effects;
+
+ delete mPlayer;
+ delete mPref;
+ config->sync();
+
+ delete d;
+}
+
+QCString NoatunApp::version() const
+{
+ return aboutData()->version().ascii();
+}
+
+inline bool logicalXOR(bool A, bool B)
+{
+ return (A || B) && !(A && B);
+}
+
+#define READBOOLOPT_EX(name, string, def, group, reversal) \
+bool NoatunApp::name() const \
+{ \
+ KConfig *config=KGlobal::config(); \
+ config->setGroup(group); \
+ return logicalXOR(config->readBoolEntry(string, def), reversal); \
+}
+
+#define READBOOLOPT(name, string, def) \
+ READBOOLOPT_EX(name, string, def, "", false)
+
+
+READBOOLOPT(autoPlay, "AutoPlay", false)
+READBOOLOPT(loopList, "LoopList", true)
+READBOOLOPT(hackUpPlaylist, "HackUpPlaylist", true)
+READBOOLOPT(fastMixer, "FastMixer", false)
+READBOOLOPT(displayRemaining, "DisplayRemaining", false)
+READBOOLOPT(clearOnOpen, "ClearOnOpen", false)
+READBOOLOPT_EX(oneInstance, "MultipleInstances", true, "KDE", true)
+
+#undef READBOOLOPT
+#undef READBOOLOPT_EX
+
+bool NoatunApp::clearOnStart() const
+{
+ return clearOnOpen();
+}
+
+int NoatunApp::startupPlayMode() const
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ return config->readNumEntry("StartupPlayMode", autoPlay() ? Play : Restore);
+}
+
+void NoatunApp::setStartupPlayMode(int mode)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("StartupPlayMode", mode);
+ config->sync();
+}
+
+void NoatunApp::setHackUpPlaylist(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("HackUpPlaylist", b);
+ config->sync();
+}
+
+void NoatunApp::setFastMixer(bool b)
+{
+ bool whatBefore=fastMixer();
+ if (whatBefore!=b)
+ {
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("FastMixer", b);
+ config->sync();
+ player()->engine()->useHardwareMixer(b);
+ }
+}
+
+void NoatunApp::setOneInstance(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("KDE");
+ config->writeEntry("MultipleInstances", !b);
+ config->sync();
+}
+
+void NoatunApp::setLoopList(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("LoopList", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setAutoPlay(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("AutoPlay", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setRememberPositions(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("RememberPositions", b);
+ KGlobal::config()->sync();
+}
+
+void NoatunApp::setSaveDirectory(const QString &s)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writePathEntry("SaveDirectory", s);
+ config->sync();
+}
+
+QString NoatunApp::saveDirectory() const
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->readPathEntry("SaveDirectory", QString(getenv("HOME")));
+}
+
+QString NoatunApp::titleFormat() const
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->readEntry("TitleFormat", "$(\"[\"author\"] - \")$(title)$(\" (\"bitrate\"kbps)\")");
+}
+
+void NoatunApp::setTitleFormat(const QString &format)
+{
+ KConfig *c=KGlobal::config();
+ c->setGroup(QString::null);
+ return c->writeEntry("TitleFormat", format);
+}
+
+void NoatunApp::setClearOnStart(bool b)
+{
+ setClearOnOpen(b);
+}
+
+void NoatunApp::setClearOnOpen(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("ClearOnOpen", b);
+ config->sync();
+}
+
+void NoatunApp::setDisplayRemaining(bool b)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("DisplayRemaining", b);
+ config->sync();
+}
+
+void NoatunApp::toggleInterfaces()
+{
+ showingInterfaces ^= true;
+
+ if(showingInterfaces)
+ emit showYourself();
+ else
+ emit hideYourself();
+}
+
+void NoatunApp::showInterfaces()
+{
+ showingInterfaces = true;
+ emit showYourself();
+}
+
+void NoatunApp::hideInterfaces()
+{
+ showingInterfaces = false;
+ emit hideYourself();
+}
+
+
+int NoatunApp::pluginMenuAdd(const QString &text, const QObject *receiver, const char *member)
+{
+ return pluginActionMenu()->menuAdd(text, receiver, member);
+}
+
+void NoatunApp::pluginMenuRemove(int id)
+{
+ pluginActionMenu()->menuRemove(id);
+}
+
+NoatunStdAction::PluginActionMenu *NoatunApp::pluginActionMenu()
+{
+ if (!mPluginActionMenu)
+ mPluginActionMenu = new NoatunStdAction::PluginActionMenu(this, "menu_actions");
+ return mPluginActionMenu;
+}
+
+KPopupMenu *NoatunApp::pluginMenu()
+{
+ if(!mPluginMenu)
+ mPluginMenu = pluginActionMenu()->popupMenu();
+ return mPluginMenu;
+}
+
+int NoatunApp::newInstance()
+{
+ // TODO, this should call playlist()->handleArguments();
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ bool clear = clearOnOpen();
+ bool playme=true;
+
+ for (int i=0; i<args->count(); i++)
+ {
+ player()->openFile(args->url(i), clear, playme);
+ playme=false;
+ clear=false;
+ }
+
+ args->clear();
+ return 0;
+}
+
+void NoatunApp::preferences()
+{
+ mPref->show();
+ mPref->raise();
+}
+
+void NoatunApp::quit()
+{
+ KApplication::quit();
+}
+
+void NoatunApp::fileOpen()
+{
+ KURL::List files = KFileDialog::getOpenURLs(":mediadir", mimeTypes(), 0,
+ i18n("Select File to Play"));
+
+ if (files.count())
+ mPlayer->openFile(files, clearOnOpen(), true);
+}
+
+void NoatunApp::effectView()
+{
+ mEffectView->show();
+}
+
+void NoatunApp::equalizerView()
+{
+ mEqualizerView->show();
+}
+
+VEqualizer * NoatunApp::vequalizer()
+{
+ return d->vequalizer;
+}
+
+Effects *NoatunApp::effects() const
+{
+ return d->effects;
+}
+
+QString NoatunApp::mimeTypes()
+{
+ QString mimeTypes;
+ Arts::TraderQuery q;
+ vector<Arts::TraderOffer> *results = q.query();
+ vector<Arts::TraderOffer>::iterator i;
+
+ for (i=results->begin(); i != results->end(); i++)
+ {
+ vector<string> *prop = (*i).getProperty("MimeType");
+
+ vector<string>::iterator istr;
+ for (istr = prop->begin(); istr != prop->end(); istr++)
+ {
+ if ( !(*istr).length())
+ continue;
+
+ const char *m = (*istr).c_str();
+ if ((KServiceType::serviceType(m)) && !mimeTypes.contains(m))
+ {
+ mimeTypes += m;
+ mimeTypes += ' ';
+ }
+ }
+ delete prop;
+ }
+ delete results;
+ return mimeTypes;
+}
+
+void NoatunApp::loadPlugins()
+{
+ mLibraryLoader->loadAll();
+ // backup in case of trouble
+ if(!mLibraryLoader->playlist())
+ {
+ kdDebug(66666) << k_funcinfo << "NO playlist plugin found!" << endl;
+ //mLibraryLoader->add("splitplaylist.plugin");
+ }
+}
+
+Playlist *NoatunApp::playlist() const
+{
+ return mLibraryLoader->playlist();
+}
+
+void NoatunApp::commitData(QSessionManager &)
+{
+}
+
+void NoatunApp::saveState(QSessionManager &sm)
+{
+ QStringList restartCommand = sm.restartCommand();
+ sm.setRestartCommand( restartCommand );
+
+ KApplication::saveState(sm);
+}
+
+// Deprecated
+QImage NoatunApp::readPNG(const QString &filename)
+{
+ QImageIO file(filename, "PNG");
+ file.setGamma(0.0);
+ file.read();
+ return file.image();
+}
+
+void NoatunApp::restoreEngineState()
+{
+ KConfig* config = KGlobal::config();
+ config->setGroup(QString::null);
+ int state = config->readNumEntry("EngineState", Arts::posPlaying);
+ switch (state)
+ {
+ case Arts::posPlaying:
+ player()->play();
+ break;
+ case Arts::posPaused:
+ if (player()->isPlaying())
+ player()->playpause();
+ break;
+ case Arts::posIdle:
+ default:
+ break;
+ }
+}
+
+void NoatunApp::saveEngineState()
+{
+ KConfig* config=KGlobal::config();
+ config->setGroup(QString::null);
+ config->writeEntry("EngineState", player()->engine()->state());
+ // we don't sync here since it's done in the destructor afterwards anyway
+}
+
+#include "app.moc"
diff --git a/noatun/library/cmodule.cpp b/noatun/library/cmodule.cpp
new file mode 100644
index 00000000..a624fb64
--- /dev/null
+++ b/noatun/library/cmodule.cpp
@@ -0,0 +1,131 @@
+#include <cmodule.h>
+#include <noatun/pluginloader.h>
+#include <common.h>
+#include <noatun/app.h>
+
+#include <qpushbutton.h>
+#include <qbuttongroup.h>
+#include <qradiobutton.h>
+#include <qcheckbox.h>
+#include <qlayout.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <klistview.h>
+#include <qsplitter.h>
+#include <qlabel.h>
+#include <qdragobject.h>
+#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <kdialog.h>
+#include <klineedit.h>
+
+#include <qtextview.h>
+#include <qwhatsthis.h>
+
+#include "mimetypetree.h"
+
+/*****************************************************************
+ * General options
+ *****************************************************************/
+
+General::General(QObject *parent)
+ : CModule(i18n("General"), i18n("General Options"), "configure", parent)
+{
+ mLoopList=new QCheckBox(i18n("&Return to start of playlist on finish"), this);
+ mLoopList->setChecked(napp->loopList());
+ QWhatsThis::add(mLoopList, i18n("When the playlist is finished playing, return to the start, but do not start playing."));
+
+ mOneInstance=new QCheckBox(i18n("Allow only one &instance of Noatun"), this);
+ mOneInstance->setChecked(napp->oneInstance());
+ QWhatsThis::add(mOneInstance, i18n("Starting noatun a second time will cause it to just append items from the start to the current instance."));
+
+ mClearOnOpen = new QCheckBox(i18n("Clear playlist &when opening a file"), this);
+ mClearOnOpen->setChecked(napp->clearOnOpen());
+ QWhatsThis::add(mClearOnOpen, i18n("Opening a file with the global Open menu item will clear the playlist first."));
+
+ mFastVolume=new QCheckBox(i18n("&Use fast hardware volume control"), this);
+ mFastVolume->setChecked(napp->fastMixer());
+ QWhatsThis::add(mFastVolume, i18n("Use the hardware mixer instead of aRts'. It affects all streams, not just Noatun's, but is a little faster."));
+
+ mRemaining=new QCheckBox(i18n("Display &remaining play time"), this);
+ mRemaining->setChecked(napp->displayRemaining());
+ QWhatsThis::add(mRemaining, i18n("Counters count down towards zero, showing remaining time instead of elapsed time."));
+
+ QLabel *titleLabel=new QLabel(i18n("Title &format:"), this);
+ mTitleFormat=new KLineEdit(this);
+ titleLabel->setBuddy(mTitleFormat);
+ mTitleFormat->setText(napp->titleFormat());
+ QWhatsThis::add(mTitleFormat, i18n(
+ "Select a title to use for each file (in the playlist and user interface). "
+ "Each element such as $(title) is replaced with the property with the name "
+ "as given in the parentheses. The properties include, but are not limited to: "
+ "title, author, date, comments and album."));
+
+ QLabel *dlsaver=new QLabel(i18n("&Download folder:"), this);
+ mDlSaver=new KURLRequester(napp->saveDirectory(), this);
+ dlsaver->setBuddy(mDlSaver);
+ connect( mDlSaver, SIGNAL( openFileDialog( KURLRequester * )),
+ this, SLOT( slotRequesterClicked( KURLRequester * )));
+ QWhatsThis::add(mDlSaver, i18n("When opening a non-local file, download it to the selected folder."));
+
+ mPlayOnStartup = new QButtonGroup(1, Horizontal, i18n("Play Behavior on Startup"), this);
+ mPlayOnStartup->setExclusive(true);
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("Restore &play state"), mPlayOnStartup),
+ NoatunApp::Restore
+ );
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("Automatically play &first file"), mPlayOnStartup),
+ NoatunApp::Play
+ );
+ mPlayOnStartup->insert(
+ new QRadioButton(i18n("&Do not start playing"), mPlayOnStartup),
+ NoatunApp::DontPlay
+ );
+
+ if (QButton* b = mPlayOnStartup->find(napp->startupPlayMode()))
+ {
+ b->toggle();
+ }
+
+ QGridLayout *layout = new QGridLayout(this, 0, KDialog::spacingHint());
+ layout->setSpacing(KDialog::spacingHint());
+
+ layout->addMultiCellWidget(mLoopList, 0, 0, 0, 1);
+ layout->addMultiCellWidget(mOneInstance, 2, 2, 0, 1);
+ layout->addMultiCellWidget(mClearOnOpen, 4, 4, 0, 1);
+ layout->addMultiCellWidget(mFastVolume, 5, 5, 0, 1);
+ layout->addMultiCellWidget(mRemaining, 6, 6, 0, 1);
+
+ layout->addWidget(titleLabel, 7, 0);
+ layout->addWidget(mTitleFormat, 7, 1);
+
+ layout->addWidget(dlsaver, 8, 0);
+ layout->addWidget(mDlSaver, 8, 1);
+
+ layout->addMultiCellWidget(mPlayOnStartup, 9, 9, 0, 1);
+
+ layout->setRowStretch(10, 1);
+}
+
+
+void General::save()
+{
+ napp->setLoopList(mLoopList->isChecked());
+ napp->setOneInstance(mOneInstance->isChecked());
+ napp->setClearOnOpen(mClearOnOpen->isChecked());
+ napp->setSaveDirectory(mDlSaver->url());
+ napp->setFastMixer(mFastVolume->isChecked());
+ napp->setTitleFormat(mTitleFormat->text());
+ napp->setDisplayRemaining(mRemaining->isChecked());
+ napp->setStartupPlayMode(mPlayOnStartup->selectedId());
+}
+
+
+void General::slotRequesterClicked( KURLRequester * )
+{
+ mDlSaver->fileDialog()->setMode(
+ (KFile::Mode)(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly));
+}
+
+#include "cmodule.moc"
diff --git a/noatun/library/cmodule.h b/noatun/library/cmodule.h
new file mode 100644
index 00000000..6e0e1179
--- /dev/null
+++ b/noatun/library/cmodule.h
@@ -0,0 +1,43 @@
+#ifndef CMODULE_H
+#define CMODULE_H
+
+#include <qframe.h>
+#include <klistview.h>
+#include "noatun/pref.h"
+
+class KListView;
+class QSplitter;
+class QListViewItem;
+class NoatunLibraryInfo;
+class QTextView;
+class QButtonGroup;
+class MimeTypeTree;
+class KLineEdit;
+
+namespace Arts {class TraderOffer;}
+
+class QCheckBox;
+class KURLRequester;
+
+class General : public CModule
+{
+Q_OBJECT
+public:
+ General(QObject *parent=0);
+ virtual void save();
+
+private slots:
+ void slotRequesterClicked( KURLRequester * );
+
+private:
+ QCheckBox *mLoopList, *mOneInstance, *mRememberPositions,
+ *mClearOnOpen, *mFastVolume, *mRemaining;
+ QButtonGroup* mPlayOnStartup;
+ KURLRequester *mDlSaver;
+ KLineEdit *mTitleFormat;
+};
+
+// I'm too lazy to grep - Neil
+#include "pluginmodule.h"
+
+#endif
diff --git a/noatun/library/controls.cpp b/noatun/library/controls.cpp
new file mode 100644
index 00000000..1fb08269
--- /dev/null
+++ b/noatun/library/controls.cpp
@@ -0,0 +1,114 @@
+#include <noatun/controls.h>
+
+L33tSlider::L33tSlider(QWidget * parent, const char * name) :
+ QSlider(parent,name), pressed(false)
+{}
+L33tSlider::L33tSlider(Orientation o, QWidget * parent, const char * name) :
+ QSlider(o,parent,name), pressed(false)
+{}
+L33tSlider::L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation o, QWidget * parent, const char * name) :
+ QSlider(minValue, maxValue, pageStep, value, o, parent,name), pressed(false)
+{}
+
+bool L33tSlider::currentlyPressed() const
+{
+ return pressed;
+}
+
+void L33tSlider::setValue(int i)
+{
+ if (!pressed)
+ QSlider::setValue(i);
+}
+
+void L33tSlider::mousePressEvent(QMouseEvent*e)
+{
+ if (e->button()!=RightButton)
+ {
+ pressed=true;
+ QSlider::mousePressEvent(e);
+ }
+}
+
+void L33tSlider::mouseReleaseEvent(QMouseEvent*e)
+{
+ pressed=false;
+ QSlider::mouseReleaseEvent(e);
+ emit userChanged(value());
+}
+
+void L33tSlider::wheelEvent(QWheelEvent *e)
+{
+ QSlider::wheelEvent(e);
+ int newValue=value() /* +e->delta()/120 */;
+ if (newValue<minValue())
+ newValue=minValue();
+ else if (newValue>maxValue())
+ newValue=maxValue();
+ setValue(newValue);
+ emit userChanged(newValue);
+}
+
+
+SliderAction::SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name )
+ : KAction( text, accel, parent, name )
+{
+ m_receiver = receiver;
+ m_member = member;
+}
+
+int SliderAction::plug( QWidget *w, int index )
+{
+ if (!w->inherits("KToolBar")) return -1;
+
+ KToolBar *toolBar = (KToolBar *)w;
+ int id = KAction::getToolButtonID();
+
+ //Create it.
+ m_slider=new L33tSlider(0, 1000, 100, 0, Horizontal, toolBar);
+ m_slider->setMinimumWidth(10);
+ toolBar->insertWidget(id, 10, m_slider, index );
+
+
+ addContainer( toolBar, id );
+ connect( toolBar, SIGNAL( destroyed() ), this, SLOT( slotDestroyed() ) );
+ toolBar->setItemAutoSized( id, true );
+
+ if (w->inherits( "KToolBar" ))
+ connect(toolBar, SIGNAL(moved(KToolBar::BarPosition)), this, SLOT(toolbarMoved(KToolBar::BarPosition)));
+
+ emit plugged();
+
+ return containerCount() - 1;
+}
+
+void SliderAction::toolbarMoved(KToolBar::BarPosition)
+{
+// I wish this worked :)
+return;
+/*
+ if (pos == KToolBar::Left || pos == KToolBar::Right)
+ {
+ m_slider->setOrientation(Vertical);
+ m_slider->setFixedWidth(m_slider->height());
+ }
+ else
+ {
+ m_slider->setOrientation(Horizontal);
+ m_slider->resize(m_slider->height(), m_slider->height());
+ }
+*/
+}
+
+void SliderAction::unplug( QWidget *w )
+{
+ KToolBar *toolBar = (KToolBar *)w;
+ int idx = findContainer( w );
+
+ toolBar->removeItem( menuId( idx ) );
+ removeContainer( idx );
+}
+
+#include "controls.moc"
diff --git a/noatun/library/conversion.cpp b/noatun/library/conversion.cpp
new file mode 100644
index 00000000..791b5554
--- /dev/null
+++ b/noatun/library/conversion.cpp
@@ -0,0 +1,149 @@
+#include "noatun/conversion.h"
+
+// inherit the aRts routines
+#include <convert.h>
+
+#include <config.h>
+#include <limits.h>
+
+namespace Conversion
+{
+
+void convertMono8ToFloat(unsigned long samples, unsigned char *from, float *to)
+{
+ Arts::convert_mono_8_float(samples, from, to);
+}
+
+void interpolateMono8ToFloat(unsigned long samples, double start, double speed,
+ unsigned char *from, float *to)
+{
+ Arts::interpolate_mono_8_float(samples, start, speed, from, to);
+}
+
+void convertMono16leToFloat(unsigned long samples, unsigned char *from, float *to)
+{
+ Arts::convert_mono_16le_float(samples, from, to);
+}
+
+void interpolateMono16leToFloat(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *to)
+{
+ Arts::interpolate_mono_16le_float(samples, startpos, speed, from, to);
+}
+
+void convertStereoI8To2Float(unsigned long samples, unsigned char *from,
+ float *left, float *right)
+{
+ Arts::convert_stereo_i8_2float(samples, from, left, right);
+}
+
+
+void interpolateStereoI8To2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right)
+{
+ Arts::interpolate_stereo_i8_2float(samples, startpos, speed, from, left, right);
+}
+
+void convertStereoI16leTo2Float(unsigned long samples, unsigned char *from, float *left,
+ float *right)
+{
+ Arts::convert_stereo_i16le_2float(samples, from, left, right);
+}
+
+void interpolateStereoI16leTo2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right)
+{
+ Arts::interpolate_stereo_i16le_2float(samples, startpos, speed, from, left, right);
+}
+
+void interpolateMonoFloatToFloat(unsigned long samples, double startpos, double speed,
+ float *from, float *to)
+{
+ Arts::interpolate_mono_float_float( samples, startpos, speed, from, to);
+}
+
+void convertStereoIFloatTo2Float(unsigned long samples, float *from, float *left,
+ float *right)
+{
+ Arts::convert_stereo_ifloat_2float(samples, from, left, right);
+}
+
+void interpolateStereoIFloatTo2Float(unsigned long samples, double startpos,
+ double speed, float *from, float *left,
+ float *right)
+{
+ Arts::interpolate_stereo_ifloat_2float(samples, startpos, speed, from, left, right);
+}
+
+void convertMonoFloatTo16le(unsigned long samples, float *from, unsigned char *to)
+{
+ Arts::convert_mono_float_16le(samples, from, to);
+}
+
+void convertStereo2FloatToI16le(unsigned long samples, float *left, float *right,
+ unsigned char *to)
+{
+ Arts::convert_stereo_2float_i16le(samples, left, right, to);
+}
+
+void convertMonoFloatTo8(unsigned long samples, float *from, unsigned char *to)
+{
+ Arts::convert_mono_float_8(samples, from, to);
+}
+
+void convertStereo2FloatToI8(unsigned long samples, float *left, float *right,
+ unsigned char *to)
+{
+ Arts::convert_stereo_2float_i8(samples, left, right, to);
+}
+
+inline void toLittleEndian(unsigned long length, char *buffer)
+{
+#ifdef WORDS_BIGENDIAN
+ swapEndian(length, buffer);
+#else
+ (void)length;
+ (void)buffer;
+#endif
+}
+
+inline void toBigEndian(unsigned long length, char *buffer)
+{
+#ifndef WORDS_BIGENDIAN
+ swapEndian(length, buffer);
+#else
+ (void)length;
+ (void)buffer;
+#endif
+}
+
+void swapEndian(unsigned long length, char *buffer)
+{
+ // if you use a little-endian non intel box, and the ASM
+ // version doesn't work, it's safe to use the C version
+#ifdef __i386__
+ __asm__(
+ "shrl $1,%0\n"
+ "jz .l2\n"
+ ".l1:\n"
+ "rolw $8,(%1)\n"
+ "incl %1\n"
+ "incl %1\n"
+ "decl %0\n"
+ "jnz .l1\n"
+ ".l2:\n"
+ : : "r" (length), "r" (buffer));
+#else
+ while (length--)
+ {
+ register char c=*(buffer+1);
+ *(buffer+1)=*buffer;
+ *(buffer)=c;
+ buffer++; buffer++;
+ --length;
+ }
+#endif
+}
+
+}
+
diff --git a/noatun/library/downloader.cpp b/noatun/library/downloader.cpp
new file mode 100644
index 00000000..734f5e11
--- /dev/null
+++ b/noatun/library/downloader.cpp
@@ -0,0 +1,236 @@
+#include <noatun/downloader.h>
+#include <noatun/app.h>
+#include <assert.h>
+#include <qfile.h>
+#include <qtimer.h>
+#include <kio/job.h>
+#include <klocale.h>
+
+DownloadItem::DownloadItem()
+{
+
+}
+
+DownloadItem::~DownloadItem()
+{
+ dequeue();
+}
+
+bool DownloadItem::isDownloaded() const
+{
+ return true;
+}
+
+QString DownloadItem::localFilename() const
+{
+ return mLocalFilename;
+}
+
+void DownloadItem::setLocalFilename(const QString &filename)
+{
+ mLocalFilename=filename;
+}
+
+void DownloadItem::downloadFinished()
+{
+}
+
+void DownloadItem::downloaded(int )
+{
+}
+
+void DownloadItem::downloadTimeout()
+{
+}
+
+bool DownloadItem::enqueue(const KURL &url)
+{
+ if (url.isLocalFile())
+ {
+ setLocalFilename(url.path());
+ return false;
+ }
+ else
+ {
+ napp->downloader()->enqueue(this, url);
+ return true;
+ }
+}
+
+void DownloadItem::dequeue()
+{
+ napp->downloader()->dequeue(this);
+}
+
+
+
+
+Downloader::Downloader(QObject *parent)
+ : QObject(parent), localfile(0), current(0), mJob(0), mTimeout(0)
+{
+ mStarted=false;
+ mUnstartedQueue=new QPtrList<Downloader::QueueItem>;
+}
+
+Downloader::~Downloader()
+{
+
+}
+
+void Downloader::start()
+{
+ assert(!mStarted);
+ mStarted=true;
+ if (current)
+ emit enqueued(current->notifier, current->file);
+
+ for (QPtrListIterator<Downloader::QueueItem> i(*mUnstartedQueue); i.current(); ++i)
+ {
+ (*i)->notifier->mLocalFilename = (*i)->local;
+ mQueue.append(*i);
+ emit enqueued((*i)->notifier, (*i)->file);
+ }
+
+ delete mUnstartedQueue;
+ mUnstartedQueue=0;
+ QTimer::singleShot(0, this, SLOT(getNext()));
+}
+
+static QString nonExistantFile(const QString &file)
+{
+ if (file.right(1)=="/") return i18n("Unknown");
+ int i=0;
+ QString f(file);
+ while (QFile(f).exists())
+ {
+ i++;
+ f=file;
+ f.insert(f.findRev('.'), '_'+QString::number(i));
+ }
+ return f;
+}
+
+QString Downloader::enqueue(DownloadItem *notifier, const KURL &file)
+{
+ if (file.isLocalFile()) return 0;
+ QueueItem *i=new QueueItem;
+ i->notifier=notifier;
+ i->file=file;
+
+ if (!mStarted)
+ {
+ i->local=notifier->mLocalFilename;
+ if (!notifier->localFilename().length())
+ {
+ i->local =
+ nonExistantFile(napp->saveDirectory()+'/'+file.fileName());
+ }
+ mUnstartedQueue->append(i);
+ return i->local;
+ }
+
+
+ if (!notifier->localFilename().length())
+ {
+ notifier->mLocalFilename=
+ i->local =
+ nonExistantFile(napp->saveDirectory()+'/'+file.fileName());
+ }
+ else
+ {
+ i->local = notifier->mLocalFilename;
+ }
+
+ mQueue.append(i);
+ QTimer::singleShot(0, this, SLOT(getNext()));
+ emit enqueued(notifier, file);
+ return i->local;
+}
+
+void Downloader::dequeue(DownloadItem *notifier)
+{
+ if (current && notifier==current->notifier)
+ {
+ mJob->kill();
+ jobDone(mJob);
+ return;
+ }
+ for (QPtrListIterator<Downloader::QueueItem> i(mQueue); i.current(); ++i)
+ {
+ if ((*i)->notifier==notifier)
+ {
+ mQueue.removeRef(*i);
+ if (mStarted)
+ emit dequeued(notifier);
+ delete *i;
+ return;
+ }
+ }
+}
+
+void Downloader::getNext()
+{
+ if (current) return;
+ if (!mStarted) return;
+ if (mQueue.isEmpty()) return;
+ current=mQueue.take(0);
+
+ // open the QFile
+ localfile=new QFile(current->local);
+ localfile->open(IO_ReadWrite | IO_Append);
+
+ mJob= KIO::get(current->file, true, false);
+ connect(mJob, SIGNAL(data(KIO::Job*, const QByteArray&)), SLOT(data(KIO::Job*, const QByteArray&)));
+ connect(mJob, SIGNAL(result(KIO::Job*)), SLOT(jobDone(KIO::Job*)));
+ connect(mJob, SIGNAL(percent(KIO::Job*, unsigned long)), SLOT(percent(KIO::Job*, unsigned long)));
+
+ if (mTimeout)
+ delete mTimeout;
+ mTimeout=new QTimer(this);
+ mTimeout->start(30000, true);
+ connect(mTimeout, SIGNAL(timeout()), SLOT(giveUpWithThisDownloadServerIsRunningNT()));
+}
+
+void Downloader::data(KIO::Job *, const QByteArray &data)
+{
+ localfile->writeBlock(data);
+ localfile->flush();
+ delete mTimeout;
+ mTimeout=0;
+}
+
+void Downloader::jobDone(KIO::Job *)
+{
+ delete mTimeout;
+ mTimeout=0;
+ current->notifier->downloadFinished();
+ if (mStarted)
+ emit dequeued(current->notifier);
+ delete current;
+ current=0;
+ mJob=0;
+ getNext();
+}
+
+void Downloader::giveUpWithThisDownloadServerIsRunningNT()
+{
+ delete mTimeout;
+ mTimeout=0;
+ if (!current) return;
+ DownloadItem *old=current->notifier;
+ if (!old) return;
+ old->downloadTimeout();
+ current=0;
+ mJob=0;
+ delete old;
+ getNext();
+}
+
+void Downloader::percent( KIO::Job *, unsigned long percent)
+{
+ if (current && current->notifier)
+ current->notifier->downloaded((int)percent);
+}
+
+#include "downloader.moc"
+
diff --git a/noatun/library/effects.cpp b/noatun/library/effects.cpp
new file mode 100644
index 00000000..3c6c601e
--- /dev/null
+++ b/noatun/library/effects.cpp
@@ -0,0 +1,285 @@
+#include "effects.h"
+#include "engine.h"
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <app.h>
+#include <player.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <qlayout.h>
+
+#include <config.h>
+
+#define HAS_ARTSVERSION_H
+
+#ifdef HAS_ARTSVERSION_H
+#include <artsgui.h>
+#include <kartswidget.h>
+#endif
+
+#define engine napp->player()->engine()
+#define server (*(engine->server()))
+#define stack (*engine->effectStack())
+
+using namespace std;
+using namespace Arts;
+
+class EffectConfigWidget : public QWidget
+{
+public:
+ EffectConfigWidget(Effect *e, QWidget *parent=0)
+ : QWidget(parent), mEf(e)
+ {}
+
+ virtual ~EffectConfigWidget()
+ {
+ mEf->mConfig=0;
+ }
+
+private:
+ Effect *mEf;
+};
+
+
+Effect::Effect(const char *name)
+ : mId(0), mName(name), mConfig(0)
+{
+ mEffect=new StereoEffect;
+ *mEffect=DynamicCast(server.createObject(std::string(name)));
+ napp->effects()->mItems.append(this);
+}
+
+long Effect::id() const
+{
+ return mId;
+}
+
+StereoEffect *Effect::effect() const
+{
+ return mEffect;
+}
+
+Effect *Effect::after() const
+{
+ QPtrList<Effect> effects=napp->effects()->effects();
+ QPtrListIterator<Effect> i(effects);
+ for(; i.current(); ++i)
+ if ((*i)->id()==mId)
+ {
+ ++i;
+ if (*i)
+ return *i;
+ }
+
+ return 0;
+}
+
+Effect *Effect::before() const
+{
+ QPtrList<Effect> effects=napp->effects()->effects();
+ QPtrListIterator<Effect> i(effects);
+ for(; i.current(); ++i)
+ if ((*i)->id()==mId)
+ {
+ --i;
+ if (*i)
+ return *i;
+ }
+
+ return 0;
+}
+
+QCString Effect::name() const
+{
+ return mName;
+}
+
+QString Effect::title() const
+{
+ return clean(mName);
+}
+
+QString Effect::clean(const QCString &name)
+{
+ int pos=name.findRev("::");
+ if (pos>0)
+ return name.right(name.length()-pos-2);
+ return name;
+}
+
+bool Effect::isNull() const
+{
+ return effect()->isNull();
+}
+
+QWidget *Effect::configure(bool /*friendly*/)
+{
+#ifdef HAS_ARTSVERSION_H
+ if (mConfig) return mConfig;
+ if (!configurable()) return 0;
+
+ GenericGuiFactory factory;
+ Widget gui = factory.createGui(*mEffect);
+
+ if(!gui.isNull())
+ {
+ mConfig=new EffectConfigWidget(this);
+ mConfig->setCaption(title());
+
+ QBoxLayout *l=new QHBoxLayout(mConfig);
+ l->add(new KArtsWidget(gui, mConfig));
+ l->freeze();
+ }
+
+ return mConfig;
+#else
+ return 0;
+#endif
+}
+
+bool Effect::configurable() const
+{
+#ifdef HAS_ARTSVERSION_H
+ TraderQuery query;
+ query.supports("Interface","Arts::GuiFactory");
+ query.supports("CanCreate", mEffect->_interfaceName());
+
+ vector<TraderOffer> *queryResults = query.query();
+ bool yes= queryResults->size();
+ delete queryResults;
+
+ return yes;
+#else
+ return 0;
+#endif
+}
+
+Effect::~Effect()
+{
+ delete mConfig;
+ napp->effects()->remove(this, false);
+ emit napp->effects()->deleting(this);
+ delete mEffect;
+ napp->effects()->mItems.removeRef(this);
+}
+
+
+Effects::Effects()
+{
+ mItems.setAutoDelete(false);
+}
+
+bool Effects::insert(const Effect *after, Effect *item)
+{
+ if (!item) return false;
+ if (item->id()) return false;
+ if (item->isNull()) return false;
+ long i;
+ item->effect()->start();
+
+ if (!after)
+ i=stack.insertTop(*item->effect(), (const char*)item->name());
+ else
+ i=stack.insertAfter(after->id(), *item->effect(), (const char*)item->name());
+ if (!i)
+ {
+ item->effect()->stop();
+ return false;
+ }
+
+ item->mId=i;
+ emit added(item);
+ return true;
+}
+
+bool Effects::append(Effect *item)
+{
+ if (!item) return false;
+ if (item->id()) return false;
+ if (item->isNull()) return false;
+
+ item->effect()->start();
+ item->mId=stack.insertBottom(*item->effect(), (const char*)item->name());
+ if (!item->mId)
+ {
+ item->effect()->stop();
+ return false;
+ }
+ emit added(item);
+ return true;
+}
+
+void Effects::move(const Effect *after, Effect *item)
+{
+ if (!item) return;
+ if (!item->id()) return;
+ long id=after ? after->id() : 0;
+ stack.move(id, item->id());
+ emit moved(item);
+}
+
+void Effects::remove(Effect *item, bool del)
+{
+ if (!item) return;
+ if (!item->id()) return;
+
+ stack.remove(item->id());
+ item->effect()->stop();
+ item->mId=0;
+ removed(item);
+
+ if (del)
+ delete item;
+}
+
+void Effects::removeAll(bool del)
+{
+ for (QPtrListIterator<Effect> i(mItems); i.current(); ++i)
+ if ((*i)->id())
+ remove(*i, del);
+}
+
+QStrList Effects::available() const
+{
+ QStrList val;
+ Arts::TraderQuery query;
+ query.supports("Interface", "Arts::StereoEffect");
+ query.supports("Interface", "Arts::SynthModule");
+ query.supports("Use", "directly");
+ vector<Arts::TraderOffer> *offers = query.query();
+ for (vector<Arts::TraderOffer>::iterator i=offers->begin(); i!=offers->end(); i++)
+ {
+ Arts::TraderOffer &offer=*i;
+ QCString name = offer.interfaceName().c_str();
+ val.append(name);
+ }
+ delete offers;
+ return val;
+}
+
+Effect *Effects::findId(long id) const
+{
+ for (QPtrListIterator<Effect> i(mItems); i.current(); ++i)
+ if ((*i)->id()==id)
+ return *i;
+ return 0;
+}
+
+QPtrList<Effect> Effects::effects() const
+{
+ vector<long> *items=stack.effectList();
+ QPtrList<Effect> effects;
+ for (vector<long>::iterator i=items->begin();i!=items->end();i++)
+ if (Effect *e=findId(*i))
+ effects.append(e);
+
+ delete items;
+ return effects;
+}
+
+#undef server
+#undef stack
+#undef engine
+
+#include "effects.moc"
diff --git a/noatun/library/effectview.cpp b/noatun/library/effectview.cpp
new file mode 100644
index 00000000..73af1fc2
--- /dev/null
+++ b/noatun/library/effectview.cpp
@@ -0,0 +1,284 @@
+// Copyright (c) 2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "effects.h"
+#include "effectview.h"
+#include "app.h"
+
+#include <kcombobox.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpushbutton.h>
+#include <qtextedit.h>
+#include <qtoolbutton.h>
+#include <qvgroupbox.h>
+#include <qwhatsthis.h>
+
+class EffectListItem : public QListViewItem
+{
+public:
+ EffectListItem(QListView *parent, QListViewItem *after, Effect *e)
+ : QListViewItem(parent, after, e->title()), mEffect(e)
+ {
+ }
+
+ Effect *effect() const { return mEffect; }
+
+private:
+ Effect *mEffect;
+};
+
+EffectList::EffectList(QWidget *parent)
+ : KListView(parent)
+{
+}
+
+bool EffectList::acceptDrag(QDropEvent *event) const
+{
+ return QCString(event->format()) == "application/x-noatun-effectdrag";
+}
+
+QDragObject *EffectList::dragObject() const
+{
+ if (!currentItem()) return 0;
+ return new QStoredDrag("application/x-noatun-effectdrag", (QWidget*)this);
+}
+
+EffectView::EffectView()
+ : KDialogBase((QWidget*)0L, 0, false, i18n("Effects"), Help | Close, Close, true)
+ , initialized(false)
+{
+}
+
+void EffectView::show()
+{
+ init();
+ KDialogBase::show();
+}
+
+namespace
+{
+QToolButton *newButton(const QIconSet &iconSet, const QString &textLabel, QObject *receiver, const char * slot, QWidget *parent, const char *name = 0)
+{
+ QToolButton *button = new QToolButton(parent, name);
+ button->setIconSet(iconSet);
+ button->setTextLabel(textLabel, true);
+ QObject::connect(button, SIGNAL(clicked()), receiver, slot);
+ button->setFixedSize(QSize(22, 22));
+ return button;
+}
+}
+
+void EffectView::init(void)
+{
+ if(initialized) return;
+ initialized = true;
+
+ setCaption(i18n("Effects - Noatun"));
+ setIcon(SmallIcon("effect"));
+
+ // Create widgets and layouts
+ QFrame *box = makeMainWidget();
+ QVBoxLayout *boxLayout = new QVBoxLayout(box, 0, KDialog::spacingHint());
+
+ // Available
+ QVGroupBox *topBox = new QVGroupBox(i18n("Available Effects"), box);
+ topBox->setInsideSpacing(KDialog::spacingHint());
+
+ QFrame *topTopFrame = new QFrame(topBox);
+ QHBoxLayout *topTopLayout = new QHBoxLayout(topTopFrame, 0, KDialog::spacingHint());
+ topTopLayout->setAutoAdd(true);
+ available = new KComboBox(false, topTopFrame);
+ QToolButton *add = newButton(BarIconSet("down", KIcon::SizeSmall), i18n("Add"), this, SLOT(addEffect()), topTopFrame);
+
+ // Active
+ QHGroupBox *bottomBox = new QHGroupBox(i18n("Active Effects"), box);
+ bottomBox->setInsideSpacing(KDialog::spacingHint());
+
+ active = new EffectList(bottomBox);
+
+ boxLayout->addWidget(topBox);
+ boxLayout->addWidget(bottomBox);
+
+ // Fill and configure widgets
+ available->insertStrList(napp->effects()->available());
+
+ active->setAcceptDrops(true);
+ active->addColumn("");
+ active->header()->hide();
+ active->setSorting(-1);
+ active->setDropVisualizer(true);
+ active->setItemsMovable(true);
+ active->setSelectionMode(QListView::Single);
+ active->setDragEnabled(true);
+ connect(active, SIGNAL(dropped(QDropEvent *, QListViewItem *)), SLOT(activeDrop(QDropEvent *, QListViewItem *)));
+
+ // when a new effect is added
+ connect(napp->effects(), SIGNAL(added(Effect *)), SLOT(added(Effect *)));
+ connect(napp->effects(), SIGNAL(removed(Effect *)), SLOT(removed(Effect *)));
+ connect(napp->effects(), SIGNAL(moved(Effect *)), SLOT(moved(Effect *)));
+
+ available->setCurrentItem(0);
+
+ connect(active, SIGNAL(currentChanged(QListViewItem *)), SLOT(activeChanged(QListViewItem *)));
+ active->setCurrentItem(0);
+
+ // the buttons
+ QFrame *bottomLeftFrame = new QFrame(bottomBox);
+ QVBoxLayout *bottomLeftLayout = new QVBoxLayout(bottomLeftFrame, 0, KDialog::spacingHint());
+ up = newButton(BarIconSet("up", KIcon::SizeSmall), i18n("Up"), this, SLOT(moveUp()), bottomLeftFrame);
+ down = newButton(BarIconSet("down", KIcon::SizeSmall), i18n("Down"), this, SLOT(moveDown()), bottomLeftFrame);
+ configure = newButton(BarIconSet("configure", KIcon::SizeSmall), i18n("Configure"), this, SLOT(configureEffect()), bottomLeftFrame);
+ remove = newButton(BarIconSet("remove", KIcon::SizeSmall), i18n("Remove"), this, SLOT(removeEffect()), bottomLeftFrame);
+ bottomLeftLayout->addWidget(up);
+ bottomLeftLayout->addWidget(down);
+ bottomLeftLayout->addWidget(configure);
+ bottomLeftLayout->addWidget(remove);
+ bottomLeftLayout->addStretch();
+
+
+ activeChanged(active->currentItem());
+
+ // Inline documentation
+ QWhatsThis::add(available, i18n("This shows all available effects.\n\nTo activate a plugin, drag files from here to the active pane on the right."));
+ QWhatsThis::add(add, i18n("This will place the selected effect at the bottom of your chain."));
+ QWhatsThis::add(active, i18n("This shows your effect chain. Noatun supports an unlimited amount of effects in any order. You can even have the same effect twice.\n\nDrag the items to and from here to add and remove them, respectively. You may also reorder them with drag-and-drop. These actions can also be performed with the buttons to the right."));
+ QWhatsThis::add(up, i18n("Move the currently selected effect up in the chain."));
+ QWhatsThis::add(down, i18n("Move the currently selected effect down in the chain."));
+ QWhatsThis::add(configure, i18n("Configure the currently selected effect.\n\nYou can change things such as intensity from here."));
+ QWhatsThis::add(remove, i18n("This will remove the selected effect from your chain."));
+
+ resize(300, 400);
+}
+
+void EffectView::activeDrop(QDropEvent *, QListViewItem *pafter)
+{
+ EffectListItem *after(static_cast<EffectListItem*>(pafter));
+ napp->effects()->move(after ? after->effect() : 0,
+ static_cast<EffectListItem*>(active->currentItem())->effect());
+ activeChanged(active->currentItem());
+}
+
+QListViewItem *EffectView::toListItem(Effect *e) const
+{
+ for(QListViewItem *i = active->firstChild(); i; i = i->itemBelow())
+ if(static_cast<EffectListItem*>(i)->effect() == e)
+ return i;
+ return 0;
+}
+
+void EffectView::added(Effect *item)
+{
+ new EffectListItem(active, toListItem(item->before()), item);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moved(Effect *item)
+{
+ delete toListItem(item);
+ added(item);
+}
+
+void EffectView::removed(Effect *item)
+{
+ delete toListItem(item);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moveDown()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+
+ if(e->after())
+ napp->effects()->move(e->after(), e);
+ active->setCurrentItem(toListItem(e));
+ active->setSelected(toListItem(e), true);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::moveUp()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ if (e->before())
+ {
+ if (e->before()->before())
+ napp->effects()->move(e->before()->before(), e);
+ else
+ napp->effects()->move(0, e);
+ }
+ active->setCurrentItem(toListItem(e));
+ active->setSelected(toListItem(e), true);
+ activeChanged(active->currentItem());
+}
+
+void EffectView::removeEffect()
+{
+ EffectListItem *item = static_cast<EffectListItem*>(active->currentItem());
+ napp->effects()->remove(item->effect());
+ activeChanged(active->currentItem());
+}
+
+void EffectView::addEffect()
+{
+ // local8Bit() and arts makes me nervous
+ napp->effects()->append(new Effect(available->currentText().local8Bit()));
+ activeChanged(active->currentItem());
+}
+
+void EffectView::configureEffect()
+{
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ if(!e) return;
+
+ QWidget *c = e->configure();
+ if(c) c->show();
+}
+
+void EffectView::activeChanged(QListViewItem *i)
+{
+ if(i)
+ {
+ up->setEnabled(i->itemAbove());
+ down->setEnabled(i->itemBelow());
+ remove->setEnabled(true);
+
+ Effect *e = static_cast<EffectListItem*>(active->currentItem())->effect();
+ configure->setEnabled(e->configurable());
+ }
+ else
+ {
+ up->setEnabled(false);
+ down->setEnabled(false);
+ remove->setEnabled(false);
+ configure->setEnabled(false);
+ }
+}
+
+#include "effectview.moc"
diff --git a/noatun/library/effectview.h b/noatun/library/effectview.h
new file mode 100644
index 00000000..1cdb1907
--- /dev/null
+++ b/noatun/library/effectview.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2001 Neil Stevens <neil@qualityassistant.com>
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef EFFECTVIEW_H
+#define EFFECTVIEW_H
+
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class EffectList;
+class Effect;
+class KComboBox;
+class QToolButton;
+
+class EffectView : public KDialogBase
+{
+Q_OBJECT
+public:
+ EffectView();
+
+ virtual void show();
+
+public slots:
+ void added(Effect *);
+ void removed(Effect *);
+ void moved(Effect *);
+
+ // buttons
+ void moveDown();
+ void moveUp();
+ void removeEffect();
+ void addEffect();
+ void configureEffect();
+
+ void activeChanged(QListViewItem *);
+
+protected slots:
+ void activeDrop(QDropEvent *, QListViewItem *);
+
+private:
+ QListViewItem *toListItem(Effect *) const;
+
+ void init(void);
+ bool initialized;
+
+ KComboBox *available;
+
+ QToolButton *up, *down, *configure, *remove;
+
+ EffectList *active;
+};
+
+class EffectList : public KListView
+{
+Q_OBJECT
+public:
+ EffectList(QWidget *parent);
+ virtual bool acceptDrag(QDropEvent *) const;
+ virtual QDragObject *dragObject() const;
+};
+
+#endif
diff --git a/noatun/library/engine.cpp b/noatun/library/engine.cpp
new file mode 100644
index 00000000..e937fa7f
--- /dev/null
+++ b/noatun/library/engine.cpp
@@ -0,0 +1,589 @@
+// $Id$
+
+#include <noatun/engine.h>
+#include <noatun/equalizer.h>
+#include <noatun/player.h>
+#include <noatun/plugin.h>
+#include <noatun/effects.h>
+#include "titleproxy.h"
+
+#include <string.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <sys/wait.h>
+#include <kplayobject.h>
+#include <kplayobjectfactory.h>
+
+#include <dynamicrequest.h>
+#include <soundserver.h>
+#include <kmedia2.h>
+#include <flowsystem.h>
+#include <noatunarts.h>
+#include <connect.h>
+#include <cpuinfo.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+
+
+static Arts::PlayObject nullPO() { return Arts::PlayObject::null(); }
+
+#define HARDWARE_VOLUME
+
+#if defined(__osf__)
+#undef HARDWARE_VOLUME
+#elif defined(__linux__)
+#include <sys/soundcard.h>
+#elif defined(__FreeBSD__)
+#include <sys/soundcard.h>
+#elif defined(__NetBSD__)
+#include <soundcard.h>
+#elif defined(___SOMETHING_UNKNOWN__)
+#include <sys/soundcard.h>
+#elif defined(_UNIXWARE)
+#include <sys/soundcard.h>
+#else
+#undef HARDWARE_VOLUME
+#endif
+
+using namespace std;
+
+namespace VolumeControls
+{
+ struct VC
+ {
+ virtual ~VC() {}
+ virtual void setVolume(int percent)=0;
+ virtual int volume() const =0;
+ };
+
+#ifdef HARDWARE_VOLUME
+
+ struct Hardware : public VC
+ {
+ Hardware(Engine *)
+ {
+ if ((fd=::open( "/dev/mixer", O_RDWR)) < 0)
+ {
+ return;
+ }
+ else
+ {
+#define ERROR { fd=-1; return; }
+ int devmask, recmask, i_recsrc, stereodevs;
+ // Mixer is open. Now define properties
+ if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_RECSRC, &i_recsrc) == -1) ERROR
+ if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs) == -1) ERROR
+ if (!devmask) ERROR
+#undef ERROR
+ }
+
+ }
+
+ virtual ~Hardware() { ::close(fd); }
+
+ virtual void setVolume(int percent)
+ {
+ percent=percent+(percent<<8);
+ ioctl(fd, MIXER_WRITE(4), &percent);
+ }
+
+ virtual int volume() const
+ {
+ int volume, left, right;
+ left=100;
+ if (ioctl(fd, MIXER_READ(4), &volume) != -1)
+ {
+ left=volume & 0x7f;
+ right=(volume>>8) & 0x7f;
+ left=(left+right)/2;
+ }
+ return left;
+ }
+ private:
+ int fd;
+ };
+#endif
+
+ struct SoftwareSSE : public VC
+ {
+ SoftwareSSE(Engine *e) : mVolume(100)
+ {
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControlSSE"));
+
+ if(volumeControl.isNull())
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
+
+ volumeControl.start();
+
+ id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
+ }
+
+ virtual void setVolume(int percent)
+ {
+ mVolume=percent;
+ float vol = pow(2.0, ((400 - (100-percent)*12)/200.0))/4.0;
+ if (percent == 0) vol = 0.0;
+ if (!volumeControl.isNull())
+ volumeControl.percent(vol);
+ }
+
+ virtual int volume() const
+ {
+ return mVolume;
+ }
+
+ private:
+ Noatun::StereoVolumeControlSSE volumeControl;
+ long id;
+ int mVolume;
+ };
+
+ struct Software : public VC
+ {
+ Software(Engine *e) : mVolume(100)
+ {
+ volumeControl = Arts::DynamicCast(e->server()->createObject("Noatun::StereoVolumeControl"));
+ volumeControl.start();
+
+ id=e->globalEffectStack()->insertBottom(volumeControl,"Volume Control");
+ }
+
+ virtual void setVolume(int percent)
+ {
+ mVolume=percent;
+ if (!volumeControl.isNull())
+ volumeControl.percent((float)percent/100.0);
+ }
+
+ virtual int volume() const
+ {
+ return mVolume;
+ }
+
+ private:
+ Noatun::StereoVolumeControl volumeControl;
+ long id;
+ int mVolume;
+ };
+
+ static VC *volumeControl(Engine *e)
+ {
+#ifdef HARDWARE_VOLUME
+ if (napp->fastMixer())
+ return new Hardware(e);
+ else
+ {
+#endif
+ if (!getenv("NO_SSE") && Arts::CpuInfo::flags()&Arts::CpuInfo::CpuSSE)
+ return new SoftwareSSE(e);
+ else
+ return new Software(e);
+
+#ifdef HARDWARE_VOLUME
+ }
+#endif
+ }
+}
+
+
+class Engine::EnginePrivate
+{
+public:
+ EnginePrivate()
+ : playobj(0),
+ server(Arts::SoundServerV2::null()),
+ globalEffectStack(Noatun::StereoEffectStack::null()),
+ effectsStack(Noatun::StereoEffectStack::null()),
+ visStack(Noatun::StereoEffectStack::null()),
+ volumeControl(0), session(Noatun::Session::null()),
+ pProxy(0)
+ {
+ }
+
+ ~EnginePrivate()
+ {
+ visStack=Noatun::StereoEffectStack::null();
+ }
+
+ KDE::PlayObject *playobj;
+ Arts::SoundServerV2 server;
+ Arts::Synth_AMAN_PLAY amanPlay;
+
+ // globalEffectStack
+ // |- effectsStack
+ // |- Effects...
+ // |- visStack
+ // |- Visualizations
+ // |- Volume Control
+ //
+ Noatun::StereoEffectStack globalEffectStack;
+ Noatun::StereoEffectStack effectsStack;
+ Noatun::StereoEffectStack visStack;
+ Noatun::Equalizer equalizer;
+
+ int volumeID;
+ VolumeControls::VC *volumeControl;
+ Noatun::Session session;
+ TitleProxy::Proxy *pProxy;
+};
+
+Arts::SoundServerV2 *Engine::server() const { return &d->server;}
+Arts::PlayObject Engine::playObject() const { return d->playobj ? d->playobj->object() : nullPO(); }
+Arts::SoundServerV2 *Engine::simpleSoundServer() const { return &d->server; }
+Noatun::StereoEffectStack *Engine::effectStack() const { return &d->effectsStack; }
+Noatun::StereoEffectStack *Engine::visualizationStack() const { return &d->visStack; }
+Noatun::StereoEffectStack *Engine::globalEffectStack() const { return &d->globalEffectStack; }
+Noatun::Equalizer *Engine::equalizer() const { return &d->equalizer; }
+Noatun::Session *Engine::session() const { return &d->session; }
+
+Engine::Engine(QObject *parent) : QObject(parent, "Engine"), mPlay(false)
+{
+ d=new EnginePrivate;
+ // Connect to aRts
+ if (!initArts())
+ {
+ KMessageBox::error(0, i18n("There was an error communicating to the aRts daemon."), i18n("aRts error"));
+ exit(0);
+ }
+
+}
+
+Engine::~Engine()
+{
+ stop();
+ delete d->volumeControl;
+ d->server=Arts::SoundServerV2::null();
+ delete d;
+}
+
+void Engine::setInitialized()
+{
+ mPlay=true;
+}
+
+bool Engine::initialized() const
+{
+ return mPlay;
+}
+
+bool Engine::open(const PlaylistItem &file)
+{
+ if(!initArts())
+ return false;
+
+ d->playobj = 0;
+
+ KDE::PlayObjectFactory factory(d->server);
+
+ if (file.isProperty("stream_") && file.url().protocol() == "http")
+ {
+ deleteProxy();
+ d->pProxy = new TitleProxy::Proxy(KURL(file.property("stream_")));
+ d->playobj = factory.createPlayObject(d->pProxy->proxyUrl(), false);
+
+ connect(d->playobj, SIGNAL(destroyed()), this, SLOT(deleteProxy()));
+ connect(
+ d->pProxy, SIGNAL(
+ metaData(
+ const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &)),
+ this, SIGNAL(
+ receivedStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &))
+ );
+ connect(d->pProxy, SIGNAL(proxyError()), this, SLOT(slotProxyError()));
+ }
+ else
+ {
+ d->playobj = factory.createPlayObject(file.url(), false);
+ }
+
+ if (!d->playobj || d->playobj->isNull())
+ {
+ kdDebug(66666) << k_funcinfo <<
+ "No playobject for '" << file.url().prettyURL() << "'" << endl;
+ delete d->playobj;
+ d->playobj=0;
+ emit playingFailed();
+ return false;
+ }
+
+ if ( !d->playobj->object().isNull() )
+ {
+ connectPlayObject();
+ }
+ else
+ {
+ connect( d->playobj, SIGNAL( playObjectCreated() ), this, SLOT( connectPlayObject() ) );
+ }
+
+ if (mPlay)
+ d->playobj->play();
+
+ return true;
+}
+
+void Engine::slotProxyError()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ emit playingFailed();
+ deleteProxy();
+}
+
+void Engine::deleteProxy()
+{
+ delete d->pProxy;
+ d->pProxy = 0;
+}
+
+void Engine::connectPlayObject()
+{
+ if (d->playobj->object().isNull())
+ {
+ emit playingFailed();
+ return;
+ }
+ d->playobj->object()._node()->start();
+
+ // TODO: check for existence of left & right streams
+ Arts::connect(d->playobj->object(),"left",d->globalEffectStack,"inleft");
+ Arts::connect(d->playobj->object(),"right",d->globalEffectStack,"inright");
+
+ emit aboutToPlay();
+}
+
+bool Engine::play()
+{
+ if (!mPlay) return true;
+ if(!d->playobj)
+ return false;
+ d->playobj->play();
+ return true;
+}
+
+void Engine::pause()
+{
+ d->playobj->pause();
+}
+
+void Engine::stop()
+{
+ if(!d->playobj) return;
+
+ d->playobj->halt();
+ delete d->playobj;
+ d->playobj=0;
+}
+
+void Engine::seek(int msec) // pass time in msecs
+{
+ if(!d->playobj) return;
+
+ Arts::poTime t;
+
+ t.custom = 0.0;
+ t.ms = (long) msec % 1000;
+ t.seconds = (long) (msec - t.ms) / 1000;
+
+ if(d->playobj)
+ d->playobj->seek(t);
+}
+
+int Engine::position()
+{
+ if(!d->playobj) return -1;
+
+ Arts::poTime time(d->playobj->currentTime());
+ return (int)(time.ms + (time.seconds*1000)); // return position in milliseconds
+}
+
+int Engine::length()
+{
+ if(!d->playobj) return -1;
+ if (!(d->playobj->capabilities() & Arts::capSeek))
+ return -1;
+
+ Arts::poTime time(d->playobj->overallTime());
+ return (int)(time.ms + (time.seconds*1000)); // return track-length in milliseconds
+}
+
+int Engine::state()
+{
+ if(d->playobj)
+ return d->playobj->state();
+ else
+ return Arts::posIdle;
+}
+
+void Engine::setVolume(int percent)
+{
+ if (percent>100)
+ percent=100;
+ if (percent<0)
+ percent=0;
+ d->volumeControl->setVolume(percent);
+}
+
+int Engine::volume() const
+{
+ return d->volumeControl->volume();
+}
+
+void Engine::useHardwareMixer(bool)
+{
+ delete d->volumeControl;
+ d->volumeControl=VolumeControls::volumeControl(this);
+}
+
+bool Engine::initArts()
+{
+ if ( d->server.isNull() || d->server.error() )
+ {
+ d->server = Arts::Reference("global:Arts_SoundServerV2");
+ int volume = d->volumeControl ? d->volumeControl->volume() : -1;
+ delete d->volumeControl;
+ d->volumeControl=0;
+
+ if( d->server.isNull() || d->server.error() )
+ {
+ // aRts seems not to be running, let's try to run it
+ // First, let's read the configuration as in kcmarts
+ KConfig config("kcmartsrc");
+ QCString cmdline;
+
+ config.setGroup("Arts");
+
+ bool rt = config.readBoolEntry("StartRealtime",false);
+ bool x11Comm = config.readBoolEntry("X11GlobalComm",false);
+
+ // put the value of x11Comm into .mcoprc
+ {
+ KConfig X11CommConfig(QDir::homeDirPath()+"/.mcoprc");
+
+ if(x11Comm)
+ X11CommConfig.writeEntry("GlobalComm", "Arts::X11GlobalComm");
+ else
+ X11CommConfig.writeEntry("GlobalComm", "Arts::TmpGlobalComm");
+
+ X11CommConfig.sync();
+ }
+
+ cmdline = QFile::encodeName(KStandardDirs::findExe(QString::fromLatin1("kdeinit_wrapper")));
+ cmdline += " ";
+
+ if (rt)
+ cmdline += QFile::encodeName(KStandardDirs::findExe(
+ QString::fromLatin1("artswrapper")));
+ else
+ cmdline += QFile::encodeName(KStandardDirs::findExe(
+ QString::fromLatin1("artsd")));
+
+ cmdline += " ";
+ cmdline += config.readEntry("Arguments","-F 10 -S 4096 -s 60 -m artsmessage -l 3 -f").utf8();
+
+ int status=::system(cmdline);
+
+ if ( status!=-1 && WIFEXITED(status) )
+ {
+ // We could have a race-condition here.
+ // The correct way to do it is to make artsd fork-and-exit
+ // after starting to listen to connections (and running artsd
+ // directly instead of using kdeinit), but this is better
+ // than nothing.
+ int time = 0;
+ do
+ {
+ // every time it fails, we should wait a little longer
+ // between tries
+ ::sleep(1+time/2);
+ d->server = Arts::Reference("global:Arts_SoundServerV2");
+ } while(++time < 5 && (d->server.isNull()));
+ }
+ }
+
+ if ( !d->server.isNull() )
+ {
+ d->amanPlay = Arts::DynamicCast(
+ d->server.createObject("Arts::Synth_AMAN_PLAY")
+ );
+
+ if (d->amanPlay.isNull())
+ goto crapOut;
+
+ d->session = Arts::DynamicCast(
+ d->server.createObject("Noatun::Session"));
+ if (d->session.isNull())
+ {
+ kdWarning() << "Couldn't instanciate artsobject Noatun::Session. "
+ << "(This is normally caused by a broken package or "
+ << "compiling kdemultimedia in a --prefix different "
+ << "from arts. It may also be from two conflicting "
+ << "packages, so uninstall every arts/artsd package "
+ << "you have installed and try again." << endl;
+ goto crapOut;
+ }
+
+ d->amanPlay.title("noatun");
+ d->amanPlay.autoRestoreID("noatun");
+ d->amanPlay.start();
+
+ d->globalEffectStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));;
+ d->globalEffectStack.start();
+ Arts::connect(d->globalEffectStack,d->amanPlay);
+
+ d->effectsStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));
+ d->effectsStack.start();
+ d->globalEffectStack.insertBottom(d->effectsStack, "Effects Stack");
+
+ d->equalizer=Arts::DynamicCast(d->server.createObject("Noatun::Equalizer"));
+ d->equalizer.start();
+ d->globalEffectStack.insertBottom(d->equalizer, "Equalizer");
+
+ if (napp->equalizer())
+ {
+ napp->equalizer()->update(true);
+ napp->equalizer()->setPreamp(napp->equalizer()->preamp());
+ napp->equalizer()->setEnabled(napp->equalizer()->isEnabled());
+ }
+
+ d->visStack=Arts::DynamicCast(
+ d->server.createObject("Noatun::StereoEffectStack"));
+ d->visStack.start();
+
+ d->globalEffectStack.insertBottom(d->visStack, "Visualization Stack");
+ d->volumeControl=VolumeControls::volumeControl(this);
+ if (volume != -1)
+ d->volumeControl->setVolume(volume);
+ }
+ else
+ {
+ crapOut:
+ KMessageBox::error( 0, i18n("Connecting/starting aRts soundserver failed. Make sure that artsd is configured properly."));
+ exit(1);
+ }
+ }
+
+ d->playobj=0;
+ emit artsError();
+ return true;
+}
+
+
+#include "engine.moc"
+
diff --git a/noatun/library/equalizer.cpp b/noatun/library/equalizer.cpp
new file mode 100644
index 00000000..2ee208db
--- /dev/null
+++ b/noatun/library/equalizer.cpp
@@ -0,0 +1,339 @@
+#include "equalizer.h"
+#include "engine.h"
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <app.h>
+#include <player.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <ktempfile.h>
+#include <qdom.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <qtextstream.h>
+#include <math.h>
+#include <kconfig.h>
+#include <klocale.h>
+#include "ksaver.h"
+
+#define EQ napp->equalizer()
+#define VEQ napp->vequalizer()
+
+struct OldEqCruft
+{
+ VInterpolation *interpolated;
+
+};
+
+static OldEqCruft *eqCruft=0;
+
+Preset::Preset(const QString &)
+{ } // unused
+
+Preset::Preset(VPreset p)
+{
+ VPreset *copy = new VPreset(p);
+
+ // isn't this horrible? :)
+ mFile = QString::number((unsigned long)copy);
+}
+
+Preset::Preset()
+{ } // unused
+
+VPreset &Preset::vpreset() const
+{
+ unsigned long addr = mFile.toULong();
+ return *(VPreset*)addr;
+}
+
+QString Preset::name() const
+{
+ return vpreset().name();
+}
+
+bool Preset::setName(const QString &name)
+{
+ return vpreset().setName(name);
+}
+
+bool Preset::save() const
+{
+ vpreset().save();
+ return true;
+}
+
+bool Preset::load()
+{
+ vpreset().load();
+ return true;
+}
+
+void Preset::remove()
+{
+ vpreset().remove();
+}
+
+QString Preset::file() const
+{
+ return vpreset().file();
+}
+
+
+Band::Band(int, int)
+{
+ // Never used
+}
+
+Band::Band(int num)
+ : mNum(num)
+{
+
+}
+
+Band::~Band()
+{}
+
+int Band::level()
+{
+ return eqCruft->interpolated->band(mNum).level();
+}
+
+void Band::setLevel(int l)
+{
+ eqCruft->interpolated->band(mNum).setLevel(l);
+}
+
+int Band::start() const
+{
+ return eqCruft->interpolated->band(mNum).start();
+}
+
+int Band::end() const
+{
+ return eqCruft->interpolated->band(mNum).end();
+}
+
+int Band::center() const
+{
+ return eqCruft->interpolated->band(mNum).center();
+}
+
+QString Band::formatStart(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).formatStart(withHz);
+}
+
+QString Band::formatEnd(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).formatEnd(withHz);
+}
+
+QString Band::format(bool withHz) const
+{
+ return eqCruft->interpolated->band(mNum).format(withHz);
+}
+
+
+Equalizer::Equalizer()
+{
+}
+
+Equalizer::~Equalizer()
+{
+ delete eqCruft->interpolated;
+ delete eqCruft;
+
+// save(napp->dirs()->saveLocation("data", "noatun/") + "equalizer", "auto");
+ for (Band *i=mBands.first(); i!=0; i=mBands.next())
+ delete i;
+}
+
+
+void Equalizer::init()
+{
+ // must be called after VEqualizer::init
+ eqCruft = new OldEqCruft;
+ eqCruft->interpolated = new VInterpolation(6);
+
+ mBands.append(new Band(0));
+ mBands.append(new Band(1));
+ mBands.append(new Band(2));
+ mBands.append(new Band(3));
+ mBands.append(new Band(4));
+ mBands.append(new Band(5));
+
+ connect(VEQ, SIGNAL(changed()), SIGNAL(changed()));
+
+ connect(VEQ, SIGNAL(created(VPreset)), SLOT(created(VPreset)));
+ connect(VEQ, SIGNAL(selected(VPreset)), SLOT(selected(VPreset)));
+ connect(VEQ, SIGNAL(renamed(VPreset)), SLOT(renamed(VPreset)));
+ connect(VEQ, SIGNAL(removed(VPreset)), SLOT(removed(VPreset)));
+
+ connect(VEQ, SIGNAL(enabled()), SIGNAL(enabled()));
+ connect(VEQ, SIGNAL(disabled()), SIGNAL(disabled()));
+ connect(VEQ, SIGNAL(enabled(bool)), SIGNAL(enabled(bool)));
+
+ connect(VEQ, SIGNAL(preampChanged(int)), SIGNAL(preampChanged(int)));
+ connect(VEQ, SIGNAL(preampChanged(int)), SIGNAL(preampChanged(int)));
+}
+
+void Equalizer::created(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit created(p);
+ delete p;
+}
+
+void Equalizer::selected(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit changed(p);
+ delete p;
+}
+
+void Equalizer::renamed(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit renamed(p);
+ delete p;
+}
+
+void Equalizer::removed(VPreset preset)
+{
+ Preset *p = new Preset(preset);
+ emit removed(p);
+ delete p;
+}
+
+QPtrList<Preset> Equalizer::presets() const
+{
+ QValueList<VPreset> presets = VEQ->presets();
+ QPtrList<Preset> list;
+ for (
+ QValueList<VPreset>::Iterator i(presets.begin());
+ i != presets.end(); ++i
+ )
+ {
+ list.append(new Preset(*i));
+ }
+ return list;
+}
+
+Preset *Equalizer::preset(const QString &file)
+{
+ VPreset p = VEQ->presetByFile(file);
+ if (!p) return 0;
+ return new Preset(p);
+}
+
+bool Equalizer::presetExists(const QString &name) const
+{
+ return VEQ->presetExists(name);
+}
+
+Preset *Equalizer::createPreset(const QString &name, bool smart)
+{
+ VPreset p = VEQ->createPreset(name, smart);
+ if (!p) return 0;
+ return new Preset(p);
+}
+
+const QPtrList<Band> &Equalizer::bands() const
+{
+ return mBands;
+}
+
+Band *Equalizer::band(int num) const
+{
+ // can't use QPtrList::at since it sets current
+
+ QPtrListIterator<Band> item(mBands);
+ item+=(unsigned int)num;
+ return *item;
+}
+
+int Equalizer::bandCount() const
+{
+ return 6; // hmm ;)
+}
+
+int Equalizer::preamp() const
+{
+ return VEQ->preamp();
+}
+
+bool Equalizer::isEnabled() const
+{
+ return VEQ->isEnabled();
+
+}
+
+void Equalizer::setPreamp(int p)
+{
+ VEQ->setPreamp(p);
+}
+
+void Equalizer::enable()
+{
+ setEnabled(true);
+}
+
+void Equalizer::disable()
+{
+ setEnabled(false);
+}
+
+void Equalizer::setEnabled(bool e)
+{
+ VEQ->setEnabled(e);
+}
+
+QString Equalizer::toString(const QString &name) const
+{
+ return VEQ->toString(name);
+}
+
+bool Equalizer::fromString(const QString &str)
+{
+ return VEQ->fromString(str);
+}
+
+bool Equalizer::save(const KURL &filename, const QString &name) const
+{
+ return VEQ->save(filename, name);
+}
+
+
+
+bool Equalizer::load(const KURL &filename)
+{
+ return VEQ->load(filename);
+}
+
+void Equalizer::add(Band *)
+{
+ // should never be called
+}
+
+void Equalizer::remove(Band *)
+{
+ // should never be called
+}
+
+void Equalizer::update(bool)
+{
+ // should never be called
+}
+
+void Equalizer::enableUpdates(bool)
+{
+ // should never be called
+}
+
+#undef EQ
+#undef EQBACK
+
+#include "equalizer.moc"
+
diff --git a/noatun/library/equalizerview.cpp b/noatun/library/equalizerview.cpp
new file mode 100644
index 00000000..5e406e13
--- /dev/null
+++ b/noatun/library/equalizerview.cpp
@@ -0,0 +1,325 @@
+#include "vequalizer.h"
+#define EQVIEW_CPP
+#include "equalizerview.h"
+#undef EQVIEW_CPP
+#include "equalizerwidget.h"
+#include "app.h"
+
+#include <knuminput.h>
+#include <kdialog.h>
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include <qlayout.h>
+#include <qslider.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qtabwidget.h>
+#include <qpushbutton.h>
+#include <qheader.h>
+#include <qfileinfo.h>
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qframe.h>
+#include <qgroupbox.h>
+
+#define EQ (napp->vequalizer())
+
+
+////////////////////////////////////////////////
+// PresetList
+
+PresetList::PresetList(QWidget *parent, const char *name)
+ : KListView(parent, name)
+{
+ setItemsRenameable(true);
+ setRenameable(0, true);
+ addColumn(""); // first column is the visible one
+ addColumn("", 0); // create one column to store cruft in
+ setColumnWidthMode(0, QListView::Maximum);
+ header()->setStretchEnabled(true,0);
+ header()->hide();
+ // a try to set a sne minimum width. unfortuately the custom item
+ // still doesn't draw all text with that minimum width
+ setMinimumWidth(kapp->fontMetrics().boundingRect(i18n("Custom")).width()+2*itemMargin());
+}
+
+void PresetList::rename(QListViewItem *item, int c)
+{
+ // We can't rename the "Custom" metapreset
+ if (item->text(0)==i18n("Custom"))
+ return;
+
+ // Or presets we don't have write access to
+ if (!QFileInfo(item->text(1)).isWritable())
+ return;
+
+ KListView::rename(item, c);
+}
+
+////////////////////////////////////////////////
+// EqualizerLevel
+
+EqualizerLevel::EqualizerLevel(QWidget *parent, VBand band)
+ : QWidget(parent), mBand(band)
+{
+ QVBoxLayout *layout = new QVBoxLayout(this,
+ 0, 0, "EqualizerLevel::layout");
+
+ mSlider = new QSlider(-200, 200, 25, 0, Qt::Vertical, this, "EqualizerLevel::mSlider");
+ mSlider->setTickmarks(QSlider::Left);
+ mSlider->setTickInterval(25);
+ layout->addWidget(mSlider);
+ connect(mSlider, SIGNAL(valueChanged(int)), SLOT(changed(int)));
+ mLabel = new QLabel("", this, "EqualizerLevel::mLabel");
+ mLabel->setAlignment(AlignHCenter | AlignVCenter);
+ layout->addWidget(mLabel);
+
+ setMinimumHeight(200);
+// setMinimumWidth(kapp->fontMetrics().width("158kHz"));
+// setMinimumWidth(kapp->fontMetrics().width("549kHz"));
+
+ setBand(band);
+
+ connect(EQ, SIGNAL(modified()), SLOT(changed()));
+ connect(mSlider, SIGNAL(valueChanged(int)), SLOT(changed(int)));
+}
+
+void EqualizerLevel::setBand(VBand band)
+{
+ mBand = band;
+ mLabel->setText(band.format());
+ changed();
+}
+
+void EqualizerLevel::changed()
+{
+ mSlider->setValue(-mBand.level());
+}
+
+void EqualizerLevel::changed(int v)
+{
+ mBand.setLevel(-v);
+}
+
+
+///////////////////////////////////////////////
+// EqualizerView
+
+EqualizerView::EqualizerView()
+ : KDialogBase(0L, "EqualizerView", false, i18n("Equalizer"), Help | Close, Close, true),
+ first(true), mWidget(0), bandsLayout(0), mPresets(0), mGoingPreset(false)
+{
+ mBands.setAutoDelete(true);
+}
+
+void EqualizerView::show()
+{
+ if (first)
+ {
+ first = false;
+ setIcon(SmallIcon("noatun"));
+ mWidget = new EqualizerWidget(this, "mWidget");
+ setMainWidget(mWidget);
+
+ bandsLayout = new QHBoxLayout(mWidget->bandsFrame,
+ 0, KDialog::spacingHint(), "bandsLayout");
+
+ connect(mWidget->preampSlider, SIGNAL(valueChanged(int)),
+ this, SLOT(setPreamp(int)));
+ connect(EQ, SIGNAL(preampChanged(int)),
+ this, SLOT(changedPreamp(int)));
+
+ mWidget->bandCount->setRange(EQ->minBands(), EQ->maxBands());
+ connect(mWidget->bandCount, SIGNAL(valueChanged(int)),
+ EQ, SLOT(setBands(int)));
+
+ QVBoxLayout *l = new QVBoxLayout(mWidget->presetFrame);
+ mPresets = new PresetList(mWidget->presetFrame, "mPresets");
+ l->addWidget(mPresets);
+
+ connect(mWidget->removePresetButton, SIGNAL(clicked()), SLOT(remove()));
+ connect(mWidget->addPresetButton, SIGNAL(clicked()), SLOT(create()));
+ connect(mWidget->resetEqButton, SIGNAL(clicked()), SLOT(reset()));
+
+ new KListViewItem(mPresets, i18n("Custom"));
+
+ connect(mPresets, SIGNAL(currentChanged(QListViewItem*)),
+ this, SLOT(select(QListViewItem*)));
+
+ connect(mPresets, SIGNAL(itemRenamed(QListViewItem*)),
+ this, SLOT(rename(QListViewItem*)));
+
+ // populate the preset list
+ QValueList<VPreset> presets = EQ->presets();
+ QValueList<VPreset>::Iterator it;
+ for (it=presets.begin(); it!=presets.end(); ++it)
+ {
+ created(*it);
+ }
+
+ connect(EQ, SIGNAL(created(VPreset)), SLOT(created(VPreset)));
+ connect(EQ, SIGNAL(renamed(VPreset)), SLOT(renamed(VPreset)));
+ connect(EQ, SIGNAL(removed(VPreset)), SLOT(removed(VPreset)));
+
+ mWidget->enabledCheckBox->setChecked(EQ->isEnabled());
+ connect(mWidget->enabledCheckBox, SIGNAL(toggled(bool)),
+ EQ, SLOT(setEnabled(bool)));
+ connect(EQ, SIGNAL(enabled(bool)),
+ mWidget->enabledCheckBox, SLOT(setChecked(bool)));
+
+ connect(EQ, SIGNAL(changed()),
+ this, SLOT(changedEq()));
+ connect(EQ, SIGNAL(changedBands()),
+ this, SLOT(changedBands()));
+
+ changedBands();
+ changedEq();
+ } // END if(first)
+
+ if (isVisible())
+ raise();
+ else
+ KDialogBase::show();
+}
+
+QListViewItem *EqualizerView::itemFor(const QString &filename)
+{
+ for (QListViewItem *i=mPresets->firstChild(); i!=0; i=i->itemBelow())
+ {
+ QString t = i->text(1);
+ if ((t.length()==0 && filename.length()==0) || t==filename)
+ return i;
+ }
+ return 0;
+}
+
+QListViewItem *EqualizerView::itemFor(const VPreset &preset)
+{
+ return itemFor(preset.file());
+}
+
+// why is it that when you move a QSlider up, it goes down?
+void EqualizerView::setPreamp(int x)
+{
+ EQ->setPreamp(-x);
+}
+
+void EqualizerView::changedPreamp(int x)
+{
+ mWidget->preampSlider->setValue(-x);
+}
+
+
+void EqualizerView::changedBands()
+{
+ mBands.clear();
+
+ VEqualizer &eq = *EQ;
+ for (int i=0; i < eq.bands(); ++i)
+ {
+ EqualizerLevel *l = new EqualizerLevel(mWidget->bandsFrame, eq[i]);
+ bandsLayout->addWidget(l);
+ l->show();
+ mBands.append(l);
+ }
+
+ mWidget->bandCount->setValue(eq.bands());
+ changedEq();
+}
+
+void EqualizerView::changedEq()
+{
+ if (!mGoingPreset)
+ {
+ QListViewItem *customitem = itemFor("");
+ if (!customitem) // this should never happen!
+ return;
+ mPresets->setSelected(customitem, true);
+ }
+}
+
+void EqualizerView::removed(VPreset p)
+{
+ delete itemFor(p);
+}
+
+void EqualizerView::created(VPreset p)
+{
+ // store the filename in QListViewItem::text(0)
+ QString n = p.name();
+ QString f = p.file();
+ new KListViewItem(mPresets, n, f);
+}
+
+void EqualizerView::renamed(VPreset p)
+{
+ QListViewItem *renamed = itemFor(p);
+ if (!renamed) // WTF !
+ {
+ created(p);
+ return;
+ }
+ renamed->setText(0, p.name());
+}
+
+void EqualizerView::remove()
+{
+ QListViewItem *current=mPresets->currentItem();
+ if (current->text(0)==i18n("Custom"))
+ return;
+ QListViewItem *then=current->itemAbove();
+ if (!then) then=current->itemBelow();
+
+ if (then)
+ mPresets->setSelected(then, true);
+
+ VPreset p = EQ->presetByFile(current->text(1));
+ p.remove();
+}
+
+void EqualizerView::create()
+{
+ VPreset p = EQ->createPreset(i18n("New Preset"));
+
+ mGoingPreset = true;
+
+ // Load the new preset
+ p.load();
+
+ // We should have just made a list view item for this preset
+ // See EquailizerView::presetAdded()
+ QListViewItem *i = itemFor(p);
+
+ if (i)
+ mPresets->setSelected(i, true);
+
+ mGoingPreset = false;
+}
+
+void EqualizerView::reset()
+{
+ VEqualizer &eq = *EQ;
+ eq.setPreamp(0);
+ for (int i=0; i < eq.bands(); ++i)
+ eq.band(i).setLevel(0);
+
+}
+
+void EqualizerView::rename(QListViewItem *item)
+{
+ EQ->presetByFile(item->text(1)).setName(item->text(0));
+ item->setText(0, EQ->presetByFile(item->text(1)).name());
+}
+
+void EqualizerView::select(QListViewItem *item)
+{
+ mGoingPreset = true;
+ EQ->presetByFile(item->text(1)).load();
+ mGoingPreset = false;
+ mWidget->removePresetButton->setEnabled(item->text(1).length());
+}
+
+#undef EQ
+#include "equalizerview.moc"
diff --git a/noatun/library/equalizerview.h b/noatun/library/equalizerview.h
new file mode 100644
index 00000000..98778fcc
--- /dev/null
+++ b/noatun/library/equalizerview.h
@@ -0,0 +1,91 @@
+#ifndef EQUALIZERVIEW_H
+#define EQUALIZERVIEW_H
+
+#include <qwidget.h>
+#include <kdialogbase.h>
+#include <klistview.h>
+
+class VBand;
+class QSlider;
+class QLabel;
+class QListViewItem;
+class VPreset;
+class QHBoxLayout;
+class EqualizerWidget;
+
+
+class EqualizerLevel : public QWidget
+{
+Q_OBJECT
+public:
+ EqualizerLevel(QWidget *parent, VBand band);
+
+public slots:
+ void changed();
+ void changed(int);
+
+ void setBand(VBand band);
+
+private:
+ VBand mBand;
+ QSlider *mSlider;
+ QLabel *mLabel;
+};
+
+
+class PresetList : public KListView
+{
+Q_OBJECT
+public:
+ PresetList(QWidget *parent, const char *name=0);
+
+public:
+ void rename(QListViewItem *item, int c);
+};
+
+
+class EqualizerView : public KDialogBase
+{
+Q_OBJECT
+ QPtrList<EqualizerLevel> mBands;
+
+public:
+ EqualizerView();
+ virtual void show();
+
+ QListViewItem *itemFor(const QString &filename);
+ QListViewItem *itemFor(const VPreset &preset);
+
+public slots:
+ void setPreamp(int);
+ void changedPreamp(int);
+
+private slots:
+ void changedBands();
+ void changedEq();
+
+ void removed(VPreset p);
+ void created(VPreset p);
+ void renamed(VPreset p);
+
+ void remove();
+ void create();
+ void reset();
+ void rename(QListViewItem *);
+ void select(QListViewItem*);
+
+private:
+ bool first;
+ EqualizerWidget *mWidget;
+ QHBoxLayout *bandsLayout;
+// QCheckBox *mOn;
+// QSlider *mPreamp;
+ PresetList *mPresets;
+ bool mGoingPreset;
+// QPushButton *mRemovePreset, *mAddPreset;
+// QFrame *mSliders;
+// KIntNumInput *mBandCount;
+};
+
+#endif
+
diff --git a/noatun/library/equalizerwidget.ui b/noatun/library/equalizerwidget.ui
new file mode 100644
index 00000000..1432f59a
--- /dev/null
+++ b/noatun/library/equalizerwidget.ui
@@ -0,0 +1,337 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>EqualizerWidget</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>EqualizerWidget</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>518</width>
+ <height>283</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Equalizer</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QLayoutWidget" row="1" column="0">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Pre&amp;amp:</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>preampSlider</cstring>
+ </property>
+ </widget>
+ <spacer row="1" column="2">
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>1</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ <spacer row="1" column="0">
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>1</width>
+ <height>8</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget" row="1" column="1">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider">
+ <property name="name">
+ <cstring>preampSlider</cstring>
+ </property>
+ <property name="minValue">
+ <number>-200</number>
+ </property>
+ <property name="maxValue">
+ <number>200</number>
+ </property>
+ <property name="lineStep">
+ <number>0</number>
+ </property>
+ <property name="pageStep">
+ <number>25</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="tickmarks">
+ <enum>Both</enum>
+ </property>
+ <property name="tickInterval">
+ <number>25</number>
+ </property>
+ </widget>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>+/-</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QGroupBox" row="1" column="1">
+ <property name="name">
+ <cstring>bandsGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Bands</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame">
+ <property name="name">
+ <cstring>bandsFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QGroupBox" row="1" column="2">
+ <property name="name">
+ <cstring>presetsGroupBox</cstring>
+ </property>
+ <property name="title">
+ <string>&amp;Presets</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QFrame" row="0" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>presetFrame</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>NoFrame</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="1" column="0">
+ <property name="name">
+ <cstring>removePresetButton</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ <widget class="QPushButton" row="2" column="0">
+ <property name="name">
+ <cstring>addPresetButton</cstring>
+ </property>
+ <property name="text">
+ <string>A&amp;dd</string>
+ </property>
+ </widget>
+ <spacer row="2" column="1">
+ <property name="name">
+ <cstring>spacer5</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QLayoutWidget" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout4</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>enabledCheckBox</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Enabled</string>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer6</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Minimum</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1_3</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;Number of bands:</string>
+ </property>
+ <property name="buddy" stdset="0">
+ <cstring>bandCount</cstring>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox">
+ <property name="name">
+ <cstring>bandCount</cstring>
+ </property>
+ <property name="maxValue">
+ <number>16</number>
+ </property>
+ <property name="value">
+ <number>6</number>
+ </property>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>8</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>resetEqButton</cstring>
+ </property>
+ <property name="text">
+ <string>Re&amp;set EQ</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </grid>
+</widget>
+<customwidgets>
+</customwidgets>
+<tabstops>
+ <tabstop>enabledCheckBox</tabstop>
+ <tabstop>bandCount</tabstop>
+ <tabstop>resetEqButton</tabstop>
+ <tabstop>preampSlider</tabstop>
+ <tabstop>removePresetButton</tabstop>
+ <tabstop>addPresetButton</tabstop>
+</tabstops>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/noatun/library/gentable.cpp b/noatun/library/gentable.cpp
new file mode 100644
index 00000000..68e04162
--- /dev/null
+++ b/noatun/library/gentable.cpp
@@ -0,0 +1,1037 @@
+const float data[] =
+{
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 2,
+ 1.93113195896148681640625,
+ 1.78310096263885498046875,
+ 1.70247924327850341796875,
+ 1.624516010284423828125,
+ 1.548387050628662109375,
+ 1.49999988079071044921875,
+ 1.4569394588470458984375,
+ 1.4230768680572509765625,
+ 1.388888835906982421875,
+ 1.3620688915252685546875,
+ 1.3333332538604736328125,
+ 1.31213867664337158203125,
+ 1.28985500335693359375,
+ 1.27981650829315185546875,
+ 1.26315784454345703125,
+ 1.24999988079071044921875,
+ 1.2345678806304931640625,
+ 1.2253520488739013671875,
+ 1.21578943729400634765625,
+ 1.20482635498046875,
+ 1.192531108856201171875,
+ 1.18749988079071044921875,
+ 1.1790392398834228515625,
+ 1.1704347133636474609375,
+ 1.16666662693023681640625,
+ 1.1615383625030517578125,
+ 1.153512477874755859375,
+ 1.14848482608795166015625,
+ 1.143036365509033203125,
+ 1.13935959339141845703125,
+ 1.13513505458831787109375,
+ 1.1296908855438232421875,
+ 1.12499988079071044921875,
+ 1.12320911884307861328125,
+ 1.11992251873016357421875,
+ 1.11627900600433349609375,
+ 1.1115043163299560546875,
+ 1.1090908050537109375,
+ 1.1054852008819580078125,
+ 1.1032257080078125,
+ 1.099999904632568359375,
+ 1.0987341403961181640625,
+ 1.09541976451873779296875,
+ 1.09374988079071044921875,
+ 1.09090900421142578125,
+ 1.088842868804931640625,
+ 1.08620679378509521484375,
+ 1.08459866046905517578125,
+ 1.0833332538604736328125,
+ 1.08128535747528076171875,
+ 1.0793650150299072265625,
+ 1.07757294178009033203125,
+ 1.07648181915283203125,
+ 1.0740740299224853515625,
+ 1.07282412052154541015625,
+ 1.07149398326873779296875,
+ 1.0701754093170166015625,
+ 1.06882584095001220703125,
+ 1.0675990581512451171875,
+ 1.065758228302001953125,
+ 1.0645160675048828125,
+ 1.06382977962493896484375,
+ 1.06249988079071044921875,
+ 1.0615384578704833984375,
+ 1.0606060028076171875,
+ 1.05952370166778564453125,
+ 1.0579373836517333984375,
+ 1.05714285373687744140625,
+ 1.05629765987396240234375,
+ 1.055452823638916015625,
+ 1.05461633205413818359375,
+ 1.05350315570831298828125,
+ 1.05263149738311767578125,
+ 1.05194795131683349609375,
+ 1.05086696147918701171875,
+ 1.0499999523162841796875,
+ 1.04951560497283935546875,
+ 1.04892957210540771484375,
+ 1.0478632450103759765625,
+ 1.0472972393035888671875,
+ 1.04651153087615966796875,
+ 1.04577457904815673828125,
+ 1.04522609710693359375,
+ 1.04464280605316162109375,
+ 1.0439865589141845703125,
+ 1.0434782505035400390625,
+ 1.0425531864166259765625,
+ 1.04188477993011474609375,
+ 1.04166662693023681640625,
+ 1.04085254669189453125,
+ 1.0404479503631591796875,
+ 1.03986012935638427734375,
+ 1.03934001922607421875,
+ 1.03876841068267822265625,
+ 1.0384614467620849609375,
+ 1.03795063495635986328125,
+ 1.03748404979705810546875,
+ 1.03688514232635498046875,
+ 1.03637897968292236328125,
+ 1.03602182865142822265625,
+ 1.03559863567352294921875,
+ 1.03508770465850830078125,
+ 1.0347592830657958984375,
+ 1.03414630889892578125,
+ 1.03389823436737060546875,
+ 1.033333301544189453125,
+ 1.03316318988800048828125,
+ 1.03260862827301025390625,
+ 1.03225803375244140625,
+ 1.0319488048553466796875,
+ 1.0315067768096923828125,
+ 1.03124988079071044921875,
+ 1.03100764751434326171875,
+ 1.0304877758026123046875,
+ 1.0302250385284423828125,
+ 1.0298507213592529296875,
+ 1.0294573307037353515625,
+ 1.029239654541015625,
+ 1.02892553806304931640625,
+ 1.02875816822052001953125,
+ 1.02830183506011962890625,
+ 1.02789247035980224609375,
+ 1.02767288684844970703125,
+ 1.02745664119720458984375,
+ 1.02711856365203857421875,
+ 1.0268414020538330078125,
+ 1.0265486240386962890625,
+ 1.0263156890869140625,
+ 1.02608692646026611328125,
+ 1.02590668201446533203125,
+ 1.0255353450775146484375,
+ 1.02531635761260986328125,
+ 1.02504265308380126953125,
+ 1.0247933864593505859375,
+ 1.02454984188079833984375,
+ 1.02436733245849609375,
+ 1.02406990528106689453125,
+ 1.0238094329833984375,
+ 1.0236685276031494140625,
+ 1.0234191417694091796875,
+ 1.02319896221160888671875,
+ 1.0230023860931396484375,
+ 1.0227272510528564453125,
+ 1.02263367176055908203125,
+ 1.0223712921142578125,
+ 1.0221674442291259765625,
+ 1.0219669342041015625,
+ 1.02173912525177001953125,
+ 1.02160274982452392578125,
+ 1.02142846584320068359375,
+ 1.0211639404296875,
+ 1.0209677219390869140625,
+ 1.0208332538604736328125,
+ 1.02064216136932373046875,
+ 1.02050113677978515625,
+ 1.02027022838592529296875,
+ 1.0201342105865478515625,
+ 1.019999980926513671875,
+ 1.01986753940582275390625,
+ 1.0196077823638916015625,
+ 1.01941740512847900390625,
+ 1.01928365230560302734375,
+ 1.01915180683135986328125,
+ 1.01902878284454345703125,
+ 1.0188140869140625,
+ 1.01866245269775390625,
+ 1.0185184478759765625,
+ 1.01840484142303466796875,
+ 1.01818180084228515625,
+ 1.01807224750518798828125,
+ 1.017937183380126953125,
+ 1.017857074737548828125,
+ 1.0176470279693603515625,
+ 1.01752567291259765625,
+ 1.01736104488372802734375,
+ 1.01724135875701904296875,
+ 1.01709401607513427734375,
+ 1.01700675487518310546875,
+ 1.01684439182281494140625,
+ 1.0166666507720947265625,
+ 1.01657450199127197265625,
+ 1.01646339893341064453125,
+ 1.016326427459716796875,
+ 1.0161879062652587890625,
+ 1.016129016876220703125,
+ 1.01601827144622802734375,
+ 1.01583111286163330078125,
+ 1.01574802398681640625,
+ 1.01562488079071044921875,
+ 1.01554048061370849609375,
+ 1.01538455486297607421875,
+ 1.0152838230133056640625,
+ 1.015151500701904296875,
+ 1.01507270336151123046875,
+ 1.01495325565338134765625,
+ 1.01484715938568115234375,
+ 1.01473677158355712890625,
+ 1.01467132568359375,
+ 1.01453483104705810546875,
+ 1.01440918445587158203125,
+ 1.014302730560302734375,
+ 1.01425659656524658203125,
+ 1.0141642093658447265625,
+ 1.01401865482330322265625,
+ 1.0139102935791015625,
+ 1.01386821269989013671875,
+ 1.01374042034149169921875,
+ 1.01367175579071044921875,
+ 1.01355922222137451171875,
+ 1.013473033905029296875,
+ 1.013388156890869140625,
+ 1.01326954364776611328125,
+ 1.01322114467620849609375,
+ 1.0131232738494873046875,
+ 1.01303780078887939453125,
+ 1.01293098926544189453125,
+ 1.012861728668212890625,
+ 1.01279580593109130859375,
+ 1.01268112659454345703125,
+ 1.0126049518585205078125,
+ 1.01249992847442626953125,
+ 1.012468814849853515625,
+ 1.01236855983734130859375,
+ 1.0122928619384765625,
+ 1.012195110321044921875,
+ 1.01213753223419189453125,
+ 1.01205670833587646484375,
+ 1.0119571685791015625,
+ 1.01190471649169921875,
+ 1.011820316314697265625,
+ 1.01176464557647705078125,
+ 1.01167309284210205078125,
+ 1.01161289215087890625,
+ 1.0115451812744140625,
+ 1.011450290679931640625,
+ 1.011395931243896484375,
+ 1.011334896087646484375,
+ 1.011273860931396484375,
+ 1.0111939907073974609375,
+ 1.0111110210418701171875,
+ 1.01105844974517822265625,
+ 1.0109889507293701171875,
+ 1.0109169483184814453125,
+ 1.010869503021240234375,
+ 1.01078164577484130859375,
+ 1.0107219219207763671875,
+ 1.01063823699951171875,
+ 1.01058197021484375,
+ 1.01052629947662353515625,
+ 1.01044380664825439453125,
+ 1.0103969573974609375,
+ 1.0103447437286376953125,
+ 1.0103092193603515625,
+ 1.01023006439208984375,
+ 1.01018321514129638671875,
+ 1.01010096073150634765625,
+ 1.01004636287689208984375,
+ 1.0099833011627197265625,
+ 1.00994026660919189453125,
+ 1.0098683834075927734375,
+ 1.00980389118194580078125,
+ 1.00976550579071044921875,
+ 1.0097086429595947265625,
+ 1.00965249538421630859375,
+ 1.00961530208587646484375,
+ 1.0095388889312744140625,
+ 1.0094835758209228515625,
+ 1.00943386554718017578125,
+ 1.0093896389007568359375,
+ 1.0093457698822021484375,
+ 1.00925922393798828125,
+ 1.00923073291778564453125,
+ 1.00917422771453857421875,
+ 1.00911843776702880859375,
+ 1.009090900421142578125,
+ 1.0090415477752685546875,
+ 1.0089685916900634765625,
+ 1.0089285373687744140625,
+ 1.00888884067535400390625,
+ 1.00882351398468017578125,
+ 1.00877702236175537109375,
+ 1.00875270366668701171875,
+ 1.0086956024169921875,
+ 1.00864541530609130859375,
+ 1.0086123943328857421875,
+ 1.00854694843292236328125,
+ 1.00850331783294677734375,
+ 1.00846016407012939453125,
+ 1.008419036865234375,
+ 1.00836813449859619140625,
+ 1.00833332538604736328125,
+ 1.00828397274017333984375,
+ 1.00823962688446044921875,
+ 1.00819671154022216796875,
+ 1.00815546512603759765625,
+ 1.00809705257415771484375,
+ 1.0080687999725341796875,
+ 1.0080320835113525390625,
+ 1.0079839229583740234375,
+ 1.0079364776611328125,
+ 1.00790059566497802734375,
+ 1.007874011993408203125,
+ 1.00781857967376708984375,
+ 1.0077903270721435546875,
+ 1.0077369213104248046875,
+ 1.00769221782684326171875,
+ 1.00766277313232421875,
+ 1.00761687755584716796875,
+ 1.0075757503509521484375,
+ 1.0075511932373046875,
+ 1.00750744342803955078125,
+ 1.00747382640838623046875,
+ 1.0074441432952880859375,
+ 1.007380008697509765625,
+ 1.0073528289794921875,
+ 1.00732898712158203125,
+ 1.00728595256805419921875,
+ 1.00724637508392333984375,
+ 1.00722301006317138671875,
+ 1.0071890354156494140625,
+ 1.0071427822113037109375,
+ 1.00710475444793701171875,
+ 1.00707542896270751953125,
+ 1.00703394412994384765625,
+ 1.0070092678070068359375,
+ 1.00696861743927001953125,
+ 1.0069444179534912109375,
+ 1.00691235065460205078125,
+ 1.00686490535736083984375,
+ 1.0068492889404296875,
+ 1.00680267810821533203125,
+ 1.00676810741424560546875,
+ 1.0067365169525146484375,
+ 1.00670230388641357421875,
+ 1.006666660308837890625,
+ 1.0066444873809814453125,
+ 1.00661051273345947265625,
+ 1.006578922271728515625,
+ 1.00654447078704833984375,
+ 1.00651454925537109375,
+ 1.00649344921112060546875,
+ 1.006465435028076171875,
+ 1.00642049312591552734375,
+ 1.00639998912811279296875,
+ 1.0063693523406982421875,
+ 1.006329059600830078125,
+ 1.006311893463134765625,
+ 1.0062713623046875,
+ 1.00624024868011474609375,
+ 1.00621116161346435546875,
+ 1.0061790943145751953125,
+ 1.00615751743316650390625,
+ 1.00613486766815185546875,
+ 1.0060975551605224609375,
+ 1.006077289581298828125,
+ 1.0060422420501708984375,
+ 1.00602400302886962890625,
+ 1.005992412567138671875,
+ 1.005952358245849609375,
+ 1.0059320926666259765625,
+ 1.00591278076171875,
+ 1.00588226318359375,
+ 1.00585925579071044921875,
+ 1.00583088397979736328125,
+ 1.005802631378173828125,
+ 1.00577557086944580078125,
+ 1.0057470798492431640625,
+ 1.0057141780853271484375,
+ 1.0056979656219482421875,
+ 1.0056817531585693359375,
+ 1.00564968585968017578125,
+ 1.00561797618865966796875,
+ 1.00560224056243896484375,
+ 1.00556433200836181640625,
+ 1.00554430484771728515625,
+ 1.00552475452423095703125,
+ 1.00550043582916259765625,
+ 1.0054776668548583984375,
+ 1.0054495334625244140625,
+ 1.00542485713958740234375,
+ 1.00540268421173095703125,
+ 1.005376338958740234375,
+ 1.0053546428680419921875,
+ 1.00533330440521240234375,
+ 1.005319118499755859375,
+ 1.0052816867828369140625,
+ 1.0052630901336669921875,
+ 1.005244731903076171875,
+ 1.0052082538604736328125,
+ 1.00519025325775146484375,
+ 1.00516521930694580078125,
+ 1.00515186786651611328125,
+ 1.00512027740478515625,
+ 1.00510203838348388671875,
+ 1.00508248805999755859375,
+ 1.005056858062744140625,
+ 1.00504410266876220703125,
+ 1.00502192974090576171875,
+ 1.00499999523162841796875,
+ 1.0049750804901123046875,
+ 1.0049545764923095703125,
+ 1.00493824481964111328125,
+ 1.00490796566009521484375,
+ 1.00489127635955810546875,
+ 1.0048732757568359375,
+ 1.00485432147979736328125,
+ 1.004830837249755859375,
+ 1.00480759143829345703125,
+ 1.00479614734649658203125,
+ 1.00476181507110595703125,
+ 1.00474488735198974609375,
+ 1.00472247600555419921875,
+ 1.0047113895416259765625,
+ 1.0046837329864501953125,
+ 1.0046679973602294921875,
+ 1.00465381145477294921875,
+ 1.0046253204345703125,
+ 1.00460827350616455078125,
+ 1.00458705425262451171875,
+ 1.0045731067657470703125,
+ 1.00455367565155029296875,
+ 1.00453507900238037109375,
+ 1.0045135021209716796875,
+ 1.0044963359832763671875,
+ 1.00448429584503173828125,
+ 1.00446426868438720703125,
+ 1.0044443607330322265625,
+ 1.00442469120025634765625,
+ 1.0044116973876953125,
+ 1.00438594818115234375,
+ 1.00437152385711669921875,
+ 1.004350185394287109375,
+ 1.00433647632598876953125,
+ 1.00431025028228759765625,
+ 1.0043010711669921875,
+ 1.004278659820556640625,
+ 1.00425755977630615234375,
+ 1.0042402744293212890625,
+ 1.00422823429107666015625,
+ 1.00420868396759033203125,
+ 1.004192829132080078125,
+ 1.0041687488555908203125,
+ 1.00415790081024169921875,
+ 1.00414359569549560546875,
+ 1.00412642955780029296875,
+ 1.0041067600250244140625,
+ 1.004092693328857421875,
+ 1.00407683849334716796875,
+ 1.0040628910064697265625,
+ 1.0040452480316162109375,
+ 1.00403225421905517578125,
+ 1.00400960445404052734375,
+ 1.0039999485015869140625,
+ 1.00398170948028564453125,
+ 1.00396823883056640625,
+ 1.003952503204345703125,
+ 1.0039370059967041015625,
+ 1.00391638278961181640625,
+ 1.00390613079071044921875,
+ 1.0038909912109375,
+ 1.00387585163116455078125,
+ 1.003860950469970703125,
+ 1.00384604930877685546875,
+ 1.0038239955902099609375,
+ 1.003810882568359375,
+ 1.00378787517547607421875,
+ 1.0037782192230224609375,
+ 1.0037634372711181640625,
+ 1.003753662109375,
+ 1.0037348270416259765625,
+ 1.0037243366241455078125,
+ 1.003703594207763671875,
+ 1.0036900043487548828125,
+ 1.00367641448974609375,
+ 1.00365960597991943359375,
+ 1.00364959239959716796875,
+ 1.00363194942474365234375,
+ 1.00362050533294677734375,
+ 1.003606319427490234375,
+ 1.0035927295684814453125,
+ 1.003577709197998046875,
+ 1.00356709957122802734375,
+ 1.0035459995269775390625,
+ 1.0035398006439208984375,
+ 1.00352108478546142578125,
+ 1.00350868701934814453125,
+ 1.0034964084625244140625,
+ 1.0034883022308349609375,
+ 1.00347220897674560546875,
+ 1.0034601688385009765625,
+ 1.0034482479095458984375,
+ 1.0034313201904296875,
+ 1.00342261791229248046875,
+ 1.00340712070465087890625,
+ 1.003391742706298828125,
+ 1.00337827205657958984375,
+ 1.003366947174072265625,
+ 1.00335562229156494140625,
+ 1.0033333301544189453125,
+ 1.00332772731781005859375,
+ 1.00331342220306396484375,
+ 1.003302097320556640625,
+ 1.0032875537872314453125,
+ 1.00327503681182861328125,
+ 1.003261566162109375,
+ 1.00324666500091552734375,
+ 1.00323617458343505859375,
+ 1.003225803375244140625,
+ 1.00321018695831298828125,
+ 1.00320339202880859375,
+ 1.00318801403045654296875,
+ 1.00317656993865966796875,
+ 1.0031645298004150390625,
+ 1.0031511783599853515625,
+ 1.0031425952911376953125,
+ 1.0031268596649169921875,
+ 1.00311768054962158203125,
+ 1.00310552120208740234375,
+ 1.0030958652496337890625,
+ 1.00308477878570556640625,
+ 1.00307214260101318359375,
+ 1.003063678741455078125,
+ 1.00304877758026123046875,
+ 1.00303637981414794921875,
+ 1.003030300140380859375,
+ 1.003017425537109375,
+ 1.00300967693328857421875,
+ 1.00299394130706787109375,
+ 1.00298798084259033203125,
+ 1.00297462940216064453125,
+ 1.00296294689178466796875,
+ 1.00295555591583251953125,
+ 1.00294458866119384765625,
+ 1.00293254852294921875,
+ 1.0029239654541015625,
+ 1.00290691852569580078125,
+ 1.0028984546661376953125,
+ 1.00288593769073486328125,
+ 1.00287353992462158203125,
+ 1.002865314483642578125,
+ 1.0028550624847412109375,
+ 1.00284087657928466796875,
+ 1.00283014774322509765625,
+ 1.0028247833251953125,
+ 1.00280892848968505859375,
+ 1.00279712677001953125,
+ 1.00278937816619873046875,
+ 1.00277769565582275390625,
+ 1.00276815891265869140625,
+ 1.00275981426239013671875,
+ 1.00274717807769775390625,
+ 1.00273716449737548828125,
+ 1.00272655487060546875,
+ 1.00271737575531005859375,
+ 1.0027062892913818359375,
+ 1.00269830226898193359375,
+ 1.0026881694793701171875,
+ 1.00267612934112548828125,
+ 1.00266802310943603515625,
+ 1.0026581287384033203125,
+ 1.0026471614837646484375,
+ 1.00263845920562744140625,
+ 1.00263154506683349609375,
+ 1.00261914730072021484375,
+ 1.0026109218597412109375,
+ 1.00260007381439208984375,
+ 1.002590656280517578125,
+ 1.00258123874664306640625,
+ 1.00257396697998046875,
+ 1.00256407260894775390625,
+ 1.0025575160980224609375,
+ 1.00254929065704345703125,
+ 1.0025379657745361328125,
+ 1.002526760101318359375,
+ 1.00252091884613037109375,
+ 1.002512454986572265625,
+ 1.00250303745269775390625,
+ 1.00249683856964111328125,
+ 1.00248754024505615234375,
+ 1.0024797916412353515625,
+ 1.00246906280517578125,
+ 1.00246298313140869140625,
+ 1.0024509429931640625,
+ 1.0024464130401611328125,
+ 1.00243747234344482421875,
+ 1.00242710113525390625,
+ 1.0024154186248779296875,
+ 1.00240957736968994140625,
+ 1.002398014068603515625,
+ 1.002389430999755859375,
+ 1.002380847930908203125,
+ 1.0023696422576904296875,
+ 1.00236117839813232421875,
+ 1.0023515224456787109375,
+ 1.00234317779541015625,
+ 1.00233638286590576171875,
+ 1.002325534820556640625,
+ 1.0023148059844970703125,
+ 1.00230944156646728515625,
+ 1.00230228900909423828125,
+ 1.00229179859161376953125,
+ 1.0022830963134765625,
+ 1.00227272510528564453125,
+ 1.00226497650146484375,
+ 1.0022590160369873046875,
+ 1.0022487640380859375,
+ 1.00224208831787109375,
+ 1.00223338603973388671875,
+ 1.00222551822662353515625,
+ 1.00221729278564453125,
+ 1.002211093902587890625,
+ 1.00220263004302978515625,
+ 1.002192974090576171875,
+ 1.00218498706817626953125,
+ 1.00217616558074951171875,
+ 1.00217151641845703125,
+ 1.00216448307037353515625,
+ 1.0021550655364990234375,
+ 1.0021469593048095703125,
+ 1.00214016437530517578125,
+ 1.00213325023651123046875,
+ 1.0021264553070068359375,
+ 1.002118587493896484375,
+ 1.00210964679718017578125,
+ 1.002105236053466796875,
+ 1.0020964145660400390625,
+ 1.00208985805511474609375,
+ 1.002083301544189453125,
+ 1.00207459926605224609375,
+ 1.00206816196441650390625,
+ 1.00206077098846435546875,
+ 1.00205433368682861328125,
+ 1.00204908847808837890625,
+ 1.00204074382781982421875,
+ 1.0020325183868408203125,
+ 1.00202834606170654296875,
+ 1.00202286243438720703125,
+ 1.00201475620269775390625,
+ 1.002007961273193359375,
+ 1.00199997425079345703125,
+ 1.00199401378631591796875,
+ 1.0019893646240234375,
+ 1.0019814968109130859375,
+ 1.0019762516021728515625,
+ 1.00196945667266845703125,
+ 1.00196325778961181640625,
+ 1.001956939697265625,
+ 1.001949310302734375,
+ 1.00194299221038818359375,
+ 1.00193417072296142578125,
+ 1.00192487239837646484375,
+ 1.0019180774688720703125,
+ 1.00191199779510498046875,
+ 1.00190293788909912109375,
+ 1.00189387798309326171875,
+ 1.00188791751861572265625,
+ 1.0018813610076904296875,
+ 1.00187265872955322265625,
+ 1.0018656253814697265625,
+ 1.00185871124267578125,
+ 1.0018517971038818359375,
+ 1.00184500217437744140625,
+ 1.001838207244873046875,
+ 1.00183141231536865234375,
+ 1.00182473659515380859375,
+ 1.001818180084228515625,
+ 1.001811504364013671875,
+ 1.00180494785308837890625,
+ 1.0017974376678466796875,
+ 1.00179207324981689453125,
+ 1.00178563594818115234375,
+ 1.00177776813507080078125,
+ 1.00177085399627685546875,
+ 1.001765727996826171875,
+ 1.00175893306732177734375,
+ 1.00175130367279052734375,
+ 1.00174510478973388671875,
+ 1.00174009799957275390625,
+ 1.0017330646514892578125,
+ 1.00172555446624755859375,
+ 1.00172007083892822265625,
+ 1.001715183258056640625,
+ 1.00170791149139404296875,
+ 1.0017006397247314453125,
+ 1.0016958713531494140625,
+ 1.00169050693511962890625,
+ 1.0016834735870361328125,
+ 1.0016777515411376953125,
+ 1.00167214870452880859375,
+ 1.00166666507720947265625,
+ 1.0016610622406005859375,
+ 1.00165557861328125,
+ 1.0016500949859619140625,
+ 1.00164473056793212890625,
+ 1.00163924694061279296875,
+ 1.0016338825225830078125,
+ 1.0016286373138427734375,
+ 1.0016224384307861328125,
+ 1.00161802768707275390625,
+ 1.0016129016876220703125,
+ 1.00160634517669677734375,
+ 1.001600742340087890625,
+ 1.00159657001495361328125,
+ 1.00159108638763427734375,
+ 1.0015847682952880859375,
+ 1.001579761505126953125,
+ 1.00157558917999267578125,
+ 1.0015697479248046875,
+ 1.00156366825103759765625,
+ 1.00155913829803466796875,
+ 1.0015552043914794921875,
+ 1.00154912471771240234375,
+ 1.00154316425323486328125,
+ 1.0015392303466796875,
+ 1.00153481960296630859375,
+ 1.0015289783477783203125,
+ 1.00152432918548583984375,
+ 1.001519680023193359375,
+ 1.0015151500701904296875,
+ 1.00151050090789794921875,
+ 1.00150597095489501953125,
+ 1.00150144100189208984375,
+ 1.00149691104888916015625,
+ 1.00149250030517578125,
+ 1.00148808956146240234375,
+ 1.0014836788177490234375,
+ 1.00147855281829833984375,
+ 1.001474857330322265625,
+ 1.0014705657958984375,
+ 1.00146520137786865234375,
+ 1.001457691192626953125,
+ 1.00145232677459716796875,
+ 1.0014450550079345703125,
+ 1.0014398097991943359375,
+ 1.0014326572418212890625,
+ 1.00142753124237060546875,
+ 1.00142037868499755859375,
+ 1.00141537189483642578125,
+ 1.0014083385467529296875,
+ 1.00140345096588134765625,
+ 1.00139653682708740234375,
+ 1.00139176845550537109375,
+ 1.0013849735260009765625,
+ 1.0013802051544189453125,
+ 1.0013735294342041015625,
+ 1.00136888027191162109375,
+ 1.001362323760986328125,
+ 1.00135767459869384765625,
+ 1.00135123729705810546875,
+ 1.00134670734405517578125,
+ 1.001340389251708984375,
+ 1.00133597850799560546875,
+ 1.00132977962493896484375,
+ 1.0013253688812255859375,
+ 1.0013191699981689453125,
+ 1.0013148784637451171875,
+ 1.00130879878997802734375,
+ 1.00130462646484375,
+ 1.0012986660003662109375,
+ 1.00129449367523193359375,
+ 1.0012886524200439453125,
+ 1.00128448009490966796875,
+ 1.00127875804901123046875,
+ 1.001274585723876953125,
+ 1.00126898288726806640625,
+ 1.00126492977142333984375,
+ 1.001259326934814453125,
+ 1.00125539302825927734375,
+ 1.00124990940093994140625,
+ 1.00124609470367431640625,
+ 1.00124061107635498046875,
+ 1.00123679637908935546875,
+ 1.0012314319610595703125,
+ 1.00122773647308349609375,
+ 1.00122249126434326171875,
+ 1.00121867656707763671875,
+ 1.001213550567626953125,
+ 1.00120985507965087890625,
+ 1.0012047290802001953125,
+ 1.001201152801513671875,
+ 1.0011961460113525390625,
+ 1.001192569732666015625,
+ 1.0011875629425048828125,
+ 1.00118410587310791015625,
+ 1.001179218292236328125,
+ 1.00117576122283935546875,
+ 1.0011708736419677734375,
+ 1.0011675357818603515625,
+ 1.0011627674102783203125,
+ 1.00115931034088134765625,
+ 1.0011546611785888671875,
+ 1.0011513233184814453125,
+ 1.00114667415618896484375,
+ 1.00114345550537109375,
+ 1.0011389255523681640625,
+ 1.00113570690155029296875,
+ 1.00113117694854736328125,
+ 1.0011279582977294921875,
+ 1.00112354755401611328125,
+ 1.00112044811248779296875,
+ 1.0011160373687744140625,
+ 1.00111293792724609375,
+ 1.001108646392822265625,
+ 1.0011055469512939453125,
+ 1.0011012554168701171875,
+ 1.00109827518463134765625,
+ 1.00109398365020751953125,
+ 1.00109100341796875,
+ 1.0010869503021240234375,
+ 1.00108397006988525390625,
+ 1.0010797977447509765625,
+ 1.0010769367218017578125,
+ 1.00107288360595703125,
+ 1.0010700225830078125,
+ 1.00106608867645263671875,
+ 1.00106322765350341796875,
+ 1.0010592937469482421875,
+ 1.0010564327239990234375,
+ 1.0010526180267333984375,
+ 1.0010497570037841796875,
+ 1.0010459423065185546875,
+ 1.00104320049285888671875,
+ 1.00103938579559326171875,
+ 1.00103676319122314453125,
+ 1.00103294849395751953125,
+ 1.00103032588958740234375,
+ 1.001026630401611328125,
+ 1.0010240077972412109375,
+ 1.00102031230926513671875,
+ 1.0010178089141845703125,
+ 1.00101411342620849609375,
+ 1.0010116100311279296875,
+ 1.00100803375244140625,
+ 1.00100553035736083984375,
+ 1.00100195407867431640625,
+ 1.00099945068359375,
+ 1.00099599361419677734375,
+ 1.0009934902191162109375,
+ 1.00099003314971923828125,
+ 1.00098764896392822265625,
+ 1.00098419189453125,
+ 1.000981807708740234375,
+ 1.0009784698486328125,
+ 1.0009746551513671875,
+ 1.00096893310546875,
+ 1.00096333026885986328125,
+ 1.00095784664154052734375,
+ 1.00095236301422119140625,
+ 1.00094687938690185546875,
+ 1.0009415149688720703125,
+ 1.0009362697601318359375,
+ 1.0009310245513916015625,
+ 1.00092589855194091796875,
+ 1.000920772552490234375,
+ 1.00091564655303955078125,
+ 1.00091063976287841796875,
+ 1.0009057521820068359375,
+ 1.00090086460113525390625,
+ 1.000895977020263671875,
+ 1.000891208648681640625,
+ 1.000886440277099609375,
+ 1.00088179111480712890625,
+ 1.0008771419525146484375,
+ 1.00087249279022216796875,
+ 1.00086796283721923828125,
+ 1.000863552093505859375,
+ 1.0008590221405029296875,
+ 1.00085461139678955078125,
+ 1.00085031986236572265625,
+ 1.00084590911865234375,
+ 1.00084173679351806640625,
+ 1.00083744525909423828125,
+ 1.0008332729339599609375,
+ 1.00082910060882568359375,
+ 1.00082504749298095703125,
+ 1.00082099437713623046875,
+ 1.00081694126129150390625,
+ 1.000813007354736328125,
+ 1.0008089542388916015625,
+ 1.0008051395416259765625,
+ 1.00080120563507080078125,
+ 1.00079739093780517578125,
+ 1.00079357624053955078125,
+ 1.0007898807525634765625,
+ 1.0007860660552978515625,
+ 1.00078237056732177734375,
+ 1.00077879428863525390625,
+ 1.0007750988006591796875,
+ 1.00077152252197265625,
+ 1.0007679462432861328125,
+ 1.00076448917388916015625,
+ 1.0007610321044921875,
+ 1.00075757503509521484375,
+ 1.0007541179656982421875,
+ 1.00075066089630126953125,
+ 1.00074732303619384765625,
+ 1.00074398517608642578125,
+ 1.00074064731597900390625,
+ 1.0007374286651611328125,
+ 1.00073421001434326171875,
+ 1.000730991363525390625,
+ 1.00072777271270751953125,
+ 1.0007245540618896484375,
+ 1.000721454620361328125,
+ 1.0007183551788330078125,
+ 1.0007152557373046875,
+ 1.0007121562957763671875,
+ 1.00070917606353759765625,
+ 1.000706195831298828125,
+ 1.00070321559906005859375,
+ 1.0007002353668212890625,
+ 1.00069725513458251953125,
+ 1.00069439411163330078125,
+ 1.00069153308868408203125,
+ 1.00068867206573486328125,
+ 1.00068581104278564453125,
+ 1.00068295001983642578125,
+ 1.0006802082061767578125,
+ 1.00067746639251708984375,
+ 1.000674724578857421875,
+ 1.00067198276519775390625,
+ 1.0006692409515380859375,
+ 1.00066661834716796875,
+ 1.0006639957427978515625,
+ 1.000661373138427734375,
+ 1.0006587505340576171875,
+ 1.0006561279296875,
+ 1.0006535053253173828125,
+ 1.00065100193023681640625,
+ 1.00064849853515625,
+ 1.0006458759307861328125,
+ 1.0006434917449951171875,
+ 1.00064098834991455078125,
+ 1.000638484954833984375,
+ 1.00063610076904296875,
+ 1.00063359737396240234375,
+ 1.00063121318817138671875,
+ 1.00062882900238037109375,
+ 1.00062656402587890625,
+ 1.000624179840087890625,
+ 1.000621795654296875,
+ 1.00061953067779541015625,
+ 1.0006172657012939453125,
+ 1.00061500072479248046875,
+ 1.000612735748291015625,
+ 1.00061047077178955078125,
+ 1.0006082057952880859375,
+ 1.000606060028076171875,
+ 1.00060379505157470703125,
+ 1.00060164928436279296875,
+ 1.00059950351715087890625,
+ 1.00059735774993896484375,
+ 1.00059521198272705078125,
+ 1.00059306621551513671875,
+ 1.00059092044830322265625,
+ 1.000588893890380859375,
+ 1.0005867481231689453125,
+ 1.00058472156524658203125,
+ 1.00058269500732421875,
+ 1.00058066844940185546875,
+ 1.0005786418914794921875,
+ 1.00057661533355712890625,
+ 1.00057470798492431640625,
+ 1.000572681427001953125,
+ 1.000570774078369140625,
+ 1.00056874752044677734375,
+ 1.00056684017181396484375,
+ 1.00056493282318115234375,
+ 1.00056302547454833984375,
+ 1.00056111812591552734375,
+ 1.00055921077728271484375,
+ 1.00055730342864990234375,
+ 1.000555515289306640625,
+ 1.000553607940673828125,
+ 1.00055181980133056640625,
+ 1.0005500316619873046875,
+ 1.00054824352264404296875,
+ 1.00054633617401123046875,
+ 1.00054454803466796875,
+ 1.0005428791046142578125,
+ 1.00054109096527099609375,
+ 1.000539302825927734375,
+ 1.0005376338958740234375,
+ 1.00053584575653076171875,
+ 1.00053417682647705078125,
+ 1.0005323886871337890625,
+ 1.000530719757080078125,
+ 1.0005290508270263671875,
+ 1.00052738189697265625,
+ 1.0005257129669189453125,
+ 1.000524044036865234375,
+ 1.0005223751068115234375,
+ 1.00052082538604736328125,
+ 1.00051915645599365234375,
+ 1.00051748752593994140625,
+ 1.00051593780517578125,
+ 1.00051438808441162109375,
+ 1.00051271915435791015625,
+ 1.00051116943359375,
+ 1.00050961971282958984375,
+ 1.0005080699920654296875,
+ 1.00050652027130126953125,
+ 1.000504970550537109375,
+ 1.00050342082977294921875,
+ 1.00050199031829833984375,
+ 1.0005004405975341796875,
+ 1.00049889087677001953125,
+ 1.00049746036529541015625,
+ 1.00049602985382080078125,
+ 1.000494480133056640625,
+ 1.00049304962158203125,
+ 1.000491619110107421875,
+ 1.0004901885986328125,
+ 1.000488758087158203125
+};
+
+
+#include <unistd.h>
+
+int main()
+{
+ ::write(1, data, sizeof(data));
+}
+
diff --git a/noatun/library/globalvideo.h b/noatun/library/globalvideo.h
new file mode 100644
index 00000000..e0f28cb0
--- /dev/null
+++ b/noatun/library/globalvideo.h
@@ -0,0 +1,26 @@
+#ifndef __GLOBALVIDEO_H
+#define __GLOBALVIDEO_H
+
+#include "noatun/video.h"
+
+
+class GlobalVideo : public QWidget
+{
+Q_OBJECT
+ QPopupMenu *menu;
+ VideoFrame *video;
+
+public:
+ GlobalVideo();
+
+public slots:
+ void appear();
+ void hide();
+ void slotAdaptSize(int w, int h);
+
+protected:
+ void mouseReleaseEvent(QMouseEvent *e);
+};
+
+
+#endif
diff --git a/noatun/library/ksaver.cpp b/noatun/library/ksaver.cpp
new file mode 100644
index 00000000..f1cc6a61
--- /dev/null
+++ b/noatun/library/ksaver.cpp
@@ -0,0 +1,184 @@
+// ksaver.cpp
+//
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+
+#include "ksaver.h"
+
+class Noatun::KSaver::KSaverPrivate
+{
+public:
+ KSaverPrivate() : isLocal(true), tempFile(0), file(0), textStream(0), dataStream(0) {};
+ bool isLocal;
+ KTempFile *tempFile;
+ QFile *file;
+ KURL url;
+ QString error;
+ QTextStream *textStream;
+ QDataStream *dataStream;
+};
+
+Noatun::KSaver::KSaver(const KURL &_target)
+{
+ d = new KSaverPrivate;
+ d->url = _target;
+
+ if(d->url.protocol() == "file")
+ {
+ d->isLocal = true;
+ d->file = new QFile(d->url.path());
+ }
+ else
+ {
+ d->isLocal = false;
+ }
+}
+
+Noatun::KSaver::~KSaver()
+{
+ close();
+ delete d;
+}
+
+bool Noatun::KSaver::open(void)
+{
+ if(d->isLocal)
+ {
+ if(d->file->open(IO_WriteOnly))
+ {
+ return true;
+ }
+ else
+ {
+ d->error = i18n("Could not write to %1.").arg(d->url.prettyURL());
+ return false;
+ }
+ }
+ else
+ {
+ d->tempFile = new KTempFile;
+ return true;
+ }
+}
+
+bool Noatun::KSaver::close(void)
+{
+ if(!d->isLocal && d->tempFile)
+ delete d->textStream;
+ d->textStream = 0;
+
+ if(!d->isLocal && d->tempFile)
+ delete d->dataStream;
+ d->dataStream = 0;
+
+ if(d->isLocal)
+ {
+ if(!d->file) return true;
+
+ delete d->file;
+ d->file = 0;
+ return true;
+ }
+ else
+ {
+ if(!d->tempFile) return true;
+
+ d->tempFile->close();
+ d->textStream = 0;
+ d->dataStream = 0;
+
+ bool retval = KIO::NetAccess::upload(d->tempFile->name(), d->url);
+
+ delete d->tempFile;
+ d->tempFile = 0;
+
+ return retval;
+ }
+}
+
+QString Noatun::KSaver::error(void)
+{
+ return d->error;
+}
+
+QFile &Noatun::KSaver::file(void)
+{
+ if(d->isLocal && d->file)
+ return *d->file;
+ else if(!d->isLocal && d->tempFile)
+ return *d->tempFile->file();
+ else
+ return *static_cast<QFile *>(0);
+}
+
+QTextStream &Noatun::KSaver::textStream()
+{
+ if(d->textStream)
+ {
+ return *d->textStream;
+ }
+ else if(d->isLocal && d->file)
+ {
+ d->textStream = new QTextStream(d->file);
+ return *d->textStream;
+ }
+ else if(!d->isLocal && d->tempFile)
+ {
+ d->textStream = d->tempFile->textStream();
+ return *d->textStream;
+ }
+ else
+ {
+ return *static_cast<QTextStream *>(0);
+ }
+}
+
+QDataStream &Noatun::KSaver::dataStream()
+{
+ if(d->dataStream)
+ {
+ return *d->dataStream;
+ }
+ else if(d->isLocal && d->file)
+ {
+ d->dataStream = new QDataStream(d->file);
+ return *d->dataStream;
+ }
+ else if(!d->isLocal && d->tempFile)
+ {
+ d->dataStream = d->tempFile->dataStream();
+ return *d->dataStream;
+ }
+ else
+ {
+ return *static_cast<QDataStream *>(0);
+ }
+}
diff --git a/noatun/library/ksaver.h b/noatun/library/ksaver.h
new file mode 100644
index 00000000..e55b2c0d
--- /dev/null
+++ b/noatun/library/ksaver.h
@@ -0,0 +1,102 @@
+// Copyright (C) 2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// Except as contained in this notice, the name(s) of the author(s) shall not be
+// used in advertising or otherwise to promote the sale, use or other dealings
+// in this Software without prior written authorization from the author(s).
+
+#ifndef KSAVER_H
+#define KSAVER_H
+
+#include <kurl.h>
+#include <qfile.h>
+#include <qstring.h>
+#include <qtextstream.h>
+#include <qdatastream.h>
+
+namespace Noatun
+{
+
+/**
+ * KSaver provides a way to save files in the most efficient way for
+ * both local and remote URLs.
+ */
+class KSaver
+{
+public:
+ /**
+ * The constructor takes the url and decides the best way to save,
+ * which will mean using something like KIO::NetAccess or QFile.
+ */
+ KSaver(const KURL &_target);
+
+ /**
+ * The destructor closes if necessary.
+ */
+ ~KSaver();
+
+ /**
+ * open actually tries to open the file
+ *
+ * true on success, false on failure (get the error in @ref error)
+ */
+ bool open(void);
+
+ /**
+ * close closes the file, and uploads if necessary.
+ *
+ * true on success, false on failure (get the error in @ref error)
+ */
+ bool close(void);
+
+ /**
+ * If open or close returns false, there was an error, and error
+ * returns what the error was, when available.
+ */
+ QString error(void);
+
+ /**
+ * file returns a QFile open for writing, which may be for a temporary
+ * file on the local filesystem.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QFile &file(void);
+
+ /**
+ * You can use this to write out your data.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QTextStream &textStream(void);
+
+ /**
+ * You can use this to write out your data.
+ *
+ * If this is called before the file is opened, you will crash.
+ */
+ QDataStream &dataStream(void);
+private:
+ class KSaverPrivate;
+ KSaverPrivate *d;
+};
+
+}
+
+#endif
diff --git a/noatun/library/mimetypetree.cpp b/noatun/library/mimetypetree.cpp
new file mode 100644
index 00000000..c2b60c81
--- /dev/null
+++ b/noatun/library/mimetypetree.cpp
@@ -0,0 +1,64 @@
+#include "mimetypetree.h"
+#include <kmimetype.h>
+#include <qdict.h>
+#include <qheader.h>
+
+
+MimeTypeTree::MimeTypeTree(QWidget *parent)
+ : KListView(parent)
+{
+ KMimeType::List list=KMimeType::allMimeTypes();
+ QDict<QListViewItem> map;
+ setRootIsDecorated(true);
+
+ addColumn("-");
+ header()->hide();
+ QValueListIterator<KMimeType::Ptr> i(list.begin());
+ for (; i != list.end(); ++i)
+ {
+ QString mimetype = (*i)->name();
+ int slash = mimetype.find("/");
+ QString major = mimetype.left(slash);
+
+ // hide all and inode majors
+ if (major == "all" || major=="inode")
+ continue;
+
+ QString minor = mimetype.mid(slash+1);
+ QListViewItem *majorItem=map[major];
+ if (!majorItem)
+ {
+ majorItem=addMajor(major);
+ map.insert(major, majorItem);
+ }
+
+ new QListViewItem(majorItem, minor);
+ }
+}
+
+void MimeTypeTree::sel(QListViewItem *item)
+{
+ QListViewItem *p=item->parent();
+ if (!p) return;
+ QString major=p->text(0);
+ QString minor=item->text(0);
+
+ emit selected(major+'/'+minor);
+}
+
+QListViewItem* MimeTypeTree::addMajor(const QString &name)
+{
+ return new QListViewItem(this, name);
+}
+
+
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+// GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666
+// 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL 666 GPL
+
+#include "mimetypetree.moc"
diff --git a/noatun/library/mimetypetree.h b/noatun/library/mimetypetree.h
new file mode 100644
index 00000000..1f315526
--- /dev/null
+++ b/noatun/library/mimetypetree.h
@@ -0,0 +1,35 @@
+/***
+ * Copyright (c) 2001 Charles Samuels <charles@kde.org>
+ * Standard BSD License. Second version.
+ * The added stipulation is that this cannot link
+ * to GPL code. Except in the explicit case
+ * of Noatun linking to this, and to a GPL plugin,
+ * where the GPL plugin does not use any code
+ * in this class. However, it may link directly
+ * to the Qt Library, where Qt may be under any license.
+ *
+ * Debian, Gnome, and GNU must ALL DIE.
+ * Especially GNU's stupid info pages.
+ **/
+#ifndef MIMETYPETREE_H
+#define MIMETYPETREE_H
+
+#include <klistview.h>
+
+class MimeTypeTree : public KListView
+{
+Q_OBJECT
+public:
+ MimeTypeTree(QWidget *parent);
+
+private:
+ QListViewItem *addMajor(const QString &name);
+private slots:
+ void sel(QListViewItem *item);
+
+signals:
+ void selected(const QString &mimetype);
+};
+
+#endif
+
diff --git a/noatun/library/noatun/Makefile.am b/noatun/library/noatun/Makefile.am
new file mode 100644
index 00000000..115760b0
--- /dev/null
+++ b/noatun/library/noatun/Makefile.am
@@ -0,0 +1,10 @@
+
+noatuninclude_HEADERS = \
+ effects.h playlist.h plugin.h \
+ controls.h engine.h pref.h pluginloader.h \
+ conversion.h equalizer.h stdaction.h scrollinglabel.h \
+ downloader.h app.h player.h stereobuttonaction.h \
+ playlistsaver.h video.h vequalizer.h
+
+noatunincludedir = $(includedir)/noatun
+
diff --git a/noatun/library/noatun/app.h b/noatun/library/noatun/app.h
new file mode 100644
index 00000000..c4effa4d
--- /dev/null
+++ b/noatun/library/noatun/app.h
@@ -0,0 +1,271 @@
+#ifndef NOATUN_H
+#define NOATUN_H
+
+#include <kuniqueapplication.h>
+#include <kdemacros.h>
+class Playlist;
+class Player;
+class LibraryLoader;
+class KPopupMenu;
+class NoatunPreferences;
+class Downloader;
+class Effects;
+class EffectView;
+class Equalizer;
+class KDialogBase;
+class VEqualizer;
+
+namespace NoatunStdAction
+{
+ class PluginActionMenu;
+}
+
+/**
+ * @class NoatunApp app.h noatun/app.h
+ * Can be accessed from every plugin by using "napp"
+ *
+ * @short Noatun Application class
+ * @author Charles Samuels
+ * @version 2.3
+ */
+class KDE_EXPORT NoatunApp : public KUniqueApplication
+{
+Q_OBJECT
+friend class Playlist;
+
+public:
+ NoatunApp();
+ ~NoatunApp();
+
+ /**
+ * Provides access to the central playlist object.
+ * Any plugin can access the noatun playlist with
+ * <pre>napp->playlist();</pre>
+ **/
+ Playlist *playlist() const;
+ /**
+ * access to the central player object
+ * Any plugin can access the noatun player backend with
+ * <pre>napp->playlist();</pre>
+ **/
+ Player *player() const { return mPlayer; }
+ /**
+ * access to NoatunPreferences
+ **/
+ NoatunPreferences *preferencesBox() const { return mPref; }
+
+ /**
+ * @return a list of mimetypes aRts (and thus Noatun) can play
+ * KFileDialog accepts this QString instead of the shell globs in
+ * its static functions, make use of it :)
+ **/
+ QString mimeTypes();
+
+ LibraryLoader *libraryLoader() const { return mLibraryLoader; }
+ Downloader *downloader() const { return mDownloader; }
+ static QImage readPNG(const QString &filename);
+ Effects *effects() const;
+ ::Equalizer *equalizer() const { return mEqualizer; }
+ ::VEqualizer *vequalizer();
+ KDialogBase *equalizerView() const { return mEqualizerView; }
+
+ QCString version() const;
+
+ virtual void commitData(QSessionManager &);
+ virtual void saveState(QSessionManager &);
+
+ /**
+ * The three startup modes how noatun should behave when it is
+ * restarted.
+ *
+ * Restore - it can restore the player's last state
+ *
+ * Play - it automatically starts playing the next file in the
+ * playlist
+ *
+ * DontPlay - it doesn't start playing
+ */
+ enum StartupPlayMode { Restore = 0, Play, DontPlay };
+
+signals:
+ /**
+ * Tells you (a UI plugin) to hide
+ */
+ void hideYourself();
+
+ /**
+ * Tells you (a UI plugin) to show again
+ */
+ void showYourself();
+
+public slots:
+ /**
+ * ask the UIs to hide or show
+ **/
+ void toggleInterfaces();
+ /**
+ * ask the UIs to show
+ **/
+ void showInterfaces();
+ /**
+ * ask the UIs to hide, then you have
+ * to look around for them, or you'll lose
+ **/
+ void hideInterfaces();
+
+public: //options
+ /**
+ * @deprecated Use startupPlayMode() instead
+ */
+ bool autoPlay() const;
+ int startupPlayMode() const;
+ bool loopList() const;
+ bool oneInstance() const;
+ QString saveDirectory() const;
+ /**
+ * @deprecated
+ * now merged with clearOnOpen()
+ **/
+ bool clearOnStart() const;
+ /**
+ * @return true if the playlist will be cleared when opening a
+ * new file via commandline or file open dialog, false otherwise
+ **/
+ bool clearOnOpen() const;
+ bool hackUpPlaylist() const;
+ /**
+ * @return true if hardware-mixing is being used, false in case
+ * software mixing is active
+ **/
+ bool fastMixer() const;
+ QString titleFormat() const;
+ /**
+ * @return true if remaining time is displayed to the user, false if
+ * played time is displayed
+ **/
+ bool displayRemaining() const;
+
+ void setOneInstance(bool);
+ void setLoopList(bool);
+ /**
+ * @deprecated Use setStartupPlayMode() instead
+ */
+ void setAutoPlay(bool);
+ void setStartupPlayMode(int mode);
+ void setSaveDirectory(const QString &);
+ void setRememberPositions(bool);
+ /**
+ * @deprecated
+ * now merged with setClearOnOpen()
+ **/
+ void setClearOnStart(bool);
+ /**
+ * Set if the playlist will be cleared when opening a
+ * new file via commandline or file open dialog
+ **/
+ void setClearOnOpen(bool);
+ void setHackUpPlaylist(bool);
+
+ /**
+ * Set if hardware-mixing ("fast") or software-mixing ("slow") should be used
+ **/
+ void setFastMixer(bool);
+
+ void setTitleFormat(const QString &);
+
+ /**
+ * Pass true if remaining time should be displayed to the user, false if
+ * played time should be displayed
+ **/
+ void setDisplayRemaining(bool);
+
+ /**
+ * To insert items use KActions and insert() them into pluginActionMenu().
+ * @return pointer to the actionmenu
+ */
+ NoatunStdAction::PluginActionMenu *pluginActionMenu();
+
+ /**
+ * @deprecated
+ * Adds an item to the plugin menu.
+ * You may use this value with pluginMenu() for greater control of your menu entry
+ *
+ * @return the ID associated with the menu item, for use in pluginMenuRemove()
+ **/
+ int pluginMenuAdd(const QString &text, const QObject *receiver, const char *member);
+
+ /**
+ * @deprecated
+ * Removes an item previously added to the plugin menu.
+ * @param id the ID of the "to be removed" menu item
+ **/
+ void pluginMenuRemove(int id);
+
+ /**
+ * @deprecated
+ * Use pluginActionMenu() instead
+ * @return pointer to the plugin menu
+ */
+ KPopupMenu *pluginMenu();
+
+protected:
+ virtual int newInstance();
+
+public slots:
+ // slots for the contextMenu
+ /**
+ * Opens the preferences dialog
+ * You can also use
+ * <pre>napp->preferencesBox()->show()</pre>
+ * @see NoatunPreferences
+ */
+ void preferences();
+ /**
+ * Exits Noatun
+ */
+ void quit();
+ /**
+ * Shows the standard file-open dialog
+ */
+ void fileOpen();
+ /**
+ * Shows the effects window
+ */
+ void effectView();
+ /**
+ * Shows the equalizer window
+ */
+ void equalizerView();
+
+private:
+ void loadPlugins();
+ void saveEngineState();
+ void restoreEngineState();
+
+private:
+ Player *mPlayer;
+ LibraryLoader *mLibraryLoader;
+ KPopupMenu *mPluginMenu;
+ NoatunStdAction::PluginActionMenu *mPluginActionMenu;
+ Downloader *mDownloader;
+ struct Private;
+ Private *d;
+ EffectView *mEffectView;
+ NoatunPreferences *mPref;
+ ::Equalizer *mEqualizer;
+ KDialogBase *mEqualizerView;
+ bool showingInterfaces;
+};
+
+#define napp (static_cast<NoatunApp*>(kapp))
+
+// version info for the plugins
+// this is MAJOR.MINOR.PATCHLEVEL
+// and you developers better ignore patchlevel :)
+#define NOATUN_MAJOR 2
+#define NOATUN_MINOR 10
+#define NOATUN_PATCHLEVEL 0
+
+#define NOATUN_VERSION "2.10.0"
+
+#endif
diff --git a/noatun/library/noatun/controls.h b/noatun/library/noatun/controls.h
new file mode 100644
index 00000000..6db8d1cd
--- /dev/null
+++ b/noatun/library/noatun/controls.h
@@ -0,0 +1,79 @@
+#ifndef __CONTROLS_H
+#define __CONTROLS_H
+
+#include <qguardedptr.h>
+
+#include <kaction.h>
+#include <ktoolbar.h>
+#include <qslider.h>
+#include <qstringlist.h>
+#include <kdemacros.h>
+
+class QComboBox;
+class QLabel;
+
+/**
+ * A slider that can be moved around while being
+ * changed internally
+ *
+ * @short Special QSlider based class suitable for time sliders
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT L33tSlider : public QSlider
+{
+Q_OBJECT
+public:
+ L33tSlider(QWidget * parent, const char * name=0);
+ L33tSlider(Orientation, QWidget * parent, const char * name=0);
+ L33tSlider(int minValue, int maxValue, int pageStep, int value,
+ Orientation, QWidget * parent, const char * name=0);
+
+ bool currentlyPressed() const;
+signals:
+ /**
+ * emmited only when the user changes the value by hand
+ **/
+ void userChanged(int value);
+
+public slots:
+ virtual void setValue(int);
+
+protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ virtual void wheelEvent(QWheelEvent *);
+
+private:
+ bool pressed;
+};
+
+/**
+ * @short A slider for your toolbar
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class SliderAction : public KAction
+{
+Q_OBJECT
+public:
+ SliderAction(const QString& text, int accel, const QObject *receiver,
+ const char *member, QObject* parent, const char* name );
+ virtual int plug( QWidget *w, int index = -1 );
+ virtual void unplug( QWidget *w );
+ QSlider* slider() const { return m_slider; }
+
+signals:
+ void plugged();
+
+public slots:
+ void toolbarMoved(KToolBar::BarPosition pos);
+
+private:
+ QGuardedPtr<QSlider> m_slider;
+ QStringList m_items;
+ const QObject *m_receiver;
+ const char *m_member;
+};
+
+#endif
diff --git a/noatun/library/noatun/conversion.h b/noatun/library/noatun/conversion.h
new file mode 100644
index 00000000..841ed0f8
--- /dev/null
+++ b/noatun/library/noatun/conversion.h
@@ -0,0 +1,117 @@
+#ifndef NOATUN_CONVERT_H
+#define NOATUN_CONVERT_H
+
+/**
+ * Helper functions to convert between sample types
+ * @short sample type conversion
+ **/
+namespace Conversion
+{
+/**
+ * Convert a mono 8 bit group to float
+ **/
+void convertMono8ToFloat(unsigned long samples, unsigned char *from, float *to);
+
+/**
+ * convert a mono 8 bit group to float, at a different speed
+ **/
+void interpolateMono8ToFloat(unsigned long samples, double start, double speed,
+ unsigned char *from, float *to);
+
+/**
+ * convert a mono 16 bit little-endian stream to float
+ **/
+void convertMono16leToFloat(unsigned long samples, unsigned char *from, float *to);
+
+/**
+ * convert a mono 16 bit little-endian stream to float at a different speed
+ **/
+void interpolateMono16leToFloat(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *to);
+
+/**
+ * convert an stereo 8-bit interleaved (Alternating left/right channel) to floats
+ **/
+void convertStereoI8To2Float(unsigned long samples, unsigned char *from,
+ float *left, float *right);
+
+/**
+ * convert a stereo 8-bit interleaved (alternating left/right channel) to floats,
+ * at a different speed
+ **/
+void interpolateStereoI8To2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right);
+/**
+ * convert an interleaved 16 bit little endian stream to two floats
+ **/
+void convertStereoI16leTo2Float(unsigned long samples, unsigned char *from, float *left,
+ float *right);
+
+/**
+ * convert an interleaved 16 bit little endian stream to two floats at a different
+ * speed
+ **/
+void interpolateStereoI16leTo2Float(unsigned long samples, double startpos, double speed,
+ unsigned char *from, float *left, float *right);
+
+/**
+ * convert a float to a float, but at a different speed
+ **/
+void interpolateMonoFloatToFloat(unsigned long samples, double startpos, double speed,
+ float *from, float *to);
+
+/**
+ * convert a stereo interleaved float to two floats
+ **/
+void convertStereoIFloatTo2Float(unsigned long samples, float *from, float *left,
+ float *right);
+
+/**
+ * convert a stereo interleaved float to two floats at a different speed
+ **/
+void interpolateStereoIFloatTo2Float(unsigned long samples, double startpos,
+ double speed, float *from, float *left,
+ float *right);
+
+/**
+ * convert a mono float to a 16 bit little endian float
+ **/
+void convertMonoFloatTo16le(unsigned long samples, float *from, unsigned char *to);
+
+/**
+ * convert a two floats to a 16 bit little endian interleaved float
+ **/
+void convertStereo2FloatToI16le(unsigned long samples, float *left, float *right,
+ unsigned char *to);
+
+/**
+ * convert a mono float to an 8 bit stream
+ **/
+void convertMonoFloatTo8(unsigned long samples, float *from, unsigned char *to);
+
+/**
+ * convert two floats to an 8 bit interleaved stream
+ **/
+void convertStereo2FloatToI8(unsigned long samples, float *left, float *right,
+ unsigned char *to);
+
+/**
+ * to little endian (Intel) with swapEndian
+ * does nothing if this is an intel
+ **/
+inline void toLittleEndian(unsigned long len, char *buffer);
+
+/**
+ * to big endian with swapEndian
+ * does nothing if this isn't an intel
+ **/
+inline void toBigEndian(unsigned long len, char *buffer);
+
+/**
+ * swap the endian, does so on every platform
+ * operates on 16 bits at a time. so loads 16, swaps, and copies them
+ **/
+void swapEndian(unsigned long length, char *buffer);
+}
+
+#endif
diff --git a/noatun/library/noatun/downloader.h b/noatun/library/noatun/downloader.h
new file mode 100644
index 00000000..c281a006
--- /dev/null
+++ b/noatun/library/noatun/downloader.h
@@ -0,0 +1,120 @@
+#ifndef _DOWNLOADER_H
+#define _DOWNLOADER_H
+
+#include <kurl.h>
+#include <qobject.h>
+#include <qptrlist.h>
+
+class QFile;
+class QTimer;
+class Downloader;
+
+
+namespace KIO
+{
+ class TransferJob;
+ class Job;
+}
+
+/**
+ * Item to download, usually queued up in Downloader
+ **/
+class DownloadItem
+{
+ friend class Downloader;
+public:
+ DownloadItem();
+ virtual ~DownloadItem();
+
+ bool isDownloaded() const;
+
+ /**
+ * @return the local filename this item will be saved to
+ **/
+ QString localFilename() const;
+
+ virtual void setLocalFilename(const QString &filename);
+
+ /**
+ * Called if this item has been fully downloaded
+ **/
+ virtual void downloadFinished();
+ /**
+ * Called at regular intervals while downloading this item
+ **/
+ virtual void downloaded(int percent);
+ /**
+ * Called when downloading this item timed out
+ **/
+ virtual void downloadTimeout();
+ /**
+ * @return true if the download was scheduled, false if the file is local
+ **/
+ bool enqueue(const KURL &url);
+ /**
+ * Remove this item from Downloader queue
+ **/
+ void dequeue();
+
+private:
+ QString mLocalFilename;
+};
+
+/**
+ * download playlistitems, in a queue based fasion
+ **/
+class Downloader : public QObject
+{
+Q_OBJECT
+ struct QueueItem
+ {
+ DownloadItem *notifier;
+ KURL file;
+ QString local;
+ };
+
+public:
+ Downloader(QObject *parent=0);
+ virtual ~Downloader();
+
+public slots:
+ QString enqueue(DownloadItem *notifier, const KURL &file);
+ void dequeue(DownloadItem *notifier);
+
+ /**
+ * @internal
+ **/
+ void start();
+
+signals:
+ /**
+ * Emitted when a new DownloadItem was added to the queue
+ * @p notifier is the added item
+ **/
+ void enqueued(DownloadItem *notifier, const KURL &file);
+ /**
+ * Emitted when a DownloadItem was removed from the queue
+ * @p notifier is the removed item
+ **/
+ void dequeued(DownloadItem *notifier);
+
+private slots:
+ void getNext();
+
+ void data( KIO::Job *, const QByteArray &data);
+ void percent( KIO::Job *, unsigned long percent);
+ void jobDone( KIO::Job *);
+ void giveUpWithThisDownloadServerIsRunningNT();
+
+private:
+ QPtrList<Downloader::QueueItem> mQueue;
+ QPtrList<Downloader::QueueItem> *mUnstartedQueue;
+ QFile *localfile;
+ Downloader::QueueItem *current;
+ KIO::TransferJob *mJob;
+ QTimer *mTimeout;
+ bool mStarted;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/effects.h b/noatun/library/noatun/effects.h
new file mode 100644
index 00000000..c39cb8aa
--- /dev/null
+++ b/noatun/library/noatun/effects.h
@@ -0,0 +1,186 @@
+#ifndef EFFECTS_H
+#define EFFECTS_H
+
+#include <qptrlist.h>
+#include <qcstring.h>
+#include <qstrlist.h>
+#include <qobject.h>
+
+namespace Arts { class StereoEffect; }
+class Engine;
+class EffectConfigWidget;
+
+/**
+ * @short Base class for a noatun effect
+ *
+ * Example:
+ * \code
+ * new Effect("Arts::SomeEffect");
+ * \endcode
+ * Then you can add, insert, etc. it using Effects
+ **/
+class Effect
+{
+friend class Effects;
+friend class EffectConfigWidget;
+public:
+ Effect(const char *name);
+ ~Effect();
+
+ /**
+ * return the effect processed
+ * directly before this one
+ **/
+ Effect *before() const;
+ /**
+ * return the effect processed
+ * directly after this one
+ **/
+ Effect *after() const;
+ long id() const;
+
+ /**
+ * get the Arts object.
+ * @internal
+ **/
+ Arts::StereoEffect *effect() const;
+
+ /**
+ * Get the name of the object.
+ **/
+ QCString name() const;
+
+ /**
+ * get the "clean" title of effect
+ **/
+ QString title() const;
+
+ /**
+ * @return true if this effect name is invalid, false otherwise
+ * <b>Note:</b> If you call StereoEffect::start() on an invalid Effect you
+ * will probably be punished with a segmentation fault.
+ **/
+ bool isNull() const;
+
+ /**
+ * show the configure dialog box for
+ * this effect. if friendly is true,
+ * then create a top-level window,
+ * set an icon and make it purdy. Otherwise
+ * create a plan widget that you can reparent.
+ **/
+ QWidget *configure(bool friendly=true);
+
+ /**
+ * Does this widget have a configurable
+ * dialog box. E.g., will configure
+ * return null?
+ **/
+ bool configurable() const;
+
+ /**
+ * Return an effect name that can be presented to a user
+ * i.e. Arts::FREEVERB will end up as FREEVERB
+ **/
+ static QString clean(const QCString &name);
+private:
+ long mId;
+ Arts::StereoEffect *mEffect;
+ QCString mName;
+ QWidget *mConfig;
+};
+
+/**
+ * Noatuns effect stack
+ * @author Charles Samuels
+ **/
+class Effects : public QObject
+{
+Q_OBJECT
+friend class Effect;
+public:
+ Effects();
+
+ bool insert(const Effect *after, Effect *item);
+
+ /**
+ * create the Effect, by getting the proper item
+ * from the list, then append it here.
+ *
+ * for example
+ * \code
+ * append(new Effect(available()[0]));
+ * \endcode
+ **/
+ bool append(Effect *item);
+
+ /**
+ * reorder the effect stack. If @p after is null,
+ * it'll be first
+ **/
+ void move(const Effect *after, Effect *item);
+ /**
+ * Removes an item from the effect stack
+ * @param item item to remove
+ * @param del delete the item from the effect stack as well (default true)
+ **/
+ void remove(Effect *item, bool del=true);
+
+ /**
+ * Removes all items from the effect stack.
+ * @param del delete the items from the effect stack as well (default true)
+ **/
+ void removeAll(bool del=true);
+
+ /**
+ * A list of all available effects, by name
+ * each of these can be given to the first
+ * argument of the Effect constructor
+ **/
+ QStrList available() const;
+
+ /**
+ * A list of all available effects objects
+ **/
+ QPtrList<Effect> effects() const;
+
+ /**
+ * Get the Effect that has the following id
+ **/
+ Effect *findId(long id) const;
+
+private:
+ QPtrListIterator<Effect> stackPosition() const;
+
+signals:
+ /**
+ * Emitted when an effect has been added to the effect stack
+ * @param effect the effect that got added
+ **/
+ void added(Effect *effect);
+ /**
+ * Emitted when an effect has been removed to the effect stack
+ * @param effect the effect that got removed
+ **/
+ void removed(Effect *effect);
+ /**
+ * Emitted when an effect has been moved
+ * @param effect the effect that got moved
+ **/
+ void moved(Effect *effect);
+ /**
+ * Emitted when an effect is about to be
+ * deleted (from memory)
+ * @param effect the effect to be deleted
+ **/
+ void deleting(Effect *effect);
+
+private:
+ // stored in no specific order
+ QPtrList<Effect> mItems;
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatun/engine.h b/noatun/library/noatun/engine.h
new file mode 100644
index 00000000..2244fa1e
--- /dev/null
+++ b/noatun/library/noatun/engine.h
@@ -0,0 +1,120 @@
+#ifndef _ENGINE_H
+#define _ENGINE_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <arts/kmedia2.h>
+#include <noatun/playlist.h>
+#include <kdemacros.h>
+class Visualization;
+
+namespace Arts
+{
+ class SoundServerV2;
+
+ class Synth_AMAN_PLAY;
+}
+
+namespace Noatun
+{
+ class StereoEffectStack;
+ class StereoVolumeControl;
+ class Equalizer;
+ class Session;
+}
+
+class NoatunApp;
+
+/**
+ * Handles all playing, connecting to aRts.
+ * Does almost everything related to multimedia.
+ * Most interfacing should be done with Player
+ **/
+class KDE_EXPORT Engine : public QObject
+{
+Q_OBJECT
+friend class NoatunApp;
+public:
+ Engine(QObject *parent=0);
+ ~Engine();
+ void setInitialized();
+ bool initialized() const;
+
+public slots:
+ /**
+ * opens the file, use play() to start playing
+ **/
+ bool open(const PlaylistItem &file);
+ /**
+ * Continues playing
+ **/
+ bool play();
+ /**
+ * Terminates playing, does not close the file
+ **/
+ void pause();
+ /**
+ * resets the engine
+ **/
+ void stop();
+ /**
+ * skips to a timecode
+ * unit is milliseconds
+ **/
+ void seek(int msec);
+
+ void setVolume(int percent);
+
+ void connectPlayObject();
+signals:
+ void done();
+ /**
+ * emitted when arts dies and noatun has to start
+ * it again. This is called when the new arts
+ * is already initialized
+ **/
+ void artsError();
+
+ void aboutToPlay();
+
+ void receivedStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl
+ );
+
+ void playingFailed();
+
+ private slots:
+ void slotProxyError();
+ void deleteProxy();
+
+public:
+ int state();
+ int position(); // return position in milliseconds
+ int length(); // return track-length in milliseconds
+ int volume() const;
+
+private:
+ int openMixerFD();
+ void closeMixerFD(int);
+ void useHardwareMixer(bool);
+ bool initArts();
+
+public:
+ Arts::SoundServerV2 *server() const;
+ Arts::PlayObject playObject() const;
+ Arts::SoundServerV2 *simpleSoundServer() const;
+ Noatun::StereoEffectStack *effectStack() const;
+ Noatun::Equalizer *equalizer() const;
+ Noatun::StereoEffectStack *visualizationStack() const;
+ Noatun::StereoEffectStack *globalEffectStack() const;
+ Noatun::Session *session() const;
+
+private:
+ class EnginePrivate;
+ EnginePrivate *d;
+ bool mPlay;
+};
+
+#endif
diff --git a/noatun/library/noatun/equalizer.h b/noatun/library/noatun/equalizer.h
new file mode 100644
index 00000000..10de88c5
--- /dev/null
+++ b/noatun/library/noatun/equalizer.h
@@ -0,0 +1,238 @@
+#ifndef NOATUN_EQUALIZER_H
+#define NOATUN_EQUALIZER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <kurl.h>
+#include <noatun/vequalizer.h>
+
+class Engine;
+class Equalizer;
+
+/**
+ * a preset stores the state of the equalizer
+ *
+ * @short EQ Preset
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Preset
+{
+friend class Equalizer;
+
+ Preset(const QString &file);
+ Preset();
+ Preset(VPreset p);
+
+public:
+ QString name() const;
+ bool setName(const QString &name);
+ bool save() const;
+ bool load();
+
+ void remove();
+
+ QString file() const;
+
+ VPreset &vpreset() const;
+private:
+ QString mFile;
+};
+
+/**
+ * This represents a single band in Noatuns %Equalizer
+ *
+ * @short EQ Band
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Band
+{
+friend class Equalizer;
+friend class QPtrList<Band>;
+
+private:
+ Band();
+ Band(int start, int end);
+ Band(int band);
+ virtual ~Band();
+public:
+ /**
+ * the intensity of the change.
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ int level();
+ void setLevel(int l);
+
+ int start() const;
+ int end() const;
+
+ /**
+ * the middle between start and end
+ **/
+ int center() const;
+
+ QString formatStart(bool withHz=true) const;
+ QString formatEnd(bool withHz=true) const;
+ /**
+ * return the format for center()
+ **/
+ QString format(bool withHz=true) const;
+
+private:
+ int mOffset;
+ int mNum, mEnd;
+};
+
+
+/**
+ * @deprecated
+ *
+ * this API is deprecated!!! Do not use it!
+ * it acts as a wrapper around the new VEqualizer API
+ * This only exists to keep compatibility in both
+ * source and binary forms. It will go away in the future.
+ * @short old Equalizer
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class Equalizer : public QObject
+{
+friend class Band;
+friend class Preset;
+friend class Engine;
+
+Q_OBJECT
+public:
+ Equalizer();
+ ~Equalizer();
+
+ const QPtrList<Band> &bands() const;
+ Band *band(int num) const;
+ int bandCount() const;
+
+ int preamp() const;
+ bool isEnabled() const;
+
+ void init();
+
+public slots:
+ /**
+ * set the preamplification
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ void setPreamp(int p);
+ void enable();
+ void disable();
+ void setEnabled(bool e);
+
+
+public:
+// saving eq stuff
+ /**
+ * save the current levels
+ * all noatun equalizer files have the "*.noatunequalizer"
+ * pattern. Nevertheless, the file can be identified
+ * by magic, so it's not required
+ **/
+ bool save(const KURL &file, const QString &friendly) const;
+
+ /**
+ * restore the EQ settings from this file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * convert the current EQ settings to string form,
+ * suitable for storage, the given string is the title
+ *
+ * @see fromString
+ **/
+ QString toString(const QString &name="stored") const;
+
+ /**
+ * undo a toString, restoring the EQ
+ * to the settings in the given string,
+ * emitting the changed signals
+ **/
+ bool fromString(const QString &str);
+
+ /**
+ * create a preset with such a name
+ * and remember that it'l return zero
+ * if the name already exists
+ *
+ * If smart is true, append a number to the end
+ * of the name, if one already exists by the given
+ **/
+ Preset *createPreset(const QString &name, bool smart=true);
+
+ /**
+ * return all the presets
+ * remember to setAutoDelete on this
+ **/
+ QPtrList<Preset> presets() const;
+
+ Preset *preset(const QString &file);
+ bool presetExists(const QString &name) const;
+
+signals:
+ void changed(Band *band);
+ void changed();
+ void enabled();
+ void disabled();
+ void enabled(bool e);
+
+ void preampChanged(int p);
+ void preampChanged();
+
+ /**
+ * the preset with the given name
+ * was selected
+ **/
+ void changed(Preset *);
+
+ /**
+ * when a new preset has been created
+ **/
+ void created(Preset*);
+
+ /**
+ * when @p preset has been renamed to @p newname
+ **/
+ void renamed(Preset *);
+
+ /**
+ * the given preset has been removed
+ **/
+ void removed(Preset *);
+
+private slots:
+ void created(VPreset preset);
+ void selected(VPreset preset);
+ void renamed(VPreset preset);
+ void removed(VPreset preset);
+
+private:
+ void add(Band*);
+ void remove(Band*);
+ // apply the data to artsd
+ void enableUpdates(bool on=true);
+ void update(bool full=false);
+
+private:
+ QPtrList<Band> mBands;
+ bool mUpdates;
+ int mPreamp;
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatun/mocs.cpp b/noatun/library/noatun/mocs.cpp
new file mode 100644
index 00000000..3d503bcc
--- /dev/null
+++ b/noatun/library/noatun/mocs.cpp
@@ -0,0 +1,37 @@
+#ifdef STUPID_KDE_AUTOMAKE
+
+#include "effects.h"
+#include "effects.moc"
+#include "playlistsaver.h"
+#include "playlistsaver.moc"
+#include "playlist.h"
+#include "playlist.moc"
+#include "plugin.h"
+#include "plugin.moc"
+#include "video.h"
+#include "video.moc"
+#include "controls.h"
+#include "controls.moc"
+#include "engine.h"
+#include "engine.moc"
+#include "pref.h"
+#include "pref.moc"
+#include "equalizer.h"
+#include "equalizer.moc"
+#include "stdaction.h"
+#include "stdaction.moc"
+#include "scrollinglabel.h"
+#include "scrollinglabel.moc"
+#include "downloader.h"
+#include "downloader.moc"
+#include "app.h"
+#include "app.moc"
+#include "player.h"
+#include "player.moc"
+#include "stereobuttonaction.h"
+#include "stereobuttonaction.moc"
+#include "vequalizer.h"
+#include "vequalizer.moc"
+
+#endif
+
diff --git a/noatun/library/noatun/player.h b/noatun/library/noatun/player.h
new file mode 100644
index 00000000..bccda62b
--- /dev/null
+++ b/noatun/library/noatun/player.h
@@ -0,0 +1,270 @@
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include <qobject.h>
+#include <qtimer.h>
+#include <kurl.h>
+#include <noatun/playlist.h>
+#include <kdemacros.h>
+class Engine;
+class Playlist;
+class KLibrary;
+
+/**
+ * This class has slots for all the common media player buttons
+ * The slots are called, and it queries the Playlist for the appropriate
+ * file.
+ *
+ * @short Noatun player backend
+ * @author Charles Samuels
+ * @version 2.4
+ **/
+class KDE_EXPORT Player : public QObject
+{
+Q_OBJECT
+friend class Effects;
+friend class PlaylistItemData;
+friend class PlaylistNotifier;
+
+public:
+ /**
+ * "None": Plays the playlist entries sequentially until the
+ * end of the playlist.
+ * "Song": Repeats the current playlist entry indefinitely.
+ * "Playlist": Plays the playlist entries sequentially, until
+ * end of the playlist. Noatun will then restart
+ * playback at the first song.
+ * "Random": Plays the entries of the playlist in a random,
+ * non-repeating order. Playback will continue
+ * indefinitely.
+ **/
+ enum LoopType { None=0, Song, Playlist, Random };
+
+public:
+ Player(QObject *parent=0);
+ ~Player();
+
+ /**
+ * @return a string with the time that can be used in the UI:
+ * CC:CC/LL:LL (mm:ss)
+ **/
+ QString lengthString(int _position=-1);
+ /**
+ * @return LoopType enum
+ **/
+ int loopStyle() const { return mLoopStyle; }
+ /**
+ * @return the volume from 0-100
+ * use volume() instead
+ **/
+ int volume() const;
+ /**
+ * @return the position in milliseconds
+ **/
+ int getTime() const { return position; }
+ /**
+ * @return the track-length in milliseconds
+ **/
+ int getLength();
+ /**
+ * @return true if we're playing
+ **/
+ bool isPlaying();
+ /**
+ * @return true if paused
+ **/
+ bool isPaused();
+ /**
+ * @return true if stopped
+ **/
+ bool isStopped();
+
+ /**
+ * get the current playlist
+ * this may be null
+ * And this may not be necessarily an item allocated
+ * by playlist()
+ **/
+ PlaylistItem current() const { return mCurrent;} // TODO: uninline
+
+ /**
+ * loads a file and optionally plays it
+ * @param file the file to load
+ * @param purge true to clear the playlist on open
+ * @param autoplay start playing that file after loading it
+ **/
+ void openFile(const KURL &file, bool purge=true, bool autoplay=false);
+
+ /**
+ * loads all given files
+ * @param files list of files to load
+ * @param purge true to clear the playlist on open
+ * @param autoplay if true, play the first added item
+ **/
+ void openFile(const KURL::List &files, bool purge=true, bool autoplay=false);
+
+ Engine *engine() const { return mEngine; }
+
+public slots:
+ /**
+ * show or hide the playlist
+ **/
+ void toggleListView();
+ /**
+ * force the playing/paused/stopped/playlist shown signals to
+ * be sent out, also, you can send this if you want to
+ * make all the UIs re-display the current item
+ **/
+ void handleButtons();
+ /**
+ * remove current from playlist
+ **/
+ void removeCurrent();
+
+ /**
+ * go back a track
+ **/
+ void back();
+ /**
+ * stop playing
+ **/
+ void stop();
+ /**
+ * start playing
+ **/
+ void play();
+
+ /**
+ * play the given file
+ **/
+ void play(const PlaylistItem &item);
+ /**
+ * start playing, or pause if we're currently playing
+ **/
+ void playpause();
+ /**
+ * go forward a track
+ **/
+ void forward(bool allowLoop = true);
+
+ /**
+ * skip to the position, unit is milliseconds
+ **/
+ void skipTo(int msec);
+
+ /**
+ * goes to the next type of looping
+ **/
+ void loop();
+
+ /**
+ * set the type of looping
+ **/
+ void loop(int i);
+
+ void setVolume(int);
+
+public slots:
+ /**
+ * @internal
+ * Play the current file
+ **/
+ void playCurrent();
+ /**
+ * @internal
+ * load the current file
+ **/
+ void newCurrent();
+
+private slots:
+ void posTimeout();
+ void aboutToPlay();
+ void slotUpdateStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl
+ );
+
+signals:
+ /**
+ * Tells you to update the seekbar, volume
+ * and title.
+ **/
+ void timeout();
+
+ void stopped();
+
+ void playing();
+
+ void paused();
+
+ /**
+ * when the type of looping is
+ * changed
+ **/
+ void loopTypeChange(int t);
+
+ /**
+ * the playlist is made visible
+ **/
+ void playlistShown();
+
+ /**
+ * the playlist is hidden
+ **/
+ void playlistHidden();
+
+ /**
+ * called at the same time as newSong, but
+ * maybe easier to work with
+ **/
+ void newSongLen(int mins, int sec);
+
+ /**
+ * when a new song is currently playing
+ **/
+ void newSong();
+
+ /**
+ * Called when a new song is about to be played, but
+ * hasn't started. player->current() is the
+ * next song
+ **/
+ void changed();
+
+ /**
+ * called when we're about to load item, but it hasn't been yet
+ *
+ * this is used for implementing new protocols
+ **/
+ void aboutToOpen(PlaylistItem item);
+
+ void volumeChanged(int);
+
+ /**
+ * this signal is emitted when the user (or a plugin) seeks
+ * the song with @sa skipTo
+ **/
+ void skipped();
+
+ /**
+ * this signal is emitted when the user (or a plugin) seeks
+ * the song with @sa skipTo
+ *
+ * @param msec is the position in the song in milliseconds
+ **/
+ void skipped(int msec);
+
+private:
+ Engine *mEngine;
+ QTimer filePos;
+ int position;
+ int mLoopStyle;
+ bool firstTimeout;
+ PlaylistItem mCurrent; // TODO eliminate
+ QPtrList<PlaylistNotifier> mNotifiers;
+};
+
+
+#endif
+
diff --git a/noatun/library/noatun/playlist.h b/noatun/library/noatun/playlist.h
new file mode 100644
index 00000000..37eabb0b
--- /dev/null
+++ b/noatun/library/noatun/playlist.h
@@ -0,0 +1,531 @@
+#ifndef NOATUNPLAYLIST_H
+#define NOATUNPLAYLIST_H
+
+#include <qobject.h>
+#include <kurl.h>
+#include <qdict.h>
+#include <qstringlist.h>
+#include <cassert>
+#include <kdemacros.h>
+
+class PlaylistItem;
+
+/**
+ * If you're not coding a playlist, ignore this class.
+ *
+ * The backend. Since PlaylistItemData is refcounted,
+ * this contains the data, the PlaylistItem is the "reference"
+ * <pre>
+ * PlaylistItem m=new PlaylistItemData;
+ * </pre>
+ * Of course, you're supposed to inherit from PlaylistItemData
+ * in your Playlist, since there are pure virtuals.
+ *
+ * You can create these objects on demand.
+ *
+ * @short Playlist item data
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT PlaylistItemData
+{
+public:
+ PlaylistItemData();
+ virtual ~PlaylistItemData();
+
+ /**
+ * Noatun asks your playlist for properties. It is your
+ * responsiblity to store the information. But usually a QMap<QString,QString>
+ * is enough.
+ *
+ * If you return the default value, the default should not
+ * be written.
+ *
+ * This returns the property, or def if such a property doesn't exist
+ **/
+ virtual QString property(const QString &key, const QString &def=0) const=0;
+
+ /**
+ * This sets the property with the given key and value.
+ *
+ * Important: If you use a QMap, you'll have to remove the current
+ * item before adding a new one
+ **/
+ virtual void setProperty(const QString &key, const QString &property)=0;
+
+ /**
+ * remove the item with given key
+ **/
+ virtual void clearProperty(const QString &key)=0;
+
+ /**
+ * return a list of property keys
+ **/
+ virtual QStringList properties() const=0;
+
+ /**
+ * return whether if the given key exists
+ **/
+ virtual bool isProperty(const QString &key) const=0;
+
+ /**
+ * return the title of the song. By default, this will
+ * use the following by default, in order of priority
+ *
+ * property("realtitle")
+ * property("title")
+ * url().filename()
+ *
+ * you shouldn't need to override this.
+ **/
+ virtual QString title() const;
+
+ /**
+ * the true filename of the song, remote or local
+ **/
+ virtual KURL url() const { return KURL(property("url")); }
+ /**
+ * set the true filename of the song, remote or local
+ **/
+ virtual void setUrl(const KURL &url) { setProperty("url", url.url()); }
+
+ /**
+ * first, this checks for the property "mimetype", else
+ * it'l ask KMimeType based on file()
+ **/
+ virtual QCString mimetype() const;
+
+ /**
+ * first, checks for the property "playObject", else,
+ * it'l ask aRts
+ **/
+ virtual QCString playObject() const;
+
+ /**
+ * return the filename to send to the playobject
+ **/
+ virtual QString file() const { return url().path(); }
+
+ /**
+ * what's the length of the song, in milliseconds?
+ **/
+ virtual int length() const;
+
+ /**
+ * sets the length of the song, in milliseconds
+ **/
+ virtual void setLength(int ms);
+
+ /**
+ * returns a friendly representation of the length
+ * of this file
+ **/
+ QString lengthString() const;
+
+ /**
+ * compare yourself with the given PlaylistItemData
+ * This is implemented in the slow fashion of
+ * comparing all the properties. You may
+ * have a much faster way of implementing this
+ * if this==&d, this will not be called, normally
+ **/
+ virtual bool operator == (const PlaylistItemData &d) const;
+
+ /**
+ * this is implemented as !(*this==d), you may have a
+ * faster way to implement this
+ **/
+ virtual bool operator != (const PlaylistItemData &d) const;
+
+ /**
+ * remove this item from the list
+ **/
+ virtual void remove() = 0;
+
+
+ /**
+ * Playlists should not download files if this is true
+ **/
+ bool streamable() const { return isProperty("stream_"); }
+
+public:
+ /**
+ * Call this when you want to signal
+ * the given item has been added to the list
+ **/
+ void added();
+
+ /**
+ * Your playlist must call this when the file
+ * is removed from the playlist
+ **/
+ void removed();
+
+ /**
+ * Your playlist must call this when the file
+ * is modified
+ **/
+ void modified();
+
+
+public: // reference counting
+ /**
+ * Have the reference counter never delete this
+ *
+ * This is useful for when you want to keep all
+ * your items hanging around
+ **/
+ void addRef() { mRefs++; }
+ void removeRef()
+ {
+ mRefs--;
+ if (!mRefs)
+ delete this;
+ }
+
+private:
+ mutable int mRefs;
+};
+
+
+/**
+ * a reference to a PlaylistItem(Data)
+ *
+ * All methods here should have the same behavior
+ * as they do for PlaylistItemData
+ *
+ * If you're a playlist, you should inherit
+ * from PlaylistItemData
+ *
+ * It's client code's responsibility to ensure that
+ * PlaylistItem is not null by using either the boolean
+ * conversion or isNull()
+ *
+ * @short Playlist items
+ * @author Charles Samuels
+ * @version 2.3
+ **/
+class KDE_EXPORT PlaylistItem
+{
+public:
+ PlaylistItem(const PlaylistItem &source);
+ PlaylistItem(PlaylistItemData *source);
+ PlaylistItem() : mData(0) {}
+ ~PlaylistItem();
+
+ PlaylistItem &operator =(const PlaylistItem &source);
+ PlaylistItem &operator =(PlaylistItemData *source);
+
+ PlaylistItemData *data() { return mData; }
+ const PlaylistItemData *data() const { return mData; }
+
+ const PlaylistItem &operator =(const PlaylistItem &source) const;
+ const PlaylistItem &operator =(const PlaylistItemData *source) const;
+
+ operator bool() const { return (bool)mData; }
+ bool isNull() const { return !(bool)mData; }
+
+ bool operator ==(const PlaylistItem &i) const
+ {
+ if (data()==i.data()) return true;
+ if (!data() || !i.data()) return false;
+ return *i.data()==*data();
+ }
+ bool operator ==(const PlaylistItemData *i) const
+ {
+ if (data()==i) return true;
+ if (!data() || !i) return false;
+ return *i==*data();
+ }
+
+ bool operator !=(const PlaylistItem &i) const
+ { return ! (*this==i); }
+ bool operator !=(const PlaylistItemData *i) const
+ { return ! (*this->data()==*i); }
+
+ QString property(const QString &key, const QString &def=0) const
+ {
+ assert(mData);
+ return mData->property(key, def);
+ }
+
+ void setProperty(const QString &key, const QString &property)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->setProperty(key, property);
+ }
+
+ void clearProperty(const QString &key)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->clearProperty(key);
+ }
+
+ QStringList properties() const
+ {
+ assert(mData);
+ return mData->properties();
+ }
+
+ bool isProperty(const QString &key) const
+ {
+ assert(mData);
+ return mData->isProperty(key);
+ }
+
+ KURL url() const { assert(mData); return mData->url(); }
+ void setUrl(const KURL &url)
+ {
+ assert(mData);
+ const_cast<PlaylistItemData*>(mData)->setUrl(url);
+ }
+
+ QCString mimetype() const { assert(mData); return mData->mimetype(); }
+ QCString playObject() const { assert(mData); return mData->playObject(); }
+ QString file() const { assert(mData); return mData->file(); }
+
+ QString title() const
+ {
+ assert(mData);
+ return mData->title();
+ }
+
+ int length() const
+ {
+ assert(mData);
+ return mData->length();
+ }
+
+ void setLength(int ms) const
+ {
+ assert(mData);
+ mData->setLength(ms);
+ }
+
+ QString lengthString() const { assert(mData); return mData->lengthString(); }
+
+ void remove() { assert(mData); mData->remove(); }
+
+ bool streamable() const { assert(mData); return mData->streamable(); }
+
+private:
+ // reference counting
+ void removeRef() const;
+ void addRef() const; // requires mData already has item
+
+private:
+ mutable PlaylistItemData *mData;
+ void *_bc1, *_bc2;
+};
+
+/**
+ * The playlist, which you derive from when creating
+ * your own playlist.
+ *
+ * Do not, under any circumstances, call a Playlist method
+ * when you can call a Player method, unless, of course, you
+ * ARE the playlist.
+ **/
+class Playlist : public QObject
+{
+Q_OBJECT
+ friend class PlaylistItemData;
+public:
+ Playlist(QObject *parent, const char *name);
+ /**
+ * on playlist unload, your playlist must
+ * have current()==0 and emit playCurrent
+ **/
+ virtual ~Playlist();
+
+ /**
+ * go to the front
+ **/
+ virtual void reset()=0;
+
+ /**
+ * empty the list
+ **/
+ virtual void clear()=0;
+
+ /**
+ * add a file
+ */
+ virtual void addFile(const KURL&, bool play=false)=0;
+
+ /**
+ * cycle forward, return that
+ **/
+ virtual PlaylistItem next()=0;
+
+ /**
+ * cycle to next section, return that
+ * defaults to return next()
+ */
+ virtual PlaylistItem nextSection();
+
+ /**
+ * cycle back, return that
+ **/
+ virtual PlaylistItem previous()=0;
+
+ /**
+ * cycle to previous section, return that
+ * defaults to return previous()
+ */
+ virtual PlaylistItem previousSection();
+
+ /**
+ * current item
+ **/
+ virtual PlaylistItem current()=0;
+ /**
+ * set the current item
+ **/
+ virtual void setCurrent(const PlaylistItem &)=0;
+
+ /**
+ * get the first item
+ **/
+ virtual PlaylistItem getFirst() const =0;
+
+ /**
+ * get the item after item, note that getFirst and getAfter do not
+ * have to follow play order since they are used solely to iterate
+ * over the entire collection in some order. Duplicating the play
+ * order (by looking into the future) is not necessary.
+ **/
+ virtual PlaylistItem getAfter(const PlaylistItem &item) const =0;
+
+ /**
+ * is the view visible?
+ **/
+
+ virtual bool listVisible() const =0;
+
+ /**
+ * do the KCmdLineArgs stuff
+ **/
+ int handleArguments();
+
+ /**
+ * return a list of songs in which at least one
+ * of the keys matches at least one of the values
+ *
+ * the default implementation will call getFirst()
+ * and getAfter() which could be potentially slow,
+ * depending how your playlist is designed. So
+ * you're free to reimplement this if you could
+ * do better
+ *
+ * A value of "" is equal to an unset value
+ *
+ * limit is the maximum amount of items to return,
+ * or -1 if you want as many as possible
+ *
+ * if exact is true, a match is only made if
+ * the string is identical to a value. if false
+ * a match is made if the string contains a value
+ *
+ * caseSensitive, if false, means that the given
+ * values are compared case insensitively to
+ * to the items in the playlist. The keys
+ * are always compared with case sensitivity
+ **/
+ virtual QValueList<PlaylistItem> select(
+ const QStringList &keys, const QStringList &values,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ );
+
+ /**
+ * The default implementation will just call
+ * the above select. Of course, you're free to implement
+ * both of these (with different mechanisms if need be)
+ * for speed
+ **/
+ virtual QValueList<PlaylistItem> select(
+ const QString &key, const QString &value,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ );
+ /**
+ * exactly the same as the above, except converts
+ * the const char* to QString (utf8)
+ **/
+ inline QValueList<PlaylistItem> select(
+ const char *key, const char *value,
+ int limit=-1, bool exact=false, bool caseSensitive=false
+ )
+ {
+ return select(
+ QString(key), QString(value),
+ limit, exact, caseSensitive
+ );
+ }
+
+public slots:
+ /**
+ * show the list!
+ **/
+ virtual void showList()=0;
+ /**
+ * hide it
+ **/
+ virtual void hideList()=0;
+ /**
+ * toggle visibility
+ **/
+ virtual void toggleList();
+
+signals:
+ /**
+ * when you want the engine to reload current()
+ * This is how your playlist forces noatun to
+ * play a new song
+ **/
+ void playCurrent();
+
+ /**
+ * when the list is hidden
+ **/
+ void listHidden();
+
+ /**
+ * when the list is shown
+ **/
+ void listShown();
+};
+
+/**
+ * this class's methods will be called whenever
+ * something happens to the playlist or its
+ * items.
+ *
+ * If the playlist plugin changes, you don't have to do
+ * anything.
+ **/
+class PlaylistNotifier
+{
+public:
+ PlaylistNotifier();
+ virtual ~PlaylistNotifier();
+
+ /**
+ * a new item is added to the list
+ **/
+ virtual void added(PlaylistItem &) {}
+
+ /**
+ * an item is removed from the list
+ **/
+ virtual void removed(PlaylistItem &) {}
+
+ /**
+ * this item was modified (via a changed
+ * or added property
+ **/
+ virtual void modified(PlaylistItem &) {}
+};
+
+
+#endif
+
diff --git a/noatun/library/noatun/playlistsaver.h b/noatun/library/noatun/playlistsaver.h
new file mode 100644
index 00000000..e83761ca
--- /dev/null
+++ b/noatun/library/noatun/playlistsaver.h
@@ -0,0 +1,102 @@
+#ifndef NOATUNPLAYLISTSAVER_H
+#define NOATUNPLAYLISTSAVER_H
+
+#include <kurl.h>
+#include <qmap.h>
+#include <noatun/playlist.h>
+
+class Structure;
+
+/**
+ * a simple one-group implementatio of
+ * the standard XML playlist format:
+ *
+ * http://noatun.kde.org/formats/xmlplaylist.phtml
+ *
+ * A custom implementation like yours should have a
+ * different "client" attribute for the playlist tag
+ **/
+class PlaylistSaver
+{
+ friend class NoatunXMLStructure;
+ friend class MSASXStructure;
+public:
+ enum Options
+ {
+ XMLPlaylist=1,
+ M3U=2,
+ PLS=4,
+ EXTM3U=8,
+ ASX=16
+ };
+
+ PlaylistSaver();
+ virtual ~PlaylistSaver();
+
+ bool save(const KURL &file, int options=0);
+ bool load(const KURL &file, int options=0);
+
+ /**
+ * guess the list's content between M3U or
+ * PLS, and give it to you
+ *
+ * this is also the way to make icecast work.
+ * you can also pass true icecast urls
+ * here and it'l do its magic.
+ *
+ * This calls readItem
+ **/
+ bool metalist(const KURL &url);
+
+ /**
+ * unused, for future expansion, do not use
+ * @internal
+ **/
+ virtual void setGroup(const QString &);
+
+protected:
+ /**
+ * read the item, and add it to your list
+ * Given is a list of properties which coincide
+ * with the standard noatun ones
+ **/
+ virtual void readItem(const QMap<QString,QString> &properties) = 0;
+
+ /**
+ * add this item to the XML file
+ * or a null item if we're at the end
+ **/
+ virtual PlaylistItem writeItem() = 0;
+
+ /**
+ * unused, for future expansion
+ * @internal
+ **/
+ virtual void changeGroup(const QString &) {}
+
+ /**
+ * this is called when you should either
+ * clear your list, or start writing from the start of the list
+ *
+ * You usually don't need to implement this, since it'l always
+ * be called immediately after load() or save()
+ **/
+ virtual void reset() {}
+
+private:
+ bool loadXML(const KURL &file, int x=0);
+ bool saveXML(const KURL &file, int x=0);
+
+ bool loadM3U(const KURL &file, int x=0);
+ bool saveM3U(const KURL &file, int x=0);
+
+ bool loadPLS(const KURL &file, int x=0);
+ bool savePLS(const KURL &file, int x=0);
+
+private:
+ class Private;
+ PlaylistSaver::Private *d; // unused
+
+};
+
+#endif
diff --git a/noatun/library/noatun/plugin.h b/noatun/library/noatun/plugin.h
new file mode 100644
index 00000000..f90dcc0d
--- /dev/null
+++ b/noatun/library/noatun/plugin.h
@@ -0,0 +1,467 @@
+#ifndef NPLUGIN_H
+#define NPLUGIN_H
+
+#include <noatun/pluginloader.h>
+#include <qmemarray.h>
+#include <vector>
+#include <kdemacros.h>
+
+namespace Noatun { class FFTScopeStereo; class FFTScope; class RawScope;
+ class RawScopeStereo; class StereoEffectStack;
+ class Listener;
+ }
+namespace Arts { class SoundServerV2; class Dispatcher; }
+
+//backwards compatibility
+
+#define NOATUNPLUGIND
+#define NOATUNPLUGINC(classname) { }
+
+class Playlist;
+class Player;
+class ExitNotifier;
+class NoatunApp;
+/**
+ * @short Base class for all plugins
+ **/
+class KDE_EXPORT Plugin
+{
+public:
+ Plugin();
+ virtual ~Plugin();
+
+ /**
+ * called directly after the plugin, just in case
+ * you want Noatun to be "ready" with your class
+ **/
+ virtual void init();
+
+ /**
+ * unload the plugin
+ * if it returns true, return from your function
+ * immediately and don't access members of this
+ * TODO
+ **/
+ virtual bool unload();
+
+ /**
+ * make multiple inheritence work
+ * only needed with playlist plugins
+ * generally "return this" in here
+ **/
+ virtual Playlist *playlist() { return 0; }
+};
+
+/**
+ * @short Base class for user-interface plugins
+ *
+ * Inherit from this one instead of Plugin if you are
+ * a user-interface
+ **/
+class KDE_EXPORT UserInterface : public Plugin
+{
+public:
+ UserInterface();
+ virtual ~UserInterface();
+};
+
+
+class TimerThingy;
+
+
+/**
+ * @short Base class for all sorts of visualizations
+ *
+ * all Noatun Visualizations can be in
+ * separate processes! You must fork, and
+ * then exec() to be able to use this.
+ * Its perfectly safe to create
+ * a visualization in a binary executed by
+ * KProcess.
+ **/
+class Visualization
+{
+friend class TimerThingy;
+friend class ThingThatTellsAVisualizationThatNoatunIsGone;
+friend class ExitNotifier;
+friend class NoatunApp;
+public:
+ /**
+ * interval is how frequently the rescope
+ * will occur
+ * 0 means disabled
+ *
+ * Pid, if not zero, can force the visualizaton
+ * to use another noatun's process's visualization stack,
+ * if it is zero, it'l try to guess what to use
+ **/
+ Visualization(int interval=125, int pid=0);
+ virtual ~Visualization();
+
+ /**
+ * start the timer, if it's 0, then this
+ * will do nothing.
+ **/
+ virtual void start();
+ /**
+ * stop the timer
+ **/
+ virtual void stop();
+
+ /**
+ * how long between each rescoping in milliseconds
+ **/
+ int interval() const;
+
+ /**
+ * set how long to wait
+ * the change takes effect immediately,
+ * unless it hadn't been start()ed beforehand
+ **/
+ virtual void setInterval(int msecs);
+
+ /**
+ * cause a rescoping to take effect immediately
+ **/
+ virtual void timeout()=0;
+
+ Noatun::StereoEffectStack visualizationStack();
+ Arts::SoundServerV2 *server();
+
+ /**
+ * return noatun's pid, useful if you're doing remote-visualization
+ *
+ * It returns the pid to use, or -1, if there was an error, or
+ * there isn't a noatun running. If there isn't a noatun running, the
+ * computer will crash, the trains will take the wrong tracks, time
+ * will start moving in the wrong direction, and the whole universe will melt
+ * down.
+ **/
+ static int noatunPid();
+
+ /**
+ * test if the server is still there
+ **/
+ bool connected();
+
+ /**
+ * create a dispatcher object , does nothing if
+ * this already has been called
+ **/
+ static void initDispatcher();
+
+private:
+ int mTimeout;
+ TimerThingy *mTimerThingy;
+ QCString mVisualizationStack;
+ Arts::SoundServerV2 *mServer;
+ static Arts::Dispatcher *mDispatcher;
+ static bool internalVis;
+};
+
+/**
+ * Base class for all kinds of FFT scopes
+ **/
+class KDE_EXPORT FFTScope : public Visualization
+{
+public:
+ FFTScope(int interval, int pid=0);
+
+ /**
+ * the "width" of each scopeEvent
+ **/
+ virtual int bands() const=0;
+
+ /**
+ * returns the magic number that you can
+ * give to "setBands". For example:
+ * <pre>
+ * setBands(magic(50));
+ * bands()==50
+ * </pre>
+ **/
+ static float magic(int bands);
+
+ /**
+ * set the width combiner value. in theory,
+ * a value of 1.0 should give you exactly
+ * 1024 samples, greater values will decrease
+ * the amount quite rapidly. by default,
+ * 1.1 is used, which gives 50. You'll have
+ * to tweak this manually.
+ *
+ * This will only return valid responses
+ * for values between 10 and 1024
+ *
+ * This function is a terrible hack, and we apologize
+ * for it. The values of these magic numbers
+ * do occasionally change, so you must use
+ * @ref magic
+ **/
+ virtual void setBands(float n)=0;
+};
+
+/**
+ * An easy to use FFT scope, stereo version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class KDE_EXPORT StereoFFTScope : public FFTScope
+{
+public:
+ StereoFFTScope(int timeout=250, int pid=0);
+ virtual ~StereoFFTScope();
+
+ /**
+ * called according to the timeout
+ * the two floats contain the data, with @p len items
+ * you override this yourself
+ **/
+ virtual void scopeEvent(float *left, float *right, int len)
+ { (void)left; (void)right; (void)len; }
+
+ /**
+ * get the current data
+ * pass two vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&left, std::vector<float> *&right);
+ virtual void timeout();
+
+ virtual int bands() const;
+ virtual void setBands(float f);
+
+private:
+ Noatun::FFTScopeStereo *mScope;
+ long mId;
+};
+
+
+/**
+ * An easy to use FFT scope, mono version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class KDE_EXPORT MonoFFTScope : public FFTScope
+{
+public:
+ MonoFFTScope(int timeout=250, int pid=0);
+ virtual ~MonoFFTScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself
+ **/
+ virtual void scopeEvent(float *data, int len)
+ { (void)data; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&data);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int bands() const;
+ virtual void setBands(float f);
+
+private:
+ Noatun::FFTScope *mScope;
+ long mId;
+};
+
+
+/**
+ * Base class for all kinds of scope visualizations, i.e. if you want to display
+ * the currently played waveform
+ **/
+class Scope : public Visualization
+{
+public:
+ Scope(int interval, int pid=0);
+
+ /**
+ * the "width" of each scopeEvent
+ **/
+ virtual int samples() const=0;
+
+ virtual void setSamples(int )=0;
+};
+
+/**
+ * An easy to use scope visualization, mono version.
+ * Note: Of course this one also works for audio with more than one channel
+ * You certainly want to reimplement scopeEvent()
+ */
+class KDE_EXPORT MonoScope : public Scope
+{
+public:
+ MonoScope(int timeout=250, int pid=0);
+ virtual ~MonoScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself, and process the data
+ **/
+ virtual void scopeEvent(float *data, int len)
+ {(void)data; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&data);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int samples() const;
+ virtual void setSamples(int);
+
+private:
+ Noatun::RawScope *mScope;
+ long mId;
+};
+
+/**
+ * An easy to use scope visualization, stereo version.
+ * You certainly want to reimplement scopeEvent()
+ **/
+class StereoScope : public Scope
+{
+public:
+ StereoScope(int timeout=250, int pid=0);
+ virtual ~StereoScope();
+
+ /**
+ * called according to the timeout
+ * the float contains the data, with @p len items
+ * you override this yourself, and process the data
+ **/
+ virtual void scopeEvent(float *left, float *right, int len)
+ { (void)left; (void)right; (void)len; }
+
+ /**
+ * get the current data
+ * pass a vector<float>*, this will do the rest.
+ * do not allocate it beforehand.
+ * you must then delete the vectors
+ * @returns the amount of elements for both left and right
+ **/
+ void scopeData(std::vector<float> *&left, std::vector<float> *&right);
+
+ /**
+ * reimplemented from class Visualization, you
+ * should never need to reimplement this yourself
+ **/
+ virtual void timeout();
+
+ virtual int samples() const;
+ virtual void setSamples(int);
+
+
+private:
+ Noatun::RawScopeStereo *mScope;
+ long mId;
+};
+
+/**
+ * @short Base class for session manager plugins
+ *
+ * Inherit from this one instead of Plugin if you are
+ * a session manager plugin
+ **/
+class SessionManagement : public Plugin
+{
+public:
+ SessionManagement();
+ virtual ~SessionManagement();
+
+ virtual void restore();
+};
+
+class NoatunListenerNotif;
+/**
+ * @short Abstract base class to monitor noatun
+ *
+ * So far only used for ExitNotifier.
+ **/
+class NoatunListener : public QObject
+{
+Q_OBJECT
+friend class NoatunListenerNotif;
+
+public:
+ NoatunListener(QObject *parent=0);
+ virtual ~NoatunListener();
+
+signals:
+ void event();
+
+protected:
+ virtual void message();
+
+ NoatunListenerNotif *mNotif;
+};
+
+/**
+ * @short Notifies you as soon as Noatun exits
+ *
+ * Link to libnoatun, and the signal event() will
+ * be emitted whenever noatun exits, and the best
+ * part is how it doesn't matter if noatun exits
+ * cleanly so you even get informed in case noatun
+ * crashes.
+ **/
+class ExitNotifier : public NoatunListener
+{
+public:
+ ExitNotifier(int pid, QObject *parent=0);
+ virtual ~ExitNotifier();
+
+private:
+ QCString appid;
+};
+
+/**
+ * @short Wrapper to set a bool variable as soon as Noatun exits
+ * This class can even be used when you cannot use signals/slots
+ * Example:
+ * <pre>
+ * bool noatunOk = false;
+ * new BoolNotifier(&noatunOk, new ExitNotifier(this), this);
+ * </pre>
+ *
+ * When noatunOk is false, then noatun has exited somehow.
+ **/
+class BoolNotifier : public QObject
+{
+Q_OBJECT
+public:
+ BoolNotifier(bool *value, NoatunListener *listener, QObject *parent=0);
+
+private slots:
+ void event() {*mValue=false;}
+
+private:
+ bool *mValue;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/pluginloader.h b/noatun/library/noatun/pluginloader.h
new file mode 100644
index 00000000..611fe358
--- /dev/null
+++ b/noatun/library/noatun/pluginloader.h
@@ -0,0 +1,99 @@
+#ifndef PLUGIN_LOADER_H
+#define PLUGIN_LOADER_H
+
+#include <qstring.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <noatun/app.h>
+
+#include <klibloader.h>
+#include <qdict.h>
+#include <kdemacros.h>
+
+struct NoatunLibraryInfo
+{
+ QString specfile;
+ QString filename;
+ QString author;
+ QString license;
+ QString type;
+ QString site;
+ QString email;
+ QString name;
+ QString comment;
+ QStringList require;
+};
+
+bool operator ==(const NoatunLibraryInfo &, const NoatunLibraryInfo &);
+
+class Playlist;
+class Visualization;
+class Plugin;
+
+/**
+ * Used for loading plugins at runtime
+ **/
+class KDE_EXPORT LibraryLoader
+{
+ friend class Plugin;
+ struct PluginLibrary
+ {
+ Plugin *plugin;
+ KLibrary *library;
+ };
+
+public:
+ LibraryLoader();
+ ~LibraryLoader();
+
+ QValueList<NoatunLibraryInfo> available() const;
+ QValueList<NoatunLibraryInfo> loaded() const;
+ QValueList<NoatunLibraryInfo> loadedByType(const QString &type) const;
+
+ /**
+ * loads all the enabled plugins
+ **/
+ bool loadAll(void);
+ bool loadAll(const QStringList &);
+
+ bool isLoaded(const QString &spec) const;
+ void add(const QString &spec);
+ void setModules(const QStringList &mods);
+ /**
+ * unload the plugin specified by spec
+ **/
+ bool remove(const QString &spec);
+ /**
+ * Same as the above, but does not call kapp->exit() even
+ * when the last userinterface plugin is removed. Necessary
+ * during session management (see marquis plugin).
+ * ### BIC: merge with above with terminateOnLastUI = true
+ */
+ bool remove(const QString &spec, bool terminateOnLastUI);
+ /**
+ * unload the plugin that is referenced by @par plugin
+ **/
+ bool remove(const LibraryLoader::PluginLibrary *plugin);
+ bool remove(const Plugin *plugin);
+
+ Playlist *playlist() const;
+
+ /**
+ * This is needed for the Plugin-List-View
+ * to see what plugins are required to show
+ * (when required by another noatun-plugin)
+ **/
+ NoatunLibraryInfo getInfo(const QString &spec) const;
+ QPtrList<Plugin> plugins() const;
+
+private:
+ bool loadSO(const QString &spec);
+ void removeNow(const QString &spec);
+
+private:
+ QDict<LibraryLoader::PluginLibrary> mLibHash;
+ Playlist *mPlaylist;
+};
+
+#endif
+
diff --git a/noatun/library/noatun/pref.h b/noatun/library/noatun/pref.h
new file mode 100644
index 00000000..95be5e0e
--- /dev/null
+++ b/noatun/library/noatun/pref.h
@@ -0,0 +1,91 @@
+#ifndef NOATUNPREF_H
+#define NOATUNPREF_H
+
+#include <kdialogbase.h>
+#include <qptrlist.h>
+#include <kdemacros.h>
+
+class CModule;
+
+/**
+ * Noatun configuration dialog
+ **/
+class NoatunPreferences : public KDialogBase
+{
+Q_OBJECT
+friend class CModule;
+
+public:
+ /**
+ * @internal
+ **/
+ NoatunPreferences(QWidget *);
+
+public:
+ /**
+ * Display noatun preferences dialog
+ **/
+ virtual void show();
+ /**
+ * Display noatun preferences dialog showing @p module
+ * Useful if you want to display your own plugin configuration tab
+ **/
+ virtual void show(CModule *module);
+
+protected:
+ virtual void slotOk();
+ virtual void slotApply();
+
+private:
+ void add(CModule *);
+ void remove(CModule *);
+
+private:
+ class NoatunPreferencesPrivate;
+ NoatunPreferencesPrivate *d;
+
+ QPtrList<CModule> mModules;
+};
+
+/**
+ * @short Base class for a configuration sheet that is shown in preferences dialog
+ *
+ * Create your GUI in constructor, reimplement reopen() and save() and
+ * you're all set.
+ **/
+class KDE_EXPORT CModule : public QWidget
+{
+Q_OBJECT
+
+public:
+ /**
+ * arguments are short and long descriptions
+ * for this module, respectively
+ *
+ * parent is the object that is this modules virtual-parent.
+ * When that is deleted, this also will go away, automagically.
+ **/
+ CModule(const QString &name, const QString &description, const QString &icon, QObject *parent=0);
+
+ virtual ~CModule();
+
+public slots:
+ /**
+ * save all your options, and apply them
+ **/
+ virtual void save() {}
+ /**
+ * reload all options (e.g., read config files)
+ **/
+ virtual void reopen() {}
+
+private slots:
+ void ownerDeleted();
+
+private:
+ class CModulePrivate;
+ CModulePrivate *d;
+};
+
+
+#endif // NOATUNPREF_H
diff --git a/noatun/library/noatun/scrollinglabel.h b/noatun/library/noatun/scrollinglabel.h
new file mode 100644
index 00000000..1615a64f
--- /dev/null
+++ b/noatun/library/noatun/scrollinglabel.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef SCROLLING_LABEL_H
+#define SCROLLING_LABEL_H
+
+#include <qwidget.h>
+/**
+ * A clever label that scrolls its contents as soon as there is not enough
+ * space to show everything at once.
+ **/
+class ScrollingLabel : public QWidget
+{
+ Q_OBJECT
+
+ public:
+ ScrollingLabel(const QString &initialText,QWidget *parent,
+ const char * name = 0);
+ virtual ~ScrollingLabel();
+
+ /**
+ * Sets the label's text
+ * set @p time (ms) if you want to display a temporary message
+ **/
+ virtual void setText(const QString &text, int time = -1);
+
+ virtual QSize sizeHint() const;
+ virtual QSize minimumSizeHint() const;
+
+ /**
+ * @return the label's text.
+ **/
+ virtual QString text() const;
+
+ /**
+ * Used to enable/disable scrolling manually
+ */
+ virtual void setScroll(bool on);
+
+ protected:
+
+ virtual void paintEvent(QPaintEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+ protected slots:
+
+ virtual void scroll();
+ virtual void restoreText();
+
+ private:
+
+ void _update();
+
+ class Private;
+ Private * d;
+};
+
+#endif
+// vim:ts=2:sw=2:tw=78:noet
diff --git a/noatun/library/noatun/stdaction.h b/noatun/library/noatun/stdaction.h
new file mode 100644
index 00000000..7cac67fe
--- /dev/null
+++ b/noatun/library/noatun/stdaction.h
@@ -0,0 +1,204 @@
+#ifndef _NOATUNSTDACTION_H_
+#define _NOATUNSTDACTION_H_
+
+#include <kaction.h>
+#include <kactionclasses.h>
+#include <kdemacros.h>
+class KPopupMenu;
+
+/**
+ * Holds all noatun related actions
+ * @short noatun specific actions
+ * @author Charles Samuels
+ **/
+namespace NoatunStdAction
+{
+
+/**
+ * An action starting noatun playback
+ **/
+class PlayAction : public KAction
+{
+Q_OBJECT
+public:
+ PlayAction(QObject *parent, const char *name);
+private slots:
+ void playing();
+ void notplaying();
+};
+
+/**
+ * An action starting/stopping noatun playback
+ **/
+class PlaylistAction : public KToggleAction
+{
+Q_OBJECT
+public:
+ PlaylistAction(QObject *parent, const char *name);
+private slots:
+ void shown();
+ void hidden();
+};
+
+/**
+ * actionmenu that holds all plugin defined actions
+ * @author Stefan Gehn
+ */
+class PluginActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ PluginActionMenu(QObject *parent, const char *name);
+ /**
+ * inserts the given @p action into the action-menu
+ * @param action the action to insert
+ * @param index defines the place where the action gets displayed in
+ */
+ virtual void insert (KAction *action, int index=-1);
+ /**
+ * removes the given @p action into the action-menu
+ */
+ virtual void remove(KAction *action);
+ /**
+ * Wrapper method for old Noatun API
+ * <b>DON'T USE</b>
+ **/
+ int menuAdd(const QString &text, const QObject *receiver, const char *member);
+ /**
+ * Wrapper method for old Noatun API
+ * <b>DON'T USE</b>
+ **/
+ void menuRemove(int id);
+private:
+ int mCount;
+};
+
+/**
+ * actionmenu that holds all vis-plugins for easier enabling/disabling
+ * @author Stefan Gehn
+ */
+class VisActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ VisActionMenu(QObject *parent, const char *name);
+private slots:
+ void fillPopup();
+ void toggleVisPlugin(int);
+private:
+ QMap<int, QString>mSpecMap;
+};
+
+
+/**
+ * actionmenu that holds all looping modes
+ * @author Stefan Gehn
+ */
+class LoopActionMenu : public KActionMenu
+{
+Q_OBJECT
+public:
+ LoopActionMenu(QObject *parent, const char *name);
+private slots:
+ void updateLooping(int);
+ void loopNoneSelected();
+ void loopSongSelected();
+ void loopPlaylistSelected();
+ void loopRandomSelected();
+private:
+ KRadioAction *mLoopNone;
+ KRadioAction *mLoopSong;
+ KRadioAction *mLoopPlaylist;
+ KRadioAction *mLoopRandom;
+};
+
+
+/**
+ * @return pointer to a KAction which opens the effects dialog on activation
+ */
+KDE_EXPORT KAction *effects(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to a KAction which opens the equalizer dialog on activation
+ */
+KDE_EXPORT KAction *equalizer(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to a KAction which goes back one track on activation
+ */
+KDE_EXPORT KAction *back(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which stops playback on activation
+ */
+KDE_EXPORT KAction *stop(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which starts/pauses playback on activation
+ */
+KDE_EXPORT KAction *playpause(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KAction which advances one track on activation
+ */
+KDE_EXPORT KAction *forward(QObject *parent = 0, const char *name = 0);
+/**
+ * @return pointer to a KToggleAction which shows/hides the playlist
+ */
+KDE_EXPORT KToggleAction *playlist(QObject *parent = 0, const char *name = 0);
+
+/**
+ * loop action
+ **/
+KDE_EXPORT LoopActionMenu *loop(QObject *parent, const char *name);
+
+/**
+ * play action
+ */
+KDE_EXPORT KAction *play(QObject *parent = 0, const char *name = 0);
+
+/**
+ * pause action
+ */
+KDE_EXPORT KAction *pause(QObject *parent = 0, const char *name = 0);
+
+/**
+ * @return pointer to the global PluginActionMenu object (there is only one instance)
+ */
+KDE_EXPORT PluginActionMenu *actions();
+
+/**
+ * @return pointer to a VisActionMenu object
+ */
+KDE_EXPORT VisActionMenu *visualizations(QObject *parent = 0, const char *name = 0);
+
+/**
+ * The global popupmenu of noatun, there's not two or three but only one of these :)
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT ContextMenu
+{
+public:
+ static KPopupMenu *createContextMenu(QWidget *p);
+
+ /**
+ * One menu to show them all, One menu to find them
+ * One menu to bring them all and in the darkness bind them
+ *
+ * In the land of Noatun where the oceans die
+ */
+ static KPopupMenu *contextMenu();
+
+ /**
+ * Show the context menu at point
+ **/
+ static void showContextMenu(const QPoint &);
+
+ /**
+ * show the context menu at the mouse's current position
+ **/
+ static void showContextMenu();
+private:
+ static KPopupMenu *mContextMenu;
+};
+
+}
+
+#endif
diff --git a/noatun/library/noatun/stereobuttonaction.h b/noatun/library/noatun/stereobuttonaction.h
new file mode 100644
index 00000000..ce423b64
--- /dev/null
+++ b/noatun/library/noatun/stereobuttonaction.h
@@ -0,0 +1,31 @@
+#ifndef STEREOBUTTONACTION_H
+#define STEREOBUTTONACTION_H
+
+#include <kaction.h>
+
+namespace NoatunStdAction
+{
+
+/**
+ * No, I never owned StereoButtonAction, but I'm tired
+ * and PlayAction is already taken.
+ */
+class StereoButtonAction : public KAction
+{
+Q_OBJECT
+public:
+ StereoButtonAction(const QString& text, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QIconSet& pix, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QString& pix, int accel = 0, QObject* parent = 0, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QIconSet& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+ StereoButtonAction(const QString& text, const QString& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name = 0 );
+public slots:
+ void disable(void);
+ void enable(void);
+ void toggleEnabled(void);
+};
+
+}
+
+#endif
diff --git a/noatun/library/noatun/vequalizer.h b/noatun/library/noatun/vequalizer.h
new file mode 100644
index 00000000..73c421e1
--- /dev/null
+++ b/noatun/library/noatun/vequalizer.h
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2003 Charles Samuels <charles@kde.org>
+ *
+ * This file is hereby licensed under the GNU General Public License version
+ * 2 or later at your option.
+ *
+ * This file is licensed under the Qt Public License version 1 with the
+ * condition that the licensed will be governed under the Laws of California
+ * (USA) instead of Norway. Disputes will be settled in Santa Clara county
+ * courts.
+ *
+ * This file is licensed under the following additional license. Be aware
+ * that it is identical to the BSD license, except for the added clause 3:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. By integrating this software into any other software codebase, you waive
+ all rights to any patents associated with the stated codebase.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef NOATUN_VEQUALIZER_H
+#define NOATUN_VEQUALIZER_H
+
+#include <qptrlist.h>
+#include <qobject.h>
+#include <kurl.h>
+#include <kdemacros.h>
+class VBand;
+
+/**
+ * Abstract base class for VInterpolation and VEqualizer
+ **/
+class VBandsInterface
+{
+ friend class VBand;
+
+public:
+ VBandsInterface();
+ virtual ~VBandsInterface();
+
+ virtual int bands() const=0;
+ virtual VBand band(int num)=0;
+ /**
+ * same as @ref band(num)
+ **/
+ VBand operator [] (int num);
+
+private:
+ virtual int level(int index) const=0;
+ virtual void setLevel(int index, int level)=0;
+};
+
+
+class VInterpolation;
+class VEqualizer;
+
+/**
+ * Represents a single band in a vequalizer
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VBand
+{
+ friend class VInterpolation;
+ friend class VEqualizer;
+
+ struct Private;
+ VBand::Private *d;
+
+private:
+ VBand(VBandsInterface *bands, int index, int start, int end);
+
+public:
+ ~VBand();
+
+ VBand(const VBand &copy);
+ VBand & operator =(const VBand &copy);
+
+ /**
+ * the intensity of the change.
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ int level() const;
+ void setLevel(int l);
+
+ int start() const;
+ int end() const;
+
+ /**
+ * the middle between start and end
+ **/
+ int center() const;
+
+ QString formatStart(bool withHz=true) const;
+ QString formatEnd(bool withHz=true) const;
+ /**
+ * return the format for center()
+ **/
+ QString format(bool withHz=true) const;
+};
+
+
+/**
+ * This class is an interpolated representation to the Eq data.
+ * This means that no matter how many bands
+ * the real equalizer has, your interpolated
+ * version appears like it has only as many
+ * bands as you asked for. You can continue
+ * to interact with all interpolations and
+ * the true equalizer as normal. They even
+ * modify eachother according to a Spline
+ * interpolation.
+ *
+ * @short interpolated representation of Eq data
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VInterpolation : public QObject, public VBandsInterface
+{
+ Q_OBJECT
+ struct Private;
+ Private *d;
+ friend class VBand;
+
+public:
+ /**
+ * create an interpolation with the one and only
+ * Noatun equalizer, and @p bands
+ **/
+ VInterpolation(int bands);
+ virtual ~VInterpolation();
+
+ virtual int bands() const;
+ virtual VBand band(int num);
+ virtual int level(int index) const;
+ virtual void setLevel(int index, int level);
+
+signals:
+ void changed();
+
+private:
+ void refresh();
+ void getFrequencies(int num, int *high, int *low) const;
+ /**
+ * where on the spline is my own interpolation's bandNum
+ * @returns an x value on the spline
+ **/
+ double onSpline(int bandNum) const;
+};
+
+class VPreset;
+
+/**
+ * @short Noatun EQ
+ * @author Charles Samuels
+ **/
+class KDE_EXPORT VEqualizer : public QObject, public VBandsInterface
+{
+ Q_OBJECT
+ friend class VBand;
+ friend class VPreset;
+ friend class Engine;
+ friend class NoatunApp;
+ friend class VInterpolation;
+
+ struct Private;
+ Private *d;
+
+ VEqualizer();
+ ~VEqualizer();
+ void init();
+
+public:
+
+ /**
+ * @return the frequencies to use with @p num many
+ * bands. This is a list of the upper frequency
+ * of each band, for example: { 40, 60, 100 } if
+ * you hear up to 40hz
+ **/
+ static QValueList<int> frequencies(int num);
+
+ /**
+ * @return number of bands I have, which may be different
+ * than what setBands was called with
+ * @sa setBands
+ **/
+ int bands() const;
+
+ /**
+ * @return first frequency I operate on, currently 20
+ *
+ * does not apply to an interpolation
+ **/
+ static int start();
+ /**
+ * @return last frequency I operate on, currently 20000
+ */
+ static int end();
+
+ /**
+ * @return maximum amount of bands I may have
+ **/
+ int maxBands() const;
+
+ /**
+ * the minimum number of bands I may have (2)
+ **/
+ int minBands() const;
+
+ VBand band(int num);
+
+ bool isEnabled() const;
+
+ /**
+ * returns the level of preamp (-100 to 100 normally)
+ **/
+ int preamp() const;
+
+public: // serialization
+ /**
+ * save the current levels
+ * all noatun equalizer files have the "*.noatunequalizer"
+ * pattern. Nevertheless, the file can be identified
+ * by magic, so it's not required
+ **/
+ bool save(const KURL &file, const QString &friendly) const;
+
+ /**
+ * restore the EQ settings from this file
+ **/
+ bool load(const KURL &file);
+
+ /**
+ * convert the current EQ settings to string form,
+ * suitable for storage, the given string is the title
+ *
+ * @see fromString
+ **/
+ QString toString(const QString &name="stored") const;
+
+ /**
+ * undo a toString, restoring the EQ
+ * to the settings in the given string,
+ * emitting the changed signals
+ **/
+ bool fromString(const QString &str);
+
+public: // presets
+ /**
+ * create a preset with such a name
+ * and remember that it'l return an invalied Preset
+ * if the name already exists
+ *
+ * If smart is true, append a number to the end
+ * of the name, if one already exists by the given
+ **/
+ VPreset createPreset(const QString &name, bool smart=true);
+
+ /**
+ * return all the presets
+ * remember to setAutoDelete on this
+ **/
+ QValueList<VPreset> presets() const;
+
+ /**
+ * @returns the preset with the given name
+ * or an invalid Preset if none exists
+ **/
+ VPreset presetByName(const QString &name);
+
+ /**
+ * @returns the preset in the given file
+ * or an invalid Preset if none exists
+ **/
+ VPreset presetByFile(const QString &file);
+
+ /**
+ * @returns whether a preset with the
+ * given name exists
+ **/
+ bool presetExists(const QString &name) const;
+
+signals:
+ /**
+ * emitted when the number of bands has changed (and hence
+ * all my associated Bands are useless
+ **/
+ void changedBands();
+
+ /**
+ * emitted when something changes. Preamplication, band level,
+ * number of bands, enabled/disabled
+ **/
+ void changed();
+ /**
+ * emitted when the value of one or more of the bands
+ * has changed, but not immediately after
+ * a changedBands
+ **/
+ void modified();
+
+ void preampChanged();
+ void preampChanged(int);
+
+ void enabled();
+ void disabled();
+
+ void enabled(bool e);
+
+ /**
+ * emitted when a preset is created
+ **/
+ void created(VPreset preset);
+
+ /**
+ * emitted when the given @p preset is
+ * selected
+ **/
+ void selected(VPreset preset);
+
+ /**
+ * when @p preset has been renamed
+ **/
+ void renamed(VPreset preset);
+
+ /**
+ * the given @p preset has been removed
+ **/
+ void removed(VPreset preset);
+public slots:
+ /**
+ * set the preamplification
+ * it's logarithmic. 0 is no change
+ * negative numbers are loss in intensity
+ * positive numbers are a gain
+ * And +-100 is the most you'd need to go
+ **/
+ void setPreamp(int p);
+ /**
+ * turn on EQ
+ */
+ void enable();
+ /**
+ * turn off EQ
+ */
+ void disable();
+ /**
+ * turn on/off EQ depending on @p e
+ */
+ void setEnabled(bool e);
+ void setBands(int bands);
+ void setBands(int bands, bool interpolate);
+
+private:
+ virtual int level(int index) const;
+ virtual void setLevel(int index, int level);
+ void setLevels(const QValueList<int> &levels);
+
+private:
+ /**
+ * tell the filter to start using the new settings
+ * if @p full is false, it doesn't take the full
+ * update, but just the values (not the number of
+ * bands
+ **/
+ void update(bool full=false);
+};
+
+/**
+ * a preset stores the state of the equalizer
+ *
+ * VEqualizer provides a way to get a list of these
+ * or access them by name
+ *
+ * this acts as a reference to the preset, so
+ * it might be invalid in which case
+ * isValid() is false, isNull() is true, and
+ * operator bool() return false
+ **/
+class VPreset
+{
+ friend class VEqualizer;
+
+ struct Private;
+ Private *d;
+
+ void *_bc;
+ VPreset(const QString &file);
+public:
+ VPreset();
+ VPreset(const VPreset &copy);
+ VPreset & operator=(const VPreset &copy);
+ ~VPreset();
+ bool operator ==(const VPreset &other) const;
+
+ /**
+ * @returns the name of the preset, which is user visible
+ **/
+ QString name() const;
+ /**
+ * set the user-presentable name of the preset
+ *
+ * Equalizer will emit renamed
+ *
+ * @returns success. If another preset is named
+ * this, it'l fail.
+ **/
+ bool setName(const QString &name);
+
+ /**
+ * @returns the file that this preset is in
+ **/
+ QString file() const;
+
+ bool isValid() const;
+ bool isNull() const { return !isValid(); }
+ operator bool() const { return isValid(); }
+
+ /**
+ * save the state of the equalizer into this preset
+ **/
+ void save();
+
+ /**
+ * load the preset into the equalizer
+ **/
+ void load() const;
+
+ /**
+ * obliterate this preset!
+ **/
+ void remove();
+};
+
+#endif
diff --git a/noatun/library/noatun/video.h b/noatun/library/noatun/video.h
new file mode 100644
index 00000000..3655c489
--- /dev/null
+++ b/noatun/library/noatun/video.h
@@ -0,0 +1,60 @@
+#ifndef NOATUN__VIDEO_H
+#define NOATUN__VIDEO_H
+
+#include <kvideowidget.h>
+#include <arts/kmedia2.h>
+
+class QPopupMenu;
+
+/**
+ * a widget that contains the video being played
+ **/
+class VideoFrame : public KVideoWidget
+{
+Q_OBJECT
+ struct Private;
+ VideoFrame::Private *d;
+
+ static QPtrList<VideoFrame> frames;
+ static VideoFrame *whose;
+
+public:
+ VideoFrame(KXMLGUIClient *clientParent, QWidget *parent=0, const char *name=0, WFlags f=0);
+ VideoFrame(QWidget *parent = 0, const char *name=0, WFlags f=0);
+ ~VideoFrame();
+
+ /**
+ * which one has the video (or will have it next, if no video is playing)
+ **/
+ static VideoFrame *playing();
+
+ QPopupMenu *popupMenu(QWidget *parent);
+ QPopupMenu *popupMenu() { return popupMenu(this); }
+
+public slots:
+ /**
+ * only one VideoFrame can be playing a video, make this the one
+ **/
+ void give();
+
+private slots:
+ void stopped();
+ void changed();
+
+signals:
+ /**
+ * signaled when video is playing in here, when
+ * (width*height) != 0
+ **/
+ void acquired();
+ /**
+ * signaled when video is no longer playing
+ * here, when (width*heoght) == 0
+ */
+ void lost();
+};
+
+
+
+#endif
+
diff --git a/noatun/library/noatunarts/Equalizer.mcopclass b/noatun/library/noatunarts/Equalizer.mcopclass
new file mode 100644
index 00000000..85dac1eb
--- /dev/null
+++ b/noatun/library/noatunarts/Equalizer.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Equalizer,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/EqualizerSSE.mcopclass b/noatun/library/noatunarts/EqualizerSSE.mcopclass
new file mode 100644
index 00000000..9002a4ad
--- /dev/null
+++ b/noatun/library/noatunarts/EqualizerSSE.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::EqualizerSSE,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Equalizer_impl.cpp b/noatun/library/noatunarts/Equalizer_impl.cpp
new file mode 100644
index 00000000..5d852b17
--- /dev/null
+++ b/noatun/library/noatunarts/Equalizer_impl.cpp
@@ -0,0 +1,472 @@
+/*
+Copyright (C) 2001 Charles Samuels <charles@kde.org>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this library; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+*/
+
+#include "noatunarts.h"
+#include "artsflow.h"
+#include "fft.h"
+#include <stdsynthmodule.h>
+#include <math.h>
+#include <cstring>
+
+using namespace std;
+using namespace Arts;
+
+/**
+ * This class is _VERY_ picky
+ * which is why Noatun has it's own Equalizer class,
+ * that does all the error checking and sends it to here
+ **/
+
+namespace Noatun
+{
+
+void resize(vector<float> &vec, unsigned int newsize)
+{
+ while (newsize < vec.size())
+ vec.pop_back();
+ while (newsize > vec.size())
+ vec.push_back(0.0);
+}
+
+class Equalizer_impl : public Equalizer_skel, public StdSynthModule
+{
+ vector<float> mLevels;
+
+ vector<BandPassInfo> mBandLeft, mBandRight;
+
+ vector<float> mLevelWidths;
+ vector<float> mLevelCenters;
+
+ bool mEnabled;
+ float mPreamp;
+
+
+ float *mBuffer;
+ unsigned int mBufferLength;
+
+ void reinit()
+ {
+ mBandLeft.clear();
+ mBandRight.clear();
+ for (unsigned int i=0; i< mLevelWidths.size(); ++i)
+ {
+ BandPassInfo nfo;
+ BandPassInit(&nfo, mLevelCenters[i], mLevelWidths[i]);
+ mBandLeft.push_back(nfo);
+ mBandRight.push_back(nfo);
+ }
+
+ }
+
+public:
+ void set(const std::vector<float>& levels, const std::vector<float>& centers, const std::vector<float>& widths)
+ {
+ mLevelCenters=centers;
+ mLevelWidths=widths;
+
+ mLevels=levels;
+ reinit();
+ }
+
+ vector<float>* levelCenters()
+ {
+ return new vector<float>(mLevelCenters);
+ }
+
+ void levelCenters(const vector<float> &l)
+ {
+ mLevelCenters=l;
+ reinit();
+ }
+
+ vector<float>* levelWidths()
+ {
+ return new vector<float>(mLevelWidths);
+ }
+
+ void levelWidths(const vector<float> &l)
+ {
+ mLevelWidths=l;
+ reinit();
+ }
+
+ vector<float>* levels()
+ {
+ return new vector<float>(mLevels);
+ }
+
+ void levels(const vector<float> &l)
+ {
+ mLevels=l;
+ reinit();
+ }
+
+ long bands()
+ {
+ return mLevels.size();
+ }
+
+ void bands(long b)
+ {
+ resize(mLevels, (int)b);
+ resize(mLevelWidths, (int)b);
+ resize(mLevelCenters, (int)b);
+ reinit();
+ }
+
+ long enabled()
+ {
+ return (long)mEnabled;
+ }
+
+ void enabled(long enabled)
+ {
+ mEnabled=(bool)enabled;
+ }
+
+ float preamp()
+ {
+ return mPreamp;
+ }
+
+ void preamp(float a)
+ {
+ mPreamp=a;
+ }
+
+ void streamInit()
+ {
+
+ }
+
+ void streamStart()
+ {
+
+ }
+
+/* BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ * BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ */
+
+ void calculateBlock(unsigned long samples)
+ {
+ // works by separating the bands
+ // multiplying, then adding
+ if (mEnabled && samples && &mLevels.front())
+ {
+
+ { // preamp;
+
+ float *left=inleft;
+ float *right=inright;
+ float *end=left+samples;
+
+ float *oleft=outleft;
+ float *oright=outright;
+
+ while (left<end)
+ {
+ // see the _long_ comment in
+ // kdemultimedia/arts/modules/synth_std_equalizer_impl.cc
+ if (::fabs(*left) + ::fabs(*right) < 0.00000001)
+ goto copy; // if you apologize, it's becomes ok
+ *oleft=*left * mPreamp;
+ *oright=*right * mPreamp;
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ BandPassInfo *leftBand=&mBandLeft.front();
+ BandPassInfo *rightBand=&mBandRight.front();
+ float *level=&mLevels.front();
+ float *end=&mLevels.back();
+ float intensity=1.0/(float)mLevels.size();
+
+ if (mBufferLength != samples)
+ {
+ delete mBuffer;
+ mBuffer = new float[samples];
+ mBufferLength = samples;
+ }
+
+ register float *buffer=mBuffer;
+ register float *bufferEnd=buffer+samples;
+ while (level<end)
+ {
+ register float *buffIter, *outIter;
+ float levelAndIntensity=*level * intensity;
+
+ BandPass(leftBand, outleft, buffer, samples);
+ for (buffIter=buffer, outIter=outleft; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ BandPass(rightBand, outright, buffer, samples);
+ for (buffIter=buffer, outIter=outright; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ ++level;
+ ++leftBand;
+ ++rightBand;
+ }
+ }
+ else
+ {
+ copy:
+ // ASM optimized, so much faster
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+ }
+
+ }
+
+ Equalizer_impl() : mEnabled(false)
+ {
+ mBuffer=0;
+ mBufferLength=0;
+ }
+
+ ~Equalizer_impl()
+ {
+ delete [] mBuffer;
+ }
+
+ // speed hack! assume that someone else will
+ // suspend us
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+
+
+
+
+
+class EqualizerSSE_impl : public EqualizerSSE_skel, public StdSynthModule
+{
+ vector<float> mLevels;
+
+ vector<BandPassInfo> mBandLeft, mBandRight;
+
+ vector<float> mLevelWidths;
+ vector<float> mLevelCenters;
+
+ bool mEnabled;
+ float mPreamp;
+
+
+
+ void reinit()
+ {
+ mBandLeft.clear();
+ mBandRight.clear();
+ for (unsigned int i=0; i< mLevelWidths.size(); ++i)
+ {
+ BandPassInfo nfo;
+ BandPassInit(&nfo, mLevelCenters[i], mLevelWidths[i]);
+ mBandLeft.push_back(nfo);
+ mBandRight.push_back(nfo);
+ }
+
+ }
+
+public:
+ void set(const std::vector<float>& levels, const std::vector<float>& centers, const std::vector<float>& widths)
+ {
+ mLevelCenters=centers;
+ mLevelWidths=widths;
+
+ mLevels=levels;
+ reinit();
+ }
+
+ vector<float>* levelCenters()
+ {
+ return new vector<float>(mLevelCenters);
+ }
+
+ void levelCenters(const vector<float> &l)
+ {
+ mLevelCenters=l;
+ reinit();
+ }
+
+ vector<float>* levelWidths()
+ {
+ return new vector<float>(mLevelWidths);
+ }
+
+ void levelWidths(const vector<float> &l)
+ {
+ mLevelWidths=l;
+ reinit();
+ }
+
+ vector<float>* levels()
+ {
+ return new vector<float>(mLevels);
+ }
+
+ void levels(const vector<float> &l)
+ {
+ mLevels=l;
+ reinit();
+ }
+
+ long bands()
+ {
+ return mLevels.size();
+ }
+
+ void bands(long b)
+ {
+ resize(mLevels, (int)b);
+ resize(mLevelWidths, (int)b);
+ resize(mLevelCenters, (int)b);
+ reinit();
+ }
+
+ long enabled()
+ {
+ return (long)mEnabled;
+ }
+
+ void enabled(long enabled)
+ {
+ mEnabled=(bool)enabled;
+ }
+
+ float preamp()
+ {
+ return mPreamp;
+ }
+
+ void preamp(float a)
+ {
+ mPreamp=a;
+ }
+
+ void streamInit()
+ {
+
+ }
+
+ void streamStart()
+ {
+
+ }
+
+/* BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ * BandPassInit(&nfoLeft, 15000.0, 5000.0);
+ */
+
+ void calculateBlock(unsigned long samples)
+ {
+#ifdef __i386__
+ // works by separating the bands
+ // multiplying, then adding
+ if (mEnabled && samples)
+ {
+ if (*inleft + *inright == 0.0)
+ goto copy; // just shut up :)
+
+ { // preamp;
+
+ float *left=inleft;
+ float *right=inright;
+ float *end=left+samples;
+
+ float *oleft=outleft;
+ float *oright=outright;
+
+ while (left<end)
+ {
+ *oleft=*left * mPreamp;
+ *oright=*right * mPreamp;
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ BandPassInfo *leftBand=&mBandLeft.front();
+ BandPassInfo *rightBand=&mBandRight.front();
+ float *level=&mLevels.front();
+ float *end=&mLevels.back();
+ float intensity=1.0/(float)mLevels.size();
+
+ register float *buffer=new float[samples];
+ register float *bufferEnd=buffer+samples;
+ while (level<end)
+ {
+ register float *buffIter, *outIter;
+ float levelAndIntensity=*level * intensity;
+
+ BandPassSSE(leftBand, outleft, buffer, samples);
+ for (buffIter=buffer, outIter=outleft; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ BandPassSSE(rightBand, outright, buffer, samples);
+ for (buffIter=buffer, outIter=outright; buffIter<bufferEnd; ++buffIter, ++outIter)
+ *outIter+=(*buffIter) * levelAndIntensity;
+
+ ++level;
+ ++leftBand;
+ ++rightBand;
+ }
+ delete [] buffer;
+ }
+ else
+ {
+ copy:
+ // ASM optimized, so much faster
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+ }
+#else
+ (void)samples; // squelch warnings
+#endif
+ }
+
+ EqualizerSSE_impl() : mEnabled(false)
+ {
+
+ }
+
+ ~EqualizerSSE_impl()
+ {
+ }
+
+ // speed hack! assume that someone else will
+ // suspend us
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+
+REGISTER_IMPLEMENTATION(Equalizer_impl);
+REGISTER_IMPLEMENTATION(EqualizerSSE_impl);
+
+}
+
+#undef SAMPLES
+
diff --git a/noatun/library/noatunarts/FFTScope.mcopclass b/noatun/library/noatunarts/FFTScope.mcopclass
new file mode 100644
index 00000000..65826259
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScope.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::FFTScope,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/FFTScopeStereo.mcopclass b/noatun/library/noatunarts/FFTScopeStereo.mcopclass
new file mode 100644
index 00000000..f463a2ed
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScopeStereo.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::FFTScopeStereo,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/FFTScopes.cpp b/noatun/library/noatunarts/FFTScopes.cpp
new file mode 100644
index 00000000..ab2a3bbb
--- /dev/null
+++ b/noatun/library/noatunarts/FFTScopes.cpp
@@ -0,0 +1,422 @@
+/*
+Copyright (C) 2000 Stefan Westerfeld <stefan@space.twc.de>
+ 2000 Charles Samuels <charles@kde.org>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this library; see the file COPYING.LIB. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+*/
+
+#include "noatunarts.h"
+#include "artsflow.h"
+#include "fft.h"
+#include <stdsynthmodule.h>
+#include <cmath>
+#include <cstring>
+
+#include <iostream>
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun
+{
+
+#define SAMPLES 4096
+
+static void doFft(float combine, float *inBuffer, vector<float> &scope)
+{
+ float out_real[SAMPLES],out_img[SAMPLES];
+ fft_float(SAMPLES,0,inBuffer,0,out_real,out_img);
+
+ scope.clear();
+
+ int previous=0;
+ int index=20;
+
+ while (previous < 2048 && index < 2048)
+ {
+ int end = int(std::exp(double(index)*combine));
+ float xrange = 0.0;
+
+ while (previous < end)
+ {
+ xrange += (std::fabs(out_img[previous]) + std::fabs(out_real[previous]));
+ previous++;
+ }
+
+ xrange /= float(SAMPLES);
+ scope.push_back(xrange);
+
+ index++;
+ }
+}
+
+
+class FFTScopeStereo_impl : public FFTScopeStereo_skel, public StdSynthModule
+{
+protected:
+ vector<float> mScopeLeft;
+ vector<float> mScopeRight;
+
+ float mCombine;
+
+ float *mWindow;
+
+ float *mInBufferLeft, *mInBufferRight;
+ unsigned long mInBufferPos;
+
+public:
+ void bandResolution(float res) { mCombine=res; }
+ float bandResolution() { return mCombine; }
+ void streamInit()
+ {
+ unsigned long i;
+ for(i=0;i<SAMPLES;i++)
+ {
+ float x = (float)i/(float)SAMPLES;
+ // double it, since we're at half intensity without
+ // adding both channels
+ mWindow[i] = sin(x*DDC_PI)*sin(x*DDC_PI)*2;
+
+ mInBufferLeft[i] = 0;
+ mInBufferRight[i] = 0;
+ }
+ doFft(mCombine, mInBufferLeft, mScopeLeft);
+ doFft(mCombine, mInBufferRight, mScopeRight);
+ }
+ void streamStart()
+ {
+ mInBufferPos = 0;
+ }
+ vector<float> *scopeLeft()
+ {
+ return new vector<float>(mScopeLeft);
+ }
+ vector<float> *scopeRight()
+ {
+ return new vector<float>(mScopeRight);
+ }
+ /*
+ in audio stream inleft, inright;
+ out audio stream outleft, outright;
+ */
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+ for(i=0;i<samples;i++)
+ {
+ mInBufferLeft[mInBufferPos] = inleft[i]*mWindow[mInBufferPos];
+ mInBufferRight[mInBufferPos] = inright[i]*mWindow[mInBufferPos];
+ if(++mInBufferPos == SAMPLES)
+ {
+ doFft(mCombine, mInBufferLeft, mScopeLeft);
+ doFft(mCombine, mInBufferRight, mScopeRight);
+ mInBufferPos = 0;
+ }
+ /*
+ monitoring only tasks can't be done with that StereoEffect
+ interface nicely - copy input to output until there is
+ something better
+ */
+ outleft[i] = inleft[i];
+ outright[i] = inright[i];
+ }
+ }
+
+ FFTScopeStereo_impl() : mCombine(0.152492)
+ {
+ mWindow = new float[SAMPLES];
+ mInBufferLeft = new float[SAMPLES];
+ mInBufferRight = new float[SAMPLES];
+
+ }
+ ~FFTScopeStereo_impl()
+ {
+ delete [] mWindow;
+ delete [] mInBufferLeft;
+ delete [] mInBufferRight;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+class FFTScope_impl : public FFTScope_skel, public StdSynthModule
+{
+protected:
+ vector<float> mScope;
+
+ float mCombine;
+
+ float *mWindow;
+ float *mInBuffer;
+ unsigned long mInBufferPos;
+
+public:
+ void bandResolution(float res) { mCombine=res; }
+ float bandResolution() { return mCombine; }
+ void streamInit()
+ {
+ unsigned long i;
+ for(i=0;i<SAMPLES;i++)
+ {
+ float x = (float)i/(float)SAMPLES;
+ mWindow[i] = sin(x*DDC_PI)*sin(x*DDC_PI);
+ mInBuffer[i] = 0;
+ }
+ doFft(mCombine, mInBuffer, mScope); // initialize so that we never return an empty scope
+ }
+ void streamStart()
+ {
+ mInBufferPos = 0;
+ }
+ vector<float> *scope()
+ {
+ return new vector<float>(mScope);
+ }
+
+ /*
+ in audio stream inleft, inright;
+ out audio stream outleft, outright;
+ */
+ void calculateBlock(unsigned long samples)
+ {
+ unsigned long i;
+
+ float *inBufferIt=mInBuffer+mInBufferPos;
+ float *inleftIt=inleft;
+ float *inrightIt=inright;
+ float *windowIt=mWindow+mInBufferPos;
+
+ for(i=0;i<samples;i++)
+ {
+ *inBufferIt = (*inleftIt + *inrightIt)* (*windowIt);
+ if(++mInBufferPos == SAMPLES)
+ {
+ doFft(mCombine, mInBuffer, mScope);
+ mInBufferPos = 0;
+ inBufferIt=mInBuffer;
+ }
+ inBufferIt++;
+ inleftIt++;
+ inrightIt++;
+ windowIt++;
+ }
+ /*
+ monitoring only tasks can't be done with that StereoEffect
+ interface nicely - copy input to output until there is
+ something better
+ */
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+
+ }
+
+ FFTScope_impl() : mCombine(0.152492)
+ {
+ mWindow = new float[SAMPLES];
+ mInBuffer = new float[SAMPLES];
+ }
+
+ ~FFTScope_impl()
+ {
+ delete [] mWindow;
+ delete [] mInBuffer;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+class RawScope_impl : public RawScope_skel, public StdSynthModule
+{
+protected:
+ float *mScope;
+
+ int mScopeLength;
+ float *mScopeEnd;
+ float *mCurrent;
+
+public:
+ vector<float> *scope()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrent, (mScopeEnd - mCurrent) * sizeof(float));
+ memcpy(front + (mScopeEnd - mCurrent)*sizeof(float), mScope,
+ (mCurrent - mScope) * sizeof(float));
+ return buf;
+ }
+
+ void buffer(long len)
+ {
+ delete [] mScope;
+
+ mScopeLength=len;
+ mScope=new float[len];
+ mScopeEnd=mScope+mScopeLength;
+ mCurrent=mScope;
+
+ memset(mScope, 0, mScopeLength);
+ }
+
+ long buffer()
+ {
+ return mScopeLength;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrent<mScopeEnd && i<samples; ++mCurrent, ++i)
+ {
+ *mCurrent = inleft[i] + inright[i];
+ }
+ if (mCurrent>=mScopeEnd)
+ mCurrent=mScope;
+ }
+
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+
+ }
+
+ RawScope_impl()
+ {
+ mScope=0;
+ buffer(512);
+
+ }
+
+ ~RawScope_impl()
+ {
+ delete [] mScope;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+class RawScopeStereo_impl : public RawScopeStereo_skel, public StdSynthModule
+{
+protected:
+ int mScopeLength;
+
+ float *mScopeLeft;
+ float *mScopeEndLeft;
+ float *mCurrentLeft;
+
+ float *mScopeRight;
+ float *mScopeEndRight;
+ float *mCurrentRight;
+
+public:
+ vector<float> *scopeLeft()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrentLeft, (mScopeEndLeft - mCurrentLeft) * sizeof(float));
+ memcpy(front + (mScopeEndLeft - mCurrentLeft)*sizeof(float), mScopeLeft,
+ (mCurrentLeft - mScopeLeft) * sizeof(float));
+ return buf;
+ }
+
+ vector<float> *scopeRight()
+ {
+ vector<float> *buf = new vector<float>;
+ buf->resize(mScopeLength);
+ char *front = (char *)(&buf->front());
+ memcpy(front, mCurrentRight, (mScopeEndRight - mCurrentRight) * sizeof(float));
+ memcpy(front + (mScopeEndRight - mCurrentRight)*sizeof(float), mScopeRight,
+ (mCurrentRight - mScopeRight) * sizeof(float));
+ return buf;
+ }
+
+ void buffer(long len)
+ {
+ delete [] mScopeRight;
+ delete [] mScopeLeft;
+
+ mScopeLength=len;
+ mScopeRight=new float[len];
+ mScopeLeft=new float[len];
+ mScopeEndRight=mScopeRight+mScopeLength;
+ mScopeEndLeft=mScopeLeft+mScopeLength;
+ mCurrentRight=mScopeRight;
+ mCurrentLeft=mScopeLeft;
+
+ memset(mScopeRight, 0, mScopeLength);
+ memset(mScopeLeft, 0, mScopeLength);
+ }
+
+ long buffer()
+ {
+ return mScopeLength;
+ }
+
+ void calculateBlock(unsigned long samples)
+ {
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrentLeft<mScopeEndLeft && i<samples; ++mCurrentLeft, ++i)
+ {
+ *mCurrentLeft = inleft[i];
+ }
+ if (mCurrentLeft>=mScopeEndLeft)
+ mCurrentLeft=mScopeLeft;
+ }
+
+ for (unsigned long i=0; i<samples; ++i)
+ {
+ for (; mCurrentRight<mScopeEndRight && i<samples; ++mCurrentRight, ++i)
+ {
+ *mCurrentRight = inright[i];
+ }
+ if (mCurrentRight>=mScopeEndRight)
+ mCurrentRight=mScopeRight;
+ }
+
+ memcpy(outleft, inleft, sizeof(float)*samples);
+ memcpy(outright, inright, sizeof(float)*samples);
+ }
+
+ RawScopeStereo_impl()
+ {
+ mScopeLeft=mScopeRight=0;
+ buffer(512);
+
+ }
+
+ ~RawScopeStereo_impl()
+ {
+ delete [] mScopeRight;
+ delete [] mScopeLeft;
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+};
+
+
+
+REGISTER_IMPLEMENTATION(FFTScope_impl);
+REGISTER_IMPLEMENTATION(FFTScopeStereo_impl);
+REGISTER_IMPLEMENTATION(RawScope_impl);
+REGISTER_IMPLEMENTATION(RawScopeStereo_impl);
+
+}
+
+#undef SAMPLES
diff --git a/noatun/library/noatunarts/Listener.mcopclass b/noatun/library/noatunarts/Listener.mcopclass
new file mode 100644
index 00000000..81f19d38
--- /dev/null
+++ b/noatun/library/noatunarts/Listener.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Listener,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Makefile.am b/noatun/library/noatunarts/Makefile.am
new file mode 100644
index 00000000..a08e279d
--- /dev/null
+++ b/noatun/library/noatunarts/Makefile.am
@@ -0,0 +1,33 @@
+INCLUDES= -I$(kde_includes)/arts $(all_includes)
+KDE_OPTIONS = nofinal
+
+lib_LTLIBRARIES = libnoatunarts.la
+libnoatunarts_la_SOURCES = noatunarts.cc fft.c Equalizer_impl.cpp \
+ FFTScopes.cpp StereoEffectStack_impl.cpp \
+ StereoVolumeControl_impl.cpp Session_impl.cpp
+libnoatunarts_la_COMPILE_FIRST = noatunarts.h
+libnoatunarts_la_LDFLAGS = $(all_libraries) -avoid-version -no-undefined
+libnoatunarts_la_LIBADD = -lkmedia2_idl -lsoundserver_idl -lartsflow
+libnoatunarts_la_METASOURCES = AUTO
+
+noatunarts.mcoptype: noatunarts.h
+noatunarts.mcopclass: noatunarts.h
+
+noatunarts.cc noatunarts.h: noatunarts.idl
+ $(MCOPIDL) -t -I$(kde_includes)/arts $(srcdir)/noatunarts.idl
+
+mcoptypedir = $(libdir)/mcop
+mcoptype_DATA = noatunarts.mcoptype noatunarts.mcopclass
+
+mcopclassdir = $(libdir)/mcop/Noatun
+mcopclass_DATA = Equalizer.mcopclass FFTScopeStereo.mcopclass StereoEffectStack.mcopclass \
+ EqualizerSSE.mcopclass RawScope.mcopclass StereoVolumeControl.mcopclass \
+ FFTScope.mcopclass RawScopeStereo.mcopclass StereoVolumeControlSSE.mcopclass \
+ Session.mcopclass Listener.mcopclass
+
+noatuninclude_HEADERS= noatunarts.h
+
+noatunincludedir = $(includedir)/noatun
+
+DISTCLEANFILES = noatunarts.cc noatunarts.h noatunarts.mcopclass noatunarts.mcoptype
+
diff --git a/noatun/library/noatunarts/RawScope.mcopclass b/noatun/library/noatunarts/RawScope.mcopclass
new file mode 100644
index 00000000..fb3d95be
--- /dev/null
+++ b/noatun/library/noatunarts/RawScope.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::RawScope,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/RawScopeStereo.mcopclass b/noatun/library/noatunarts/RawScopeStereo.mcopclass
new file mode 100644
index 00000000..cad987ee
--- /dev/null
+++ b/noatun/library/noatunarts/RawScopeStereo.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::RawScopeStereo,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Session.mcopclass b/noatun/library/noatunarts/Session.mcopclass
new file mode 100644
index 00000000..036b8409
--- /dev/null
+++ b/noatun/library/noatunarts/Session.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::Session,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/Session_impl.cpp b/noatun/library/noatunarts/Session_impl.cpp
new file mode 100644
index 00000000..63912801
--- /dev/null
+++ b/noatun/library/noatunarts/Session_impl.cpp
@@ -0,0 +1,82 @@
+#include "noatunarts.h"
+#include <list>
+#include <algorithm>
+
+using namespace Arts;
+using namespace std;
+
+static bool compareArtsObjects(const Noatun::Listener &left, const Noatun::Listener &right)
+{
+ return left._isEqual(right);
+}
+
+list<Noatun::Listener>::iterator find(list<Noatun::Listener> &v, const Noatun::Listener &is,
+ bool (*compare)(const Noatun::Listener& left, const Noatun::Listener& right))
+{
+ for (list<Noatun::Listener>::iterator i=v.begin(); i!=v.end(); ++i)
+ {
+ if ((*compare)(is, *i))
+ return i;
+ }
+
+ return v.end();
+}
+
+static void sendMessage(Noatun::Listener l)
+{
+ l.message();
+}
+
+namespace Noatun
+{
+
+class Session_impl : public Session_skel
+{
+ list<Listener> listeners;
+
+ long mPid;
+
+public:
+
+ ~Session_impl()
+ {
+ for_each(listeners.begin(), listeners.end(), sendMessage);
+ }
+
+ long pid() { return mPid; }
+ void pid(long p) { mPid=p; }
+
+
+ void addListener(Noatun::Listener listener)
+ {
+ listeners.push_back(listener);
+ }
+
+ void removeListener(Noatun::Listener listener)
+ {
+ list<Listener>::iterator i=
+ find(listeners, listener, &compareArtsObjects);
+ if (i!=listeners.end())
+ listeners.erase(i);
+ }
+
+};
+
+class Listener_impl : public Listener_skel
+{
+
+private:
+ virtual void message()
+ {
+ // hmm
+ }
+
+};
+
+
+REGISTER_IMPLEMENTATION(Session_impl);
+REGISTER_IMPLEMENTATION(Listener_impl);
+
+
+} // namespace Noatun
+
diff --git a/noatun/library/noatunarts/StereoEffectStack.mcopclass b/noatun/library/noatunarts/StereoEffectStack.mcopclass
new file mode 100644
index 00000000..71c9a9cf
--- /dev/null
+++ b/noatun/library/noatunarts/StereoEffectStack.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoEffectStack,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoEffectStack_impl.cpp b/noatun/library/noatunarts/StereoEffectStack_impl.cpp
new file mode 100644
index 00000000..684d9694
--- /dev/null
+++ b/noatun/library/noatunarts/StereoEffectStack_impl.cpp
@@ -0,0 +1,253 @@
+#include "noatunarts.h"
+
+#include <artsflow.h>
+#include <flowsystem.h>
+#include <stdsynthmodule.h>
+#include <debug.h>
+
+using namespace std;
+using namespace Arts;
+
+namespace Noatun
+{
+class StereoEffectStack_impl : public StereoEffectStack_skel, public StdSynthModule
+{
+ public:
+ long nextID;
+
+ struct EffectEntry
+ {
+ StereoEffect effect;
+ string name;
+ long id;
+ };
+ list<EffectEntry *> fx;
+
+ void xconnect(bool connect, Object from, string fromP, Object to, string toP)
+ {
+ if(connect)
+ from._node()->connect(fromP,to._node(),toP);
+ else
+ from._node()->disconnect(fromP,to._node(),toP);
+ }
+
+ void xvirtualize(bool connect, string myPort, Object impl, string implPort)
+ {
+ if(connect)
+ _node()->virtualize(myPort,impl._node(),implPort);
+ else
+ _node()->devirtualize(myPort,impl._node(),implPort);
+ }
+
+ void internalconnect(bool c)
+ {
+ if(fx.empty())
+ {
+ /* no effects - forward input through to output */
+ xvirtualize(c,"outleft",Object::_from_base(this->_copy()),"inleft");
+ xvirtualize(c,"outright",Object::_from_base(this->_copy()),"inright");
+ }
+ else
+ {
+ list<EffectEntry *>::iterator ei;
+ EffectEntry *laste = 0;
+
+ long count = 0;
+ for(ei = fx.begin(); ei != fx.end(); ei++, count++)
+ {
+ EffectEntry *e = *ei;
+ if(count == 0) /* top of chain? virtualize to effect */
+ {
+ xvirtualize(c,"inleft",e->effect,"inleft");
+ xvirtualize(c,"inright",e->effect,"inright");
+ }
+ else /* not top? connect last effect to current effect */
+ {
+ xconnect(c,laste->effect,"outleft",e->effect,"inleft");
+ xconnect(c,laste->effect,"outright",e->effect,"inright");
+ }
+ laste = e;
+ }
+ /* end: virtualize effect output to our output */
+ xvirtualize(c,"outleft",laste->effect,"outleft");
+ xvirtualize(c,"outright",laste->effect,"outright");
+ }
+ }
+ void disconnect() { internalconnect(false); }
+ void reconnect() { internalconnect(true); }
+
+
+ long insertAfter(long after, StereoEffect effect, const string &name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+ disconnect();
+
+ list<EffectEntry*>::iterator i = fx.begin();
+
+ bool found=false;
+ // seek through until we find 'after'
+ while(i != fx.end())
+ if((*i)->id == after)
+ {
+ found = true;
+ break;
+ }
+ else
+ i++;
+
+ long newId=0;
+ if (found)
+ {
+ i++;
+ EffectEntry *e = new EffectEntry;
+ e->effect=effect;
+ e->name=name;
+ e->id=nextID++;
+ fx.insert(i, e);
+ newId=e->id;
+ }
+ else
+ arts_warning("StereoEffectStack::insertAfter failed. "
+ "id %d not found?", after);
+
+ reconnect();
+ return newId;
+
+ }
+
+ void move(long after, long item)
+ {
+ arts_return_if_fail(item != 0);
+ disconnect();
+
+ list<EffectEntry*>::iterator iAfter=fx.begin();
+ bool found=false;
+ if (after)
+ while(iAfter != fx.end())
+ if((*iAfter)->id == after)
+ {
+ found = true;
+ iAfter++;
+ break;
+ }
+ else
+ iAfter++;
+ else
+ found=true;
+
+ list<EffectEntry*>::iterator iItem=fx.begin();
+ while (iItem != fx.end())
+ if((*iItem)->id == item)
+ {
+ found &= true;
+ break;
+ }
+ else
+ iItem++;
+ if (!found)
+ arts_warning("StereoEffectStack::move couldn't find items");
+ else
+ {
+ fx.insert(iAfter, *iItem);
+ fx.erase(iItem);
+ }
+
+ reconnect();
+
+ }
+
+ vector<long> *effectList()
+ {
+ vector<long> *items=new vector<long>;
+ for (list<EffectEntry*>::iterator i=fx.begin(); i!=fx.end();i++)
+ items->push_back((*i)->id);
+ return items;
+ }
+
+ // as stolen from stereoeffectstack_impl.cc
+ StereoEffectStack_impl() : nextID(1)
+ {
+ reconnect();
+ }
+
+ ~StereoEffectStack_impl()
+ {
+ // disconnect remaining effects
+ EffectEntry *laste = 0;
+ list<EffectEntry *>::iterator ei;
+
+ for(ei = fx.begin(); ei != fx.end(); ei++)
+ {
+ EffectEntry *e = *ei;
+ if(laste)
+ {
+ xconnect(false,laste->effect,"outleft",e->effect,"inleft");
+ xconnect(false,laste->effect,"outright",e->effect,"inright");
+ }
+ laste = e;
+ }
+ // delete remaining effect entries
+ for(ei = fx.begin(); ei != fx.end(); ei++)
+ delete *ei;
+ fx.clear();
+ }
+ long insertTop(StereoEffect effect, const string& name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+
+ disconnect();
+ EffectEntry *e = new EffectEntry();
+ e->effect = effect;
+ e->name = name;
+ e->id = nextID++;
+ fx.push_front(e);
+ reconnect();
+ return e->id;
+ }
+ long insertBottom(StereoEffect effect, const string& name)
+ {
+ arts_return_val_if_fail(!effect.isNull(),0);
+
+ disconnect();
+ EffectEntry *e = new EffectEntry();
+ e->effect = effect;
+ e->name = name;
+ e->id = nextID++;
+ fx.push_back(e);
+ reconnect();
+ return e->id;
+ }
+
+ void remove(long ID)
+ {
+ arts_return_if_fail(ID != 0);
+
+ bool found = false;
+ disconnect();
+ list<EffectEntry *>::iterator ei = fx.begin();
+
+ while(ei != fx.end())
+ {
+ if((*ei)->id == ID) {
+ found = true;
+ delete (*ei);
+ fx.erase(ei);
+ ei = fx.begin();
+ }
+ else ei++;
+ }
+ if(!found) {
+ arts_warning("StereoEffectStack::remove failed. id %d not found?",
+ ID);
+ }
+ reconnect();
+ }
+
+ AutoSuspendState autoSuspend() { return asSuspend; }
+
+};
+
+REGISTER_IMPLEMENTATION(StereoEffectStack_impl);
+
+}
+
diff --git a/noatun/library/noatunarts/StereoVolumeControl.mcopclass b/noatun/library/noatunarts/StereoVolumeControl.mcopclass
new file mode 100644
index 00000000..c4aded7e
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControl.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoVolumeControl,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass b/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass
new file mode 100644
index 00000000..9a979f30
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControlSSE.mcopclass
@@ -0,0 +1,4 @@
+Interface=Noatun::StereoVolumeControlSSE,Arts::StereoEffect,Arts::Object
+Language=C++
+Library=libnoatunarts.la
+
diff --git a/noatun/library/noatunarts/StereoVolumeControl_impl.cpp b/noatun/library/noatunarts/StereoVolumeControl_impl.cpp
new file mode 100644
index 00000000..425704f6
--- /dev/null
+++ b/noatun/library/noatunarts/StereoVolumeControl_impl.cpp
@@ -0,0 +1,181 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <artsflow.h>
+#include <stdsynthmodule.h>
+#include <flowsystem.h>
+#include "noatunarts.h"
+
+using namespace Arts;
+
+namespace Noatun
+{
+
+class StereoVolumeControl_impl : virtual public StereoVolumeControl_skel,
+ virtual public StdSynthModule
+{
+ float mPercent;
+ float level;
+public:
+ StereoVolumeControl_impl() : mPercent(1.0), level(0.0)
+ { }
+
+ /*attribute float scaleFactor;*/
+ void percent(float p) { mPercent=p; }
+ float percent() { return mPercent; }
+
+ void calculateBlock(unsigned long samples)
+ {
+ register float *left=inleft;
+ register float *right=inright;
+ register float *oleft=outleft;
+ register float *oright=outright;
+
+ level = *right + *left;
+
+ register float p=mPercent;
+
+ register float *end=left+samples;
+
+ while (left<end)
+ {
+ *oleft=*left * p;
+ *oright=*right * p;
+
+ ++left;
+ ++right;
+ ++oleft;
+ ++oright;
+ }
+ }
+
+ AutoSuspendState autoSuspend()
+ {
+ return (level < 0.001) ? asSuspend : asNoSuspend;
+ }
+};
+
+class StereoVolumeControlSSE_impl : virtual public Noatun::StereoVolumeControlSSE_skel,
+ virtual public StdSynthModule
+{
+ float mPercent;
+ float level;
+
+public:
+ StereoVolumeControlSSE_impl() : mPercent(1.0), level(0.0)
+ { }
+
+ /*attribute float scaleFactor;*/
+ void percent(float p) { mPercent=p; }
+ float percent() { return mPercent; }
+
+ void calculateBlock(unsigned long samples)
+ {
+#ifdef HAVE_X86_SSE
+ float *left=inleft;
+ float *right=inright;
+ float *oleft=outleft;
+ float *oright=outright;
+
+ level = *right + *left;
+
+ // need to copy the data members to locals to get enough
+ // spare registers (malte)
+
+ long p = (long)(mPercent*100.0);
+ __asm__ __volatile__(
+ "pushl $100 \n"
+ "fildl (%%esp) \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "addl $4, %%esp \n"
+#endif
+ "fildl %5 \n"
+ "fdivp \n" // percent / 100.0
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "pushl $100 \n"
+#endif
+ "fstps (%%esp) \n"
+ "movss (%%esp), %%xmm1 \n"
+ "shufps $0x00, %%xmm1, %%xmm1 \n" // percentage in all of xmm1
+ "addl $4, %%esp \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "subl $4, %4 \n"
+ "jl .l2 \n" // samples < 4
+#else
+ "pushl %4 \n" // save sample count
+ "shrl $2, %4 \n"
+ "jz .l2 \n" // samples < 4
+#endif
+ "xorl %%ecx, %%ecx \n"
+
+ ".l1: \n"
+ // left
+ "movups (%0, %%ecx, 8), %%xmm0 \n"
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %2, %%eax \n"
+ "movups %%xmm0, (%%eax, %%ecx, 8) \n"
+ // right
+ "movups (%1, %%ecx, 8), %%xmm0 \n"
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %3, %%eax \n"
+ "movups %%xmm0, (%%eax, %%ecx, 8) \n"
+
+ "incl %%ecx \n"
+ "incl %%ecx \n"
+#if defined(__GNUC__) && (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
+ "subl $4, %4 \n"
+ "jge .l1 \n"
+ ".l2: \n"
+ "addl $4, %4 \n"
+#else
+ "decl %4 \n"
+ "jnz .l1 \n"
+ ".l2: \n"
+ "popl %4 \n" // restore sample count
+ "andl $3, %4 \n"
+#endif
+ "jz .l4 \n"
+
+ // calculate remaining samples for samples % 4 != 0
+ "shll $1, %%ecx \n"
+ ".l3: \n"
+ "movss (%0, %%ecx, 4), %%xmm0 \n" // load left
+ "movss (%1, %%ecx, 4), %%xmm2 \n" // load right
+ "shufps $0x00, %%xmm2, %%xmm0 \n" // both channels in xmm0
+ "mulps %%xmm1, %%xmm0 \n"
+ "movl %2, %%eax \n"
+ "movss %%xmm0, (%%eax, %%ecx, 4) \n" // store left
+ "shufps $0x02, %%xmm0, %%xmm0 \n"
+ "movl %3, %%eax \n"
+ "movss %%xmm0, (%%eax, %%ecx, 4) \n" // store right
+ "incl %%ecx \n"
+ "decl %4 \n"
+ "jnz .l3 \n"
+
+ ".l4: \n"
+ "emms \n"
+ :
+ : "r" (left), // %0
+ "r" (right), // %1
+ "m" (oleft), // %2
+ "m" (oright), // %3
+ "r" (samples), // %4
+ "m" (p) // %5
+ : "eax", "ecx"
+ );
+#endif
+ }
+
+ AutoSuspendState autoSuspend()
+ {
+ return (level < 0.001) ? asSuspend : asNoSuspend;
+ }
+};
+
+REGISTER_IMPLEMENTATION(StereoVolumeControlSSE_impl);
+REGISTER_IMPLEMENTATION(StereoVolumeControl_impl);
+
+}
+
diff --git a/noatun/library/noatunarts/fft.c b/noatun/library/noatunarts/fft.c
new file mode 100644
index 00000000..86d647fb
--- /dev/null
+++ b/noatun/library/noatunarts/fft.c
@@ -0,0 +1,384 @@
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include "fft.h"
+
+#define TRUE 1
+#define FALSE 0
+
+/*
+ band pass filter for the Eq. This is a modification of Kai Lassfolk's work, as
+ removed from the Sound Processing Kit:
+
+ Sound Processing Kit - A C++ Class Library for Audio Signal Processing
+ Copyright (C) 1995-1998 Kai Lassfolk
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define SAMPLERATE 44100
+
+#ifndef M_PI
+#define M_PI DDC_PI
+#endif
+
+void BandPassInit(struct BandPassInfo *ip, float center, float bw)
+{
+ ip->center = center;
+ ip->bandwidth = bw;
+
+ ip->C = 1.0 / tan(M_PI * bw / SAMPLERATE);
+ ip->D = 2 * cos(2 * M_PI * center / SAMPLERATE);
+
+ ip->a[0] = 1.0 / (1.0 + ip->C);
+ ip->a[1] = 0.0;
+ ip->a[2] = -ip->a[0];
+
+ ip->b[0] = -ip->C * ip->D * ip->a[0];
+ ip->b[1] = (ip->C - 1.0) * ip->a[0];
+
+ ip->bufferX[0] = ip->bufferX[1] = 0.0;
+ ip->bufferY[0] = ip->bufferY[1] = 0.0;
+}
+void BandPassSSE(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples)
+{
+#ifdef HAVE_X86_SSE
+ __asm__(
+ "testl %0, %0 \n"
+ "jz .l5 \n" /* if (!samples) */
+
+ "movl %1, %%ecx \n"
+ "movups 0x10(%%ecx), %%xmm2 \n" /* ip->a[0] */
+ "shufps $0x00, %%xmm2, %%xmm2 \n" /* ip->a[0] all over xmm3 */
+ "movups 0x14(%%ecx), %%xmm4 \n" /* xmm4 = {ip->a[1], ip->a[2], ip->b} */
+ "movups 0x24(%%ecx), %%xmm5 \n" /* xmm5 = {ip->bufferX, ip->bufferY} */
+ "xorl %%ecx, %%ecx \n" /* i = 0 */
+ "movl $1, %%edx \n" /* j = 1 */
+ "prefetcht0 (%2) \n"
+ ".l1: \n"
+
+ "decl %%edx \n" /* --j */
+ "jnz .l4 \n" /* if (j) */
+
+ /* only load single values if less than four remain in inbuffer */
+ "testl $0xfffffffc, %0 \n"
+ "jnz .l2 \n"
+ "movss (%2, %%ecx, 4), %%xmm3\n"
+ "movl $1, %%edx \n"
+ "jmp .l3 \n"
+ ".l2: \n"
+ /* {inbuffer[i], inbuffer[i+1], inbuffer[i+2], inbuffer[i+3]} * ip->a[0] */
+ "movups (%2, %%ecx, 4), %%xmm3\n"
+ "movl $3, %%edx \n" /* j = 3 */
+ ".l3: \n"
+ "movaps %%xmm3, %%xmm6 \n"
+ "mulps %%xmm2, %%xmm3 \n"
+ ".l4: \n"
+
+ /* {ip->a[1], ip->a[2], ip->b} * {ip->bufferX, ip->bufferY} */
+ "movaps %%xmm4, %%xmm0 \n"
+ "mulps %%xmm5, %%xmm0 \n"
+ "movaps %%xmm0, %%xmm1 \n"
+ /* xmm0 = {xmm0[0] + xmm0[1], <unused>, xmm0[2] + xmm0[3], <unused>} */
+ "shufps $0xb1, %%xmm0, %%xmm1 \n"
+ "addps %%xmm1, %%xmm0 \n"
+ /* xmm0[0] -= xmm0[2] */
+ "movhlps %%xmm0, %%xmm1 \n"
+ "subss %%xmm1, %%xmm0 \n"
+ "addss %%xmm3, %%xmm0 \n" /* xmm0[0] += inbuffer[i] * ip->a[0] */
+ "movss %%xmm0, (%3, %%ecx, 4)\n" /* outbuffer[i] = xmm0[0] */
+
+ /* xmm5 = {inbuffer[i], xmm5[0], outbuffer[i], xmm5[2]} */
+ "shufps $0x24, %%xmm5, %%xmm0 \n"
+ "shufps $0x81, %%xmm0, %%xmm5 \n"
+ "movss %%xmm6, %%xmm5 \n"
+
+ /* right-shift xmm3 (inbuffer * ip->a[0]) and xmm6 (inbuffer) */
+ "shufps $0x39, %%xmm3, %%xmm3 \n"
+ "shufps $0x39, %%xmm6, %%xmm6 \n"
+
+ "incl %%ecx \n" /* ++i */
+ "decl %0 \n"
+ "jnz .l1 \n"
+
+ "movl %1,%%ecx \n"
+ "movups %%xmm5, 0x24(%%ecx) \n" /* {ip->bufferX, ip->bufferY} = xmm5 */
+ "emms \n"
+ ".l5: \n"
+ :
+ : "r" (samples), /* %0 */
+ "m" (ip), /* %1 */
+ "r" (inbuffer), /* %2 */
+ "r" (outbuffer) /* %3 */
+ : "ecx", "edx");
+#endif
+}
+
+void BandPass(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples)
+{
+ unsigned long i;
+ for (i=0; i<samples; ++i)
+ {
+ outbuffer[i] = ip->a[0] * inbuffer[i] + ip->a[1] * ip->bufferX[0] + ip->a[2]
+ * ip->bufferX[1] - ip->b[0] * ip->bufferY[0] - ip->b[1]
+ * ip->bufferY[1];
+
+ ip->bufferX[1] = ip->bufferX[0];
+ ip->bufferX[0] = inbuffer[i];
+ ip->bufferY[1] = ip->bufferY[0];
+ ip->bufferY[0] = outbuffer[i];
+ }
+}
+
+
+/*============================================================================
+
+ fftmisc.c - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Helper routines for Fast Fourier Transform implementation.
+ Contains common code for fft_float() and fft_double().
+
+ See also:
+ fourierf.c
+ fourierd.c
+ ..\include\fourier.h
+
+ Revision history:
+
+1998 September 19 [Don Cross]
+ Improved the efficiency of IsPowerOfTwo().
+ Updated coding standards.
+
+============================================================================*/
+
+
+#define BITS_PER_WORD (sizeof(unsigned) * 8)
+
+
+static int IsPowerOfTwo ( unsigned x )
+{
+ if ( x < 2 )
+ return FALSE;
+
+ if ( x & (x-1) ) /* Thanks to 'byang' for this cute trick! */
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static unsigned NumberOfBitsNeeded ( unsigned PowerOfTwo )
+{
+ unsigned i;
+
+ if ( PowerOfTwo < 2 )
+ {
+ fprintf (
+ stderr,
+ ">>> Error in fftmisc.c: argument %d to NumberOfBitsNeeded is too small.\n",
+ PowerOfTwo );
+
+ exit(1);
+ }
+
+ for ( i=0; ; i++ )
+ {
+ if ( PowerOfTwo & (1 << i) )
+ return i;
+ }
+}
+
+
+
+static unsigned ReverseBits ( unsigned ind, unsigned NumBits )
+{
+ unsigned i, rev;
+
+ for ( i=rev=0; i < NumBits; i++ )
+ {
+ rev = (rev << 1) | (ind & 1);
+ ind >>= 1;
+ }
+
+ return rev;
+}
+
+/*
+static double Index_to_frequency ( unsigned NumSamples, unsigned Index )
+{
+ if ( Index >= NumSamples )
+ return 0.0;
+ else if ( Index <= NumSamples/2 )
+ return (double)Index / (double)NumSamples;
+
+ return -(double)(NumSamples-Index) / (double)NumSamples;
+}
+*/
+#undef TRUE
+#undef FALSE
+#undef BITS_PER_WORD
+/*============================================================================
+
+ fourierf.c - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Contains definitions for doing Fourier transforms
+ and inverse Fourier transforms.
+
+ This module performs operations on arrays of 'float'.
+
+ Revision history:
+
+1998 September 19 [Don Cross]
+ Updated coding standards.
+ Improved efficiency of trig calculations.
+
+============================================================================*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "fft.h"
+
+#define CHECKPOINTER(p) CheckPointer(p,#p)
+
+static void CheckPointer ( const void *p, const char *name )
+{
+ if ( p == NULL )
+ {
+ fprintf ( stderr, "Error in fft_float(): %s == NULL\n", name );
+ exit(1);
+ }
+}
+
+
+void fft_float (
+ unsigned NumSamples,
+ int InverseTransform,
+ float *RealIn,
+ float *ImagIn,
+ float *RealOut,
+ float *ImagOut )
+{
+ unsigned NumBits; /* Number of bits needed to store indices */
+ unsigned i, j, k, n;
+ unsigned BlockSize, BlockEnd;
+
+ double angle_numerator = 2.0 * DDC_PI;
+ double tr, ti; /* temp real, temp imaginary */
+
+ if ( !IsPowerOfTwo(NumSamples) )
+ {
+ fprintf (
+ stderr,
+ "Error in fft(): NumSamples=%u is not power of two\n",
+ NumSamples );
+
+ exit(1);
+ }
+
+ if ( InverseTransform )
+ angle_numerator = -angle_numerator;
+
+ CHECKPOINTER ( RealIn );
+ CHECKPOINTER ( RealOut );
+ CHECKPOINTER ( ImagOut );
+
+ NumBits = NumberOfBitsNeeded ( NumSamples );
+
+ /*
+ ** Do simultaneous data copy and bit-reversal ordering into outputs...
+ */
+
+ for ( i=0; i < NumSamples; i++ )
+ {
+ j = ReverseBits ( i, NumBits );
+ RealOut[j] = RealIn[i];
+ ImagOut[j] = (ImagIn == NULL) ? 0.0 : ImagIn[i];
+ }
+
+ /*
+ ** Do the FFT itself...
+ */
+
+ BlockEnd = 1;
+ for ( BlockSize = 2; BlockSize <= NumSamples; BlockSize <<= 1 )
+ {
+ double delta_angle = angle_numerator / (double)BlockSize;
+ double sm2 = sin ( -2 * delta_angle );
+ double sm1 = sin ( -delta_angle );
+ double cm2 = cos ( -2 * delta_angle );
+ double cm1 = cos ( -delta_angle );
+ double w = 2 * cm1;
+ double ar[3], ai[3];
+
+ for ( i=0; i < NumSamples; i += BlockSize )
+ {
+ ar[2] = cm2;
+ ar[1] = cm1;
+
+ ai[2] = sm2;
+ ai[1] = sm1;
+
+ for ( j=i, n=0; n < BlockEnd; j++, n++ )
+ {
+ ar[0] = w*ar[1] - ar[2];
+ ar[2] = ar[1];
+ ar[1] = ar[0];
+
+ ai[0] = w*ai[1] - ai[2];
+ ai[2] = ai[1];
+ ai[1] = ai[0];
+
+ k = j + BlockEnd;
+ tr = ar[0]*RealOut[k] - ai[0]*ImagOut[k];
+ ti = ar[0]*ImagOut[k] + ai[0]*RealOut[k];
+
+ RealOut[k] = RealOut[j] - tr;
+ ImagOut[k] = ImagOut[j] - ti;
+
+ RealOut[j] += tr;
+ ImagOut[j] += ti;
+ }
+ }
+
+ BlockEnd = BlockSize;
+ }
+
+ /*
+ ** Need to normalize if inverse transform...
+ */
+
+ if ( InverseTransform )
+ {
+ double denom = (double)NumSamples;
+
+ for ( i=0; i < NumSamples; i++ )
+ {
+ RealOut[i] /= denom;
+ ImagOut[i] /= denom;
+ }
+ }
+}
+
+
diff --git a/noatun/library/noatunarts/fft.h b/noatun/library/noatunarts/fft.h
new file mode 100644
index 00000000..e7f7804d
--- /dev/null
+++ b/noatun/library/noatunarts/fft.h
@@ -0,0 +1,88 @@
+#ifndef FFT_H
+#define FFT_H
+
+
+
+/* this is from ddcmath.h */
+
+#define DDC_PI (3.14159265358979323846)
+
+/*============================================================================
+
+ fourier.h - Don Cross <dcross@intersrv.com>
+
+ http://www.intersrv.com/~dcross/fft.html
+
+ Contains definitions for doing Fourier transforms
+ and inverse Fourier transforms.
+
+============================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** If you change anything here, make sure to check if
+** the offsets used in the asm version of BandPass() are affected
+*/
+struct BandPassInfo
+{
+ float center;
+ float bandwidth;
+
+ float C, D;
+ float a[3], b[2];
+
+ float bufferX[2];
+ float bufferY[2];
+
+};
+
+void BandPassInit(struct BandPassInfo *i, float center, float bw);
+void BandPassSSE(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples);
+void BandPass(struct BandPassInfo *ip, float *inbuffer, float *outbuffer, unsigned long samples);
+
+/*
+** fft() computes the Fourier transform or inverse transform
+** of the complex inputs to produce the complex outputs.
+** The number of samples must be a power of two to do the
+** recursive decomposition of the FFT algorithm.
+** See Chapter 12 of "Numerical Recipes in FORTRAN" by
+** Press, Teukolsky, Vetterling, and Flannery,
+** Cambridge University Press.
+**
+** Notes: If you pass ImaginaryIn = NULL, this function will "pretend"
+** that it is an array of all zeroes. This is convenient for
+** transforming digital samples of real number data without
+** wasting memory.
+*/
+
+void fft_float (
+ unsigned NumSamples, /* must be a power of 2 */
+ int InverseTransform, /* 0=forward FFT, 1=inverse FFT */
+ float *RealIn, /* array of input's real samples */
+ float *ImaginaryIn, /* array of input's imag samples */
+ float *RealOut, /* array of output's reals */
+ float *ImaginaryOut ); /* array of output's imaginaries */
+
+
+/*
+int IsPowerOfTwo ( unsigned x );
+unsigned NumberOfBitsNeeded ( unsigned PowerOfTwo );
+unsigned ReverseBits ( unsigned index, unsigned NumBits );
+*/
+
+/*
+** The following function returns an "abstract frequency" of a
+** given index into a buffer with a given number of frequency samples.
+** Multiply return value by sampling rate to get frequency expressed in Hz.
+*/
+/*
+double Index_to_frequency ( unsigned NumSamples, unsigned Index );
+*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* FFT_H */
diff --git a/noatun/library/noatunarts/noatunarts.idl b/noatun/library/noatunarts/noatunarts.idl
new file mode 100644
index 00000000..809e2bb6
--- /dev/null
+++ b/noatun/library/noatunarts/noatunarts.idl
@@ -0,0 +1,91 @@
+#include <artsflow.idl>
+
+module Noatun
+{
+
+interface Equalizer : Arts::StereoEffect
+{
+ attribute sequence<float> levelCenters;
+ attribute sequence<float> levelWidths;
+ attribute sequence<float> levels;
+
+ attribute long bands;
+ attribute long enabled;
+ attribute float preamp;
+ void set(sequence<float> levels, sequence<float> centers, sequence<float> widths);
+};
+
+interface EqualizerSSE : Arts::StereoEffect
+{
+ attribute sequence<float> levelCenters;
+ attribute sequence<float> levelWidths;
+ attribute sequence<float> levels;
+
+ attribute long bands;
+ attribute long enabled;
+ attribute float preamp;
+ void set(sequence<float> levels, sequence<float> centers, sequence<float> widths);
+};
+
+interface FFTScope : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scope();
+};
+
+interface FFTScopeStereo : Arts::StereoEffect
+{
+ attribute float bandResolution;
+ sequence<float> scopeRight();
+ sequence<float> scopeLeft();
+};
+
+interface RawScope : Arts::StereoEffect
+{
+ attribute long buffer;
+ sequence<float> scope();
+};
+
+interface RawScopeStereo : Arts::StereoEffect
+{
+ attribute long buffer;
+ sequence<float> scopeLeft();
+ sequence<float> scopeRight();
+};
+
+interface StereoEffectStack : Arts::StereoEffect
+{
+ long insertAfter(long after, Arts::StereoEffect effect, string name);
+ void move(long after, long item);
+ sequence<long> effectList();
+ long insertTop(Arts::StereoEffect effect, string name);
+ long insertBottom(Arts::StereoEffect effect, string name);
+ void remove(long ID);
+};
+
+interface StereoVolumeControl : Arts::StereoEffect
+{
+ attribute float percent;
+};
+
+interface StereoVolumeControlSSE : Arts::StereoEffect
+{
+ attribute float percent;
+};
+
+interface Listener
+{
+ void message();
+};
+
+interface Session
+{
+ attribute long pid;
+ void addListener(Noatun::Listener listener);
+ void removeListener(Noatun::Listener listener);
+};
+
+
+};
+
+
diff --git a/noatun/library/noatunlistview.h b/noatun/library/noatunlistview.h
new file mode 100644
index 00000000..1571f64e
--- /dev/null
+++ b/noatun/library/noatunlistview.h
@@ -0,0 +1,5 @@
+// yeah yeah yeah.. fix qt and this can go away
+
+#define private protected
+#include <qlistview.h>
+#undef private
diff --git a/noatun/library/noatunstdaction.cpp b/noatun/library/noatunstdaction.cpp
new file mode 100644
index 00000000..f2837623
--- /dev/null
+++ b/noatun/library/noatunstdaction.cpp
@@ -0,0 +1,362 @@
+#include "stdaction.h"
+#include "app.h"
+#include "player.h"
+#include "stereobuttonaction.h"
+#include "pluginloader.h"
+
+#include <khelpmenu.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kstdaction.h>
+#include <qcursor.h>
+#include <qmap.h>
+#include <kdebug.h>
+#include <kstdguiitem.h>
+
+/**
+ * A namespace to have all of noatun's standard actions
+ * This is treated like KStdAction
+ **/
+namespace NoatunStdAction
+{
+/////////////////////////////////////////////////////
+PlayAction::PlayAction(QObject *parent, const char *name)
+ : KAction(i18n("Play"), 0, napp->player(), SLOT(playpause()), parent, name)
+{
+ connect(napp->player(), SIGNAL(playing()), SLOT(playing()));
+ connect(napp->player(), SIGNAL(paused()), SLOT(notplaying()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(notplaying()));
+ if (napp->player()->isPlaying())
+ playing();
+ else if (napp->player()->isPaused() || napp->player()->isStopped())
+ notplaying();
+}
+
+void PlayAction::playing()
+{
+ setIconSet(QIconSet(SmallIcon("player_pause")));
+ setText(i18n("Pause"));
+}
+
+void PlayAction::notplaying()
+{
+ setIconSet(QIconSet(SmallIcon("player_play")));
+ setText(i18n("Play"));
+}
+/////////////////////////////////////////////////////
+
+PlaylistAction::PlaylistAction(QObject *parent, const char *name)
+ : KToggleAction(i18n("Show Playlist"), "playlist", 0, napp->player(), SLOT(toggleListView()), parent, name)
+{
+ setCheckedState(i18n("Hide Playlist"));
+ connect(napp->player(), SIGNAL(playlistShown()), SLOT(shown()));
+ connect(napp->player(), SIGNAL(playlistHidden()), SLOT(hidden()));
+ setChecked(napp->playlist()->listVisible());
+}
+
+void PlaylistAction::shown()
+{
+ setChecked(true);
+}
+
+void PlaylistAction::hidden()
+{
+ setChecked(false);
+}
+
+////////////////////////////////////////////////////
+
+PluginActionMenu::PluginActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Actions"), parent, name)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ setEnabled(false);
+ mCount=0;
+}
+
+void PluginActionMenu::insert (KAction *action, int index)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ KActionMenu::insert(action,index);
+ setEnabled(true);
+ mCount++;
+}
+
+void PluginActionMenu::remove(KAction *action)
+{
+// kdDebug(66666) << k_funcinfo << "called" << endl;
+ KActionMenu::remove(action);
+ mCount--;
+ if(mCount==0)
+ setEnabled(false);
+}
+
+int PluginActionMenu::menuAdd(const QString &text, const QObject *receiver, const char *member)
+{
+// kdDebug(66666) << k_funcinfo << "called, mCount is currently at " << mCount << endl;
+ setEnabled(true);
+ mCount++;
+ return popupMenu()->insertItem(text, receiver, member);
+}
+
+void PluginActionMenu::menuRemove(int id)
+{
+// kdDebug(66666) << k_funcinfo << "called, mCount is currently at " << mCount << endl;
+ popupMenu()->removeItem(id);
+ mCount--;
+ if(mCount==0)
+ setEnabled(false);
+}
+
+////////////////////////////////////////////////////
+
+VisActionMenu::VisActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Visualizations"), parent, name)
+{
+ connect(popupMenu(), SIGNAL(aboutToShow()), this, SLOT(fillPopup()));
+ connect(popupMenu(), SIGNAL(activated(int)), this, SLOT(toggleVisPlugin(int)));
+}
+
+void VisActionMenu::fillPopup()
+{
+ int id;
+ popupMenu()->clear();
+ mSpecMap.clear();
+
+ QValueList<NoatunLibraryInfo> available = napp->libraryLoader()->available();
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ for(QValueList<NoatunLibraryInfo>::Iterator i = available.begin(); i != available.end(); ++i)
+ {
+ if ((*i).type == "visualization")
+ {
+ id = popupMenu()->insertItem((*i).name);
+ mSpecMap[id] = (*i).specfile;
+ popupMenu()->setItemChecked(id, loaded.contains(*i));
+ }
+ }
+}
+
+void VisActionMenu::toggleVisPlugin(int id)
+{
+ if(!mSpecMap.contains(id))
+ return;
+
+ QString specfile = mSpecMap[id];
+
+ if(popupMenu()->isItemChecked(id))
+ {
+ napp->libraryLoader()->remove(specfile);
+ popupMenu()->setItemChecked(id, false);
+ }
+ else
+ {
+ napp->libraryLoader()->add(specfile);
+ popupMenu()->setItemChecked(id, true);
+ }
+}
+
+////////////////////////////////////////////////////
+
+LoopActionMenu::LoopActionMenu(QObject *parent, const char *name)
+ : KActionMenu(i18n("&Loop"), parent, name)
+{
+ mLoopNone = new KRadioAction(i18n("&None"), QString::fromLocal8Bit("noatunloopnone"),
+ 0, this, SLOT(loopNoneSelected()), this, "loop_none");
+ mLoopNone->setExclusiveGroup("loopType");
+ insert(mLoopNone);
+
+ mLoopSong = new KRadioAction(i18n("&Song"), QString::fromLocal8Bit("noatunloopsong"),
+ 0, this, SLOT(loopSongSelected()), this, "loop_song");
+ mLoopSong->setExclusiveGroup("loopType");
+ insert(mLoopSong);
+
+ mLoopPlaylist = new KRadioAction(i18n("&Playlist"), QString::fromLocal8Bit("noatunloopplaylist"),
+ 0, this, SLOT(loopPlaylistSelected()), this, "loop_playlist");
+ mLoopPlaylist->setExclusiveGroup("loopType");
+ insert(mLoopPlaylist);
+
+ mLoopRandom = new KRadioAction(i18n("&Random"), QString::fromLocal8Bit("noatunlooprandom"),
+ 0, this, SLOT(loopRandomSelected()), this, "loop_random");
+ mLoopRandom->setExclusiveGroup("loopType");
+ insert(mLoopRandom);
+
+ connect(napp->player(), SIGNAL(loopTypeChange(int)), this, SLOT(updateLooping(int)));
+
+ updateLooping(static_cast<int>(napp->player()->loopStyle()));
+}
+
+void LoopActionMenu::updateLooping(int loopType)
+{
+ switch(loopType)
+ {
+ case Player::None:
+ mLoopNone->setChecked(true);
+ setIcon("noatunloopnone");
+ break;
+ case Player::Song:
+ mLoopSong->setChecked(true);
+ setIcon("noatunloopsong");
+ break;
+ case Player::Playlist:
+ mLoopPlaylist->setChecked(true);
+ setIcon("noatunloopplaylist");
+ break;
+ case Player::Random:
+ mLoopRandom->setChecked(true);
+ setIcon("noatunlooprandom");
+ break;
+ }
+}
+
+void LoopActionMenu::loopNoneSelected()
+{
+ napp->player()->loop(Player::None);
+}
+
+void LoopActionMenu::loopSongSelected()
+{
+ napp->player()->loop(Player::Song);
+}
+
+void LoopActionMenu::loopPlaylistSelected()
+{
+ napp->player()->loop(Player::Playlist);
+}
+
+void LoopActionMenu::loopRandomSelected()
+{
+ napp->player()->loop(Player::Random);
+}
+
+////////////////////////////////////////////////////
+
+KAction *playpause(QObject *parent, const char *name)
+{
+ return new PlayAction(parent, name);
+}
+
+KAction *effects(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Effects..."), "effect", 0, napp, SLOT(effectView()), parent, name);
+}
+
+KAction *equalizer(QObject *parent, const char *name)
+{
+ return new KAction(i18n("E&qualizer..."), "equalizer", 0, napp, SLOT(equalizerView()), parent, name);
+}
+
+KAction *back(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Back"), "player_start", 0, napp->player(), SLOT(back()), parent, name);
+}
+
+KAction *stop(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("Stop"), "player_stop", 0, napp->player(), SLOT(stop()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(disable()));
+ if(napp->player()->isStopped())
+ action->disable();
+ else
+ action->enable();
+ return action;
+}
+
+KAction *forward(QObject *parent, const char *name)
+{
+ return new KAction(i18n("&Forward"), "player_end", 0, napp->player(), SLOT(forward()), parent, name);
+}
+
+KAction *play(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("&Play"), "player_play", 0, napp->player(), SLOT(playpause()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(disable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(enable()));
+ if(napp->player()->isPlaying())
+ action->disable();
+ else
+ action->enable();
+ return action;
+}
+
+KAction *pause(QObject *parent, const char *name)
+{
+ StereoButtonAction *action = new StereoButtonAction(i18n("&Pause"), "player_pause", 0, napp->player(), SLOT(playpause()), parent, name);
+ QObject::connect(napp->player(), SIGNAL(playing()), action, SLOT(enable()));
+ QObject::connect(napp->player(), SIGNAL(paused()), action, SLOT(disable()));
+ QObject::connect(napp->player(), SIGNAL(stopped()), action, SLOT(disable()));
+ if(napp->player()->isPlaying())
+ action->enable();
+ else
+ action->disable();
+ return action;
+}
+
+LoopActionMenu *loop(QObject *parent, const char *name)
+{
+ return new LoopActionMenu(parent, name);
+}
+
+PluginActionMenu *actions()
+{
+ // NoatunApp makes sure that we only have one ActionMenu around
+ return napp->pluginActionMenu();
+}
+
+VisActionMenu *visualizations(QObject *parent, const char *name)
+{
+ return new VisActionMenu(parent, name);
+}
+
+KToggleAction *playlist(QObject *parent, const char *name)
+{
+ return new PlaylistAction(parent, name);
+}
+
+KPopupMenu *ContextMenu::mContextMenu = 0;
+
+KPopupMenu *ContextMenu::contextMenu()
+{
+ if(!mContextMenu) mContextMenu = createContextMenu(0);
+
+ return mContextMenu;
+}
+
+KPopupMenu *ContextMenu::createContextMenu(QWidget *p)
+{
+ KPopupMenu *contextMenu = new KPopupMenu(p, "NoatunContextMenu");
+
+ KHelpMenu *helpmenu = new KHelpMenu(contextMenu, kapp->aboutData(), false);
+ KActionCollection* actions = new KActionCollection(helpmenu);
+
+ KStdAction::open(napp, SLOT(fileOpen()), actions)->plug(contextMenu);
+ KStdAction::quit(napp, SLOT(quit()), actions)->plug(contextMenu);
+ contextMenu->insertItem(SmallIcon("help"), KStdGuiItem::help().text(), helpmenu->menu());
+ contextMenu->insertSeparator();
+ KStdAction::preferences(napp, SLOT(preferences()), actions)->plug(contextMenu);
+ NoatunStdAction::playlist(contextMenu)->plug(contextMenu);
+ NoatunStdAction::effects(contextMenu)->plug(contextMenu);
+ NoatunStdAction::equalizer(napp)->plug(contextMenu);
+ NoatunStdAction::visualizations(napp)->plug(contextMenu);
+ napp->pluginActionMenu()->plug(contextMenu);
+
+ return contextMenu;
+}
+
+void ContextMenu::showContextMenu(const QPoint &p)
+{
+ contextMenu()->exec(p);
+}
+
+void ContextMenu::showContextMenu()
+{
+ showContextMenu(QCursor::pos());
+}
+
+} // END namespace NoatunStdAction
+
+#include "stdaction.moc"
diff --git a/noatun/library/noatuntags/Makefile.am b/noatun/library/noatuntags/Makefile.am
new file mode 100644
index 00000000..ea90faa1
--- /dev/null
+++ b/noatun/library/noatuntags/Makefile.am
@@ -0,0 +1,14 @@
+INCLUDES = -I$(top_srcdir)/noatun/library $(all_includes)
+
+lib_LTLIBRARIES = libnoatuntags.la
+
+libnoatuntags_la_SOURCES = tags.cpp
+libnoatuntags_la_LDFLAGS = -version-info 3:0:2 $(all_libraries)
+libnoatuntags_la_LIBADD = ../libnoatun.la
+
+libnoatuntags_la_METASOURCES = AUTO
+
+noatuntagsincludedir = $(includedir)/noatun
+noatuntagsinclude_HEADERS = tags.h
+
+
diff --git a/noatun/library/noatuntags/tags.cpp b/noatun/library/noatuntags/tags.cpp
new file mode 100644
index 00000000..38fd4abf
--- /dev/null
+++ b/noatun/library/noatuntags/tags.cpp
@@ -0,0 +1,229 @@
+#include "tags.h"
+#include "tagsgetter.h"
+#include <klocale.h>
+#include <qslider.h>
+#include <qspinbox.h>
+#include <kconfig.h>
+#include <qlayout.h>
+#include <qhbox.h>
+#include <qlabel.h>
+#include <qtimer.h>
+#include <noatun/player.h>
+#include <qcheckbox.h>
+
+TagsGetter *Tags::getter=0;
+
+TagsGetter::TagsGetter()
+{
+ new Control(this);
+ connect(napp->player(), SIGNAL(newSong()), SLOT(newSong()));
+}
+
+int TagsGetter::interval() const
+{
+ KGlobal::config()->setGroup("Tags");
+ return KGlobal::config()->readNumEntry("interval", 250);
+}
+
+bool TagsGetter::loadAuto() const
+{
+ KGlobal::config()->setGroup("Tags");
+ return KGlobal::config()->readBoolEntry("LoadAuto", true);
+}
+
+void TagsGetter::added(PlaylistItem &i)
+{
+ items += i;
+ killTimers();
+ startTimer(interval());
+}
+
+void TagsGetter::removed(PlaylistItem &i)
+{
+ items.remove(i);
+}
+
+void TagsGetter::getSongs()
+{
+ items = napp->playlist()->select("Tags::tagged_", "", -1, true, true);
+ killTimers();
+ startTimer(interval());
+}
+
+void TagsGetter::timerEvent(QTimerEvent *)
+{
+ if (!items.size())
+ {
+ killTimers();
+ return;
+ }
+
+ PlaylistItem item=items.first();
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (i->update(item))
+ {
+ item.setProperty("Tags::tagged_", "1");
+ if (item==napp->player()->current())
+ napp->player()->handleButtons();
+ }
+ }
+
+ items.remove(items.begin());
+}
+
+void TagsGetter::newSong()
+{
+ PlaylistItem item=napp->player()->current();
+ if (!item) return;
+
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (i->update(item))
+ {
+ item.setProperty("Tags::tagged_", "1");
+ napp->player()->handleButtons();
+ }
+ }
+ items.remove(item);
+}
+
+void TagsGetter::setInterval(int ms)
+{
+ killTimers();
+ startTimer(ms);
+
+ KGlobal::config()->setGroup("Tags");
+ KGlobal::config()->writeEntry("interval", ms);
+ KGlobal::config()->sync();
+}
+
+void TagsGetter::setLoadAuto(bool eh)
+{
+
+ KGlobal::config()->setGroup("Tags");
+ KGlobal::config()->writeEntry("LoadAuto", eh);
+ KGlobal::config()->sync();
+
+ killTimers();
+
+ if (eh) startTimer(interval());
+}
+
+void TagsGetter::associate(Tags *t)
+{
+ tags.append(t);
+ sortPriority();
+// getSongs();
+ QTimer::singleShot(interval(), this, SLOT(getSongs()));
+}
+
+void TagsGetter::sortPriority()
+{
+ // find the lowest one, since it comes first
+
+ int lowest=0;
+ for (Tags *i=tags.first(); i; i=tags.next())
+ {
+ if (lowest>i->mPriority)
+ lowest=i->mPriority;
+ }
+
+ QPtrList<Tags> sorted;
+ while (tags.count())
+ {
+ // find the one equal to myself
+ for (Tags *i=tags.first(); i;)
+ {
+ if (lowest==i->mPriority)
+ {
+ sorted.append(i);
+ tags.removeRef(i);
+ i=tags.first();
+ }
+ else
+ {
+ i=tags.next();
+ }
+ }
+ lowest++;
+ }
+
+ tags=sorted;
+}
+
+bool TagsGetter::unassociate(Tags *t)
+{
+ tags.removeRef(t);
+ if (tags.count()==0)
+ {
+ delete this;
+ return true;
+ }
+ return false;
+}
+
+Tags::Tags(int priority) : mPriority(priority)
+{
+ if (!getter)
+ getter=new TagsGetter;
+ getter->associate(this);
+}
+
+Tags::~Tags()
+{
+ if (getter->unassociate(this))
+ getter=0;
+}
+
+
+Control::Control(TagsGetter *parent)
+ : CModule(i18n("Tagging"), i18n("Settings for Tag Loaders"), "edit", parent)
+{
+ // todo
+ (void)I18N_NOOP("Rescan All Tags");
+
+ QVBoxLayout *l=new QVBoxLayout(this);
+ QCheckBox *onPlay;
+ {
+ onPlay=new QCheckBox(i18n("Load tags &automatically"), this);
+ l->addWidget(onPlay);
+ onPlay->show();
+ }
+
+ {
+ QHBox *intervalLine=new QHBox(this);
+ l->addWidget(intervalLine);
+ l->addStretch();
+
+ new QLabel(i18n(
+ "The time between each time noatun scans for a new file"
+ ", and updates tags (e.g., ID3)",
+ "Interval:"), intervalLine);
+ QSlider *slider=new QSlider(
+ 0, 2000, 100, 0, Horizontal, intervalLine
+ );
+ QSpinBox *spin=new QSpinBox(
+ 0, 2000, 10, intervalLine
+ );
+
+ spin->setSuffix(i18n("Milliseconds", " ms"));
+
+
+ connect(slider, SIGNAL(valueChanged(int)), spin, SLOT(setValue(int)));
+ connect(spin, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));
+ slider->setValue(parent->interval());
+ connect(slider, SIGNAL(valueChanged(int)), parent, SLOT(setInterval(int)));
+
+ connect(onPlay, SIGNAL(toggled(bool)), intervalLine, SLOT(setEnabled(bool)));
+ }
+ connect(onPlay, SIGNAL(toggled(bool)), parent, SLOT(setLoadAuto(bool)));
+
+ onPlay->setChecked(parent->loadAuto());
+}
+
+
+
+
+#include "tagsgetter.moc"
+
diff --git a/noatun/library/noatuntags/tags.h b/noatun/library/noatuntags/tags.h
new file mode 100644
index 00000000..ec98aef2
--- /dev/null
+++ b/noatun/library/noatuntags/tags.h
@@ -0,0 +1,35 @@
+#ifndef __NOATUN_TAGS___TACOS_ARE_YUMMY
+#define __NOATUN_TAGS___TACOS_ARE_YUMMY
+
+#include <noatun/playlist.h>
+
+class TagsGetter;
+
+class Tags
+{
+ friend class TagsGetter;
+public:
+ /**
+ * priority is how early this comes
+ * 0 means "normal"
+ * anything larger than zero means that this should come later,
+ * negative numbers mean it should come first
+ *
+ * I'm talking about the order which you're being processed
+ **/
+ Tags(int priority=0);
+ virtual ~Tags();
+
+ /**
+ * this will be called occasionally
+ * with an item you should fill up
+ **/
+ virtual bool update(PlaylistItem &item)=0;
+
+private:
+ static TagsGetter *getter;
+ int mPriority;
+};
+
+#endif
+
diff --git a/noatun/library/noatuntags/tagsgetter.h b/noatun/library/noatuntags/tagsgetter.h
new file mode 100644
index 00000000..269b7b90
--- /dev/null
+++ b/noatun/library/noatuntags/tagsgetter.h
@@ -0,0 +1,56 @@
+#ifndef TAGSGET_H
+#define TAGSGET_H
+
+#include <qobject.h>
+#include <cmodule.h>
+#include <qvaluelist.h>
+#include <qptrlist.h>
+#include "tags.h"
+
+class TagsGetter : public QObject, public PlaylistNotifier
+{
+Q_OBJECT
+public:
+ TagsGetter();
+ void associate(Tags *t);
+ // if returns true, I'm deleted
+ bool unassociate(Tags *t);
+
+ int interval() const;
+ bool loadAuto() const;
+
+public: //playlistnotifier
+ virtual void added(PlaylistItem &);
+ virtual void removed(PlaylistItem &);
+
+protected:
+ void timerEvent(QTimerEvent *);
+
+private slots:
+ // select the songs that need updating
+ void getSongs();
+
+ void newSong();
+
+public slots:
+ void setInterval(int ms);
+ void setLoadAuto(bool);
+
+private:
+ void sortPriority();
+
+private:
+ QPtrList<Tags> tags;
+ QValueList<PlaylistItem> items;
+};
+
+class Control : public CModule
+{
+Q_OBJECT
+public:
+ Control(TagsGetter* parent);
+};
+
+
+#endif
+
diff --git a/noatun/library/noatunui.cpp b/noatun/library/noatunui.cpp
new file mode 100644
index 00000000..9dbb1ff2
--- /dev/null
+++ b/noatun/library/noatunui.cpp
@@ -0,0 +1,12 @@
+#include "plugin.h"
+
+UserInterface::UserInterface()
+ : Plugin()
+{
+}
+
+UserInterface::~UserInterface()
+{
+
+}
+
diff --git a/noatun/library/player.cpp b/noatun/library/player.cpp
new file mode 100644
index 00000000..7760d9a8
--- /dev/null
+++ b/noatun/library/player.cpp
@@ -0,0 +1,379 @@
+#include "player.h"
+
+#include <noatun/playlist.h>
+#include "engine.h"
+#include "app.h"
+#include "titleproxy.h"
+
+#include <klibloader.h>
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <qfile.h>
+
+enum ArtsPOS { posIdle=0, posPlaying, posPaused };
+
+
+
+Player::Player(QObject *parent) : QObject(parent, "Player"),
+ position(-1), mLoopStyle(None), firstTimeout(true)
+{
+ mEngine=new Engine;
+ connect(&filePos, SIGNAL(timeout()), SLOT(posTimeout()));
+ connect(mEngine, SIGNAL(aboutToPlay()), this, SLOT(aboutToPlay()));
+ connect(mEngine,
+ SIGNAL(receivedStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &)),
+ this, SLOT(
+ slotUpdateStreamMeta(const QString &, const QString &,
+ const QString &, const QString &,
+ const QString &, const QString &))
+ );
+ connect(mEngine, SIGNAL(playingFailed()), this, SLOT(forward()));
+
+ handleButtons();
+}
+
+Player::~Player()
+{
+ delete mEngine;
+}
+
+bool Player::isPlaying()
+{
+ return mEngine->state()==posPlaying;
+}
+
+bool Player::isPaused()
+{
+ return mEngine->state()==posPaused;
+}
+
+bool Player::isStopped()
+{
+ return mEngine->state()==posIdle;
+}
+
+void Player::toggleListView()
+{
+ napp->playlist()->toggleList();
+}
+
+void Player::handleButtons()
+{
+ switch (mEngine->state())
+ {
+ case (posPlaying):
+ emit playing();
+ break;
+ case (posPaused):
+ emit paused();
+ break;
+ case (posIdle):
+ emit stopped();
+ }
+}
+
+void Player::back()
+{
+ if (napp->playlist()->previous())
+ {
+ stop();
+ play();
+ }
+}
+
+void Player::stop()
+{
+ filePos.stop();
+ position=0;
+ mEngine->stop();
+ emit stopped();
+ mCurrent=0;
+}
+
+void Player::play()
+{
+ napp->processEvents();
+ bool work=false;
+ firstTimeout=true;
+
+ if (mEngine->state()==posPlaying) // do nothing if already playing
+ return;
+
+ if (mEngine->state()==posPaused)
+ {
+ work=mEngine->play();
+ }
+ else
+ {
+ stop();
+ mCurrent = napp->playlist()->current();
+ if (!mCurrent)
+ {
+ work=false;
+ }
+ else
+ {
+ mEngine->blockSignals(true);
+ work=mEngine->open(mCurrent);
+ mEngine->blockSignals(false);
+ }
+ }
+
+ if (!work)
+ {
+ forward(false);
+ }
+ else
+ {
+ filePos.start(500);
+ emit changed();
+ mEngine->play();
+ }
+
+ handleButtons();
+}
+
+void Player::play(const PlaylistItem &item)
+{
+ napp->playlist()->setCurrent(item);
+}
+
+void Player::playpause()
+{
+ if (mEngine->state()==posPlaying)
+ {
+ filePos.stop();
+ mEngine->pause();
+// emit paused(); NOT necessary because emitted in handleButtons() (mETz)
+ handleButtons();
+ }
+ else
+ play();
+}
+
+void Player::forward(bool allowLoop)
+{
+ stop();
+ if (napp->playlist()->next())
+ play();
+ else if (allowLoop && napp->loopList())
+ if (napp->playlist()->reset(), napp->playlist()->current())
+ play();
+}
+
+void Player::skipTo(int msec) // skip to a certain time in the track
+{
+ if( (current()) && (msec>=0) )
+ {
+ mEngine->seek(msec);
+ position = mEngine->position(); // make sure position is recent
+ emit timeout(); // update the UI
+ emit skipped(msec);
+ emit skipped();
+ }
+}
+
+void Player::playCurrent()
+{
+ if (!mEngine->initialized()) return;
+ stop();
+ mCurrent=0;
+ if (napp->playlist()->current())
+ play();
+}
+
+void Player::newCurrent()
+{
+ // the second half of the following
+ if (!napp->playlist() || !mEngine->initialized())
+ return; // no playlist, or squelch playing as an optimization
+ if ((mEngine->state()!=posPlaying) && napp->autoPlay())
+ playCurrent();
+}
+
+void Player::posTimeout()
+{
+ if (mEngine->state()==posIdle)
+ {
+ stop();
+ handleButtons();
+ // If you're supposed to loop the song, don't go next
+ // otherwise, do go next
+ if (loopStyle()==Song || napp->playlist()->next())
+ play();
+ else if (loopStyle()==Playlist)
+ {
+ napp->playlist()->reset();
+ play();
+ }
+ else if (napp->loopList())
+ napp->playlist()->reset();
+
+ return;
+ }
+ position = mEngine->position();
+
+ if (current())
+ {
+ current().setLength(mEngine->length());
+ if (current().length() && firstTimeout)
+ {
+ int minutes = (int) ( current().length() / 60 );
+ int seconds = current().length() - minutes * 60;
+ emit newSongLen ( minutes, seconds );
+ firstTimeout = false;
+ emit newSong();
+ }
+ }
+
+ emit timeout();
+}
+
+QString Player::lengthString(int _position)
+{
+ if (!current())
+ return QString("--:--/--:--");
+
+ QString posString;
+ QString lenString;
+ int secs, seconds, minutes;
+
+ if (_position < 0)
+ _position = position;
+
+ if (_position<0)
+ {
+ posString="--:--/";
+ }
+ else
+ { // get the position
+ bool remain = napp->displayRemaining() && current();
+ if (remain && current().length()<0)
+ {
+ remain = false;
+ }
+
+ if (remain)
+ {
+ _position = current().length() - _position;
+ }
+
+ secs = _position / 1000; // convert milliseconds -> seconds
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+
+ // the string has to look like '00:00/00:00'
+ posString.sprintf("%.2d:%.2d/", minutes, seconds);
+ if (remain) posString.prepend('-');
+ }
+
+ if (!current() || current().length()<0)
+ {
+ posString+="--:--";
+ }
+ else
+ { // get the length
+ secs = current().length() / 1000; // convert milliseconds -> seconds
+ seconds = secs % 60;
+ minutes = (secs - seconds) / 60;
+
+ // the string has to look like '00:00/00:00'
+ lenString.sprintf("%.2d:%.2d", minutes, seconds);
+ posString += lenString;
+ }
+
+ return posString;
+}
+
+int Player::getLength()
+{
+ if (!current())
+ return -1;
+ return current().length(); // return track-length in milliseconds
+}
+
+void Player::openFile(const KURL &f, bool purge, bool autoplay)
+{
+ if (purge)
+ napp->playlist()->clear();
+ napp->playlist()->addFile(f, autoplay);
+}
+
+void Player::openFile(const KURL::List &f, bool purge, bool autoplay)
+{
+ if (purge)
+ napp->playlist()->clear();
+ for (KURL::List::ConstIterator i(f.begin()); i != f.end(); ++i)
+ {
+ napp->playlist()->addFile(*i, autoplay);
+ autoplay=false;
+ }
+}
+
+
+void Player::loop()
+{
+ mLoopStyle++;
+ if (mLoopStyle>Random)
+ mLoopStyle=None;
+ emit loopTypeChange(mLoopStyle);
+}
+void Player::loop(int i)
+{
+ mLoopStyle=i;
+ emit loopTypeChange(mLoopStyle);
+}
+
+void Player::removeCurrent()
+{
+ if (napp->playlist()->current())
+ napp->playlist()->current().remove();
+}
+
+void Player::setVolume(int v)
+{
+ if (v<0) v=0;
+ if (v>100) v=100;
+ mEngine->setVolume(v);
+ emit timeout();
+ emit volumeChanged(v);
+}
+
+int Player::volume() const
+{
+ return mEngine->volume();
+}
+
+void Player::aboutToPlay()
+{
+ emit aboutToOpen( mCurrent );
+}
+
+void Player::slotUpdateStreamMeta(
+ const QString &streamName, const QString &streamGenre,
+ const QString &streamUrl, const QString &streamBitrate,
+ const QString &trackTitle, const QString &trackUrl)
+{
+ PlaylistItem currentItem = napp->playlist()->current();
+ if(currentItem)
+ {
+ currentItem.setProperty("title", trackTitle);
+ currentItem.setProperty("bitrate", streamBitrate);
+
+ if(!streamName.isEmpty())
+ currentItem.setProperty("author", streamName);
+ if(!streamGenre.isEmpty())
+ currentItem.setProperty("genre", streamGenre);
+ if(!trackUrl.isEmpty())
+ currentItem.setProperty("comment", trackUrl);
+ else if(!streamUrl.isEmpty())
+ currentItem.setProperty("comment", streamUrl);
+ else
+ currentItem.clearProperty("comment");
+ emit changed();
+ }
+}
+
+#include "player.moc"
diff --git a/noatun/library/playlist.cpp b/noatun/library/playlist.cpp
new file mode 100644
index 00000000..4e729924
--- /dev/null
+++ b/noatun/library/playlist.cpp
@@ -0,0 +1,442 @@
+#include <noatun/playlist.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/downloader.h>
+#include <noatun/pluginloader.h>
+
+#include <kcmdlineargs.h>
+#include <kfile.h>
+#include <kmimetype.h>
+#include <qregexp.h>
+#include <assert.h>
+#include <qmetaobject.h>
+#include <kmedia2.h>
+#include <vector>
+
+PlaylistItemData::PlaylistItemData()
+{
+ mRefs=0;
+}
+
+PlaylistItemData::~PlaylistItemData()
+{
+}
+
+QCString PlaylistItemData::mimetype() const
+{
+ if (isProperty("mimetype"))
+ return property("mimetype").latin1();
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url());
+
+ return mimetype->name().latin1();
+}
+
+QCString PlaylistItemData::playObject() const
+{
+ if (isProperty("playObject"))
+ return property("playObject").latin1();
+
+ std::string objectType;
+
+ Arts::TraderQuery query;
+ query.supports("Interface","Arts::PlayObject");
+ query.supports("MimeType", std::string(mimetype()));
+
+ std::vector<Arts::TraderOffer> *offers = query.query();
+ if (!offers) return "";
+
+ if(!offers->empty())
+ objectType = offers->front().interfaceName();
+
+ delete offers;
+
+ return QCString(objectType.c_str());
+}
+
+QString PlaylistItemData::title() const
+{
+ if (isProperty("realtitle"))
+ return property("realtitle");
+
+
+ // "$(property)"
+ QString format=napp->titleFormat();
+
+ QRegExp find("(?:(?:\\\\\\\\))*\\$\\((.*)");
+
+ int start=0;
+ while (start != -1)
+ {
+ start = find.search(format, start);
+ if (start == -1) break;
+
+ // test if there's an odd amount of backslashes
+ if (start>0 && format[start-1]=='\\')
+ {
+ // yes, so half the amount of backslashes
+
+ // count how many there are first
+ QRegExp counter("([\\\\]+)");
+ counter.search(format, start-1);
+ uint len=counter.cap(1).length()-1;
+
+ // and half them, and remove one more
+ format.replace(start-1, len/2+1, "");
+ start=start-1+len/2+find.cap(1).length()+3;
+ continue;
+ }
+
+ // now replace the backslashes with half as many
+
+ if (format[start]=='\\')
+ {
+ // count how many there are first
+ QRegExp counter("([\\\\]+)");
+ counter.search(format, start);
+ uint len=counter.cap(1).length();
+
+ // and half them
+ format.replace(start, len/2, "");
+ start=start+len/2;
+ }
+
+ // "sth"foo"sth"
+ QString cont(find.cap(1));
+ QString prefix,suffix,propname;
+ unsigned int i=0;
+ if (cont[i] == '"')
+ {
+ i++;
+ for (; i < cont.length(); i++)
+ {
+ if (cont[i] != '"')
+ prefix += cont[i];
+ else
+ break;
+ }
+ i++;
+ }
+
+
+ for (; i < cont.length(); ++i)
+ {
+ if (cont[i]!='"' && cont[i]!=')')
+ propname += cont[i];
+ else
+ break;
+ }
+
+ if (cont[i] == '"')
+ {
+ i++;
+ for (; i < cont.length(); i++)
+ {
+ if (cont[i] != '"')
+ suffix += cont[i];
+ else
+ break;
+ }
+ i++;
+ }
+ i++;
+
+
+ QString propval = property(propname);
+ if (propname == "title" && !propval.length())
+ {
+ propval = url().filename();
+ }
+ else if (propname == "comment")
+ {
+ // comments can contain newlines
+ // these are not wanted in a formatted title
+ propval.replace('\n', ' ');
+ }
+
+ if (propval.length())
+ {
+ propval = prefix+propval+suffix;
+ format.replace(start, i+2, propval);
+ start += propval.length();
+ }
+ else
+ {
+ format.replace(start, i+2, "");
+ }
+ }
+ return format;
+
+}
+
+int PlaylistItemData::length() const
+{
+ return property("length", "-1").toInt();
+}
+
+void PlaylistItemData::setLength(int ms)
+{
+ setProperty("length", QString::number(ms));
+}
+
+PlaylistItem::PlaylistItem(const PlaylistItem &source)
+{
+ mData=source.mData;
+ addRef();
+}
+
+PlaylistItem::PlaylistItem(PlaylistItemData *source)
+{
+ mData=source;
+ addRef();
+}
+
+PlaylistItem::~PlaylistItem()
+{
+ removeRef();
+}
+
+PlaylistItem &PlaylistItem::operator =(const PlaylistItem &source)
+{
+ if (source)
+ source.addRef();
+ removeRef();
+ mData=source.mData;
+ return *this;
+}
+
+PlaylistItem &PlaylistItem::operator =(PlaylistItemData *source)
+{
+ if (source)
+ source->addRef();
+ removeRef();
+ mData=source;
+ return *this;
+}
+
+const PlaylistItem &PlaylistItem::operator =(const PlaylistItem &source) const
+{
+ if (source)
+ source.addRef();
+ removeRef();
+ mData=source.mData;
+ return *this;
+}
+
+const PlaylistItem &PlaylistItem::operator =(const PlaylistItemData *source) const
+{
+ if (source)
+ const_cast<PlaylistItemData*>(source)->addRef();
+ removeRef();
+ mData=const_cast<PlaylistItemData*>(source);
+ return *this;
+}
+
+// reference counting
+void PlaylistItem::removeRef() const
+{
+ if (mData)
+ {
+ mData->removeRef();
+ }
+}
+
+void PlaylistItem::addRef() const
+{
+ if (mData)
+ mData->addRef();
+}
+
+QString PlaylistItemData::lengthString() const
+{
+ if ( length() == -1 ) // no file loaded
+ return QString("--:--");
+
+ int secs = length()/1000; // convert milliseconds -> seconds
+ int seconds = secs % 60;
+ return QString().sprintf("%.2d:%.2d", ((secs-seconds)/60), seconds);
+}
+
+
+bool PlaylistItemData::operator == (const PlaylistItemData &d) const
+{
+ return &d==this;
+}
+
+bool PlaylistItemData::operator != (const PlaylistItemData &d) const
+{
+ return ! (*this==d);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+Playlist::Playlist(QObject *parent, const char *name) : QObject(parent, name)
+{
+ napp->player()->connect(this, SIGNAL(playCurrent()), SLOT(playCurrent()));
+ napp->player()->connect(this, SIGNAL(listHidden()), SIGNAL(playlistHidden()));
+ napp->player()->connect(this, SIGNAL(listShown()), SIGNAL(playlistShown()));
+
+}
+
+Playlist::~Playlist()
+{
+
+}
+
+void Playlist::toggleList()
+{
+ if (listVisible())
+ hideList();
+ else
+ showList();
+}
+
+int Playlist::handleArguments()
+{
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ int i;
+ bool play= napp->autoPlay();
+
+ for (i=0; i < args->count(); i++)
+ {
+ KURL u(args->url(i));
+ if (u.isValid())
+ addFile(u, play);
+ play=false;
+ }
+ args->clear();
+ return i;
+}
+
+QValueList<PlaylistItem> Playlist::select(
+ const QStringList &keys, const QStringList &values,
+ int limit, bool exact, bool caseSensitive
+ )
+{
+ QValueList<PlaylistItem> list;
+ QString k;
+ QString v;
+ QStringList::ConstIterator key, val;
+ for (PlaylistItem i(getFirst()); i && limit; i=getAfter(i))
+ {
+ for (key = keys.begin(); key != keys.end() && limit ; ++key)
+ {
+ k=*key;
+ v=i.property(k);
+
+ for (val = values.begin(); val != values.end() && limit; ++val)
+ {
+ if ((*val).length()==0 && v.length()==0)
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ else if (exact)
+ {
+ if (
+ (!caseSensitive && (*val).lower()==v.lower())
+ ||
+ (caseSensitive && (*val)==v)
+ )
+
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ }
+ else
+ {
+ if ((*val).find(v, 0, caseSensitive)!=-1)
+ {
+ list.append(i);
+ limit--;
+ goto nextSong;
+ }
+ }
+ }
+ }
+ nextSong:
+ ;
+ }
+ return list;
+}
+
+QValueList<PlaylistItem> Playlist::select(
+ const QString &key, const QString &value,
+ int limit, bool exact, bool caseSensitive
+ )
+{
+ QStringList keys(key);
+ QStringList values(value);
+ return select(keys, values, limit, exact, caseSensitive);
+}
+
+void PlaylistItemData::added()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->added(item);
+ }
+}
+
+void PlaylistItemData::removed()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->removed(item);
+ }
+}
+
+void PlaylistItemData::modified()
+{
+ PlaylistItem item(this);
+ for (
+ PlaylistNotifier *i=napp->player()->mNotifiers.first();
+ i; i=napp->player()->mNotifiers.next())
+ {
+ i->modified(item);
+ }
+}
+
+
+PlaylistItem Playlist::nextSection()
+{
+ return next();
+}
+
+PlaylistItem Playlist::previousSection()
+{
+ return previous();
+}
+
+
+PlaylistNotifier::PlaylistNotifier()
+{
+ napp->player()->mNotifiers.append(this);
+}
+
+PlaylistNotifier::~PlaylistNotifier()
+{
+ napp->player()->mNotifiers.removeRef(this);
+}
+
+
+
+#include "playlist.moc"
+
diff --git a/noatun/library/playlistsaver.cpp b/noatun/library/playlistsaver.cpp
new file mode 100644
index 00000000..58f5d5d2
--- /dev/null
+++ b/noatun/library/playlistsaver.cpp
@@ -0,0 +1,771 @@
+#include <noatun/playlistsaver.h>
+#include <qdom.h>
+#include <kio/netaccess.h>
+#include <qfile.h>
+#include <qtextstream.h>
+#include <noatun/app.h>
+#include "ksaver.h"
+#include <ksimpleconfig.h>
+#include <kmimetype.h>
+#include <klocale.h>
+#include <qregexp.h>
+#include <qxml.h>
+#include <kdebug.h>
+
+PlaylistSaver::PlaylistSaver()
+{
+}
+
+PlaylistSaver::~PlaylistSaver()
+{
+}
+
+bool PlaylistSaver::save(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "opt=" << opt << endl;
+
+ if(file.isEmpty() || !file.isValid())
+ return false;
+
+ switch (opt)
+ {
+ default:
+ case 0:
+ case XMLPlaylist:
+ return saveXML(file, opt);
+
+ case M3U:
+ case EXTM3U:
+ return saveM3U(file, opt);
+
+ case PLS:
+ return savePLS(file, opt);
+
+ case ASX:
+ return false; // No, I won't code that! [mETz]
+ }
+}
+
+bool PlaylistSaver::load(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "opt=" << opt << endl;
+
+ switch (opt)
+ {
+ default:
+ case 0:
+ case XMLPlaylist:
+ case ASX:
+ return loadXML(file, opt);
+
+ case M3U:
+ case EXTM3U:
+ return loadM3U(file, opt);
+
+ case PLS:
+ return loadPLS(file, opt);
+ }
+}
+
+bool PlaylistSaver::metalist(const KURL &url)
+{
+ kdDebug(66666) << k_funcinfo << "url=" << url.url() << endl;
+
+ QString end=url.filename().right(3).lower();
+ /*
+ if (end=="mp3" || end=="ogg") // we want to download streams only
+ {
+ kdDebug(66666) << k_funcinfo << "I can only load playlists" << endl;
+ return false;
+ }
+ */
+
+/*
+.wax audio/x-ms-wax Metafiles that reference Windows Media files with the
+ .asf, .wma or .wax file extensions.
+
+.wvx video/x-ms-wvx Metafiles that reference Windows Media files with the
+ .wma, .wmv, .wvx or .wax file extensions.
+
+.asx video/x-ms-asf Metafiles that reference Windows Media files with the
+ .wma, .wax, .wmv, .wvx, .asf, or .asx file extensions.
+*/
+
+ // it's actually a stream!
+ if (end!="pls" &&
+ end!="m3u" &&
+ end!="wax" && // windows mediaplayer metafile
+ end!="wvx" && // windows mediaplayer metafile
+ end!="asx" && // windows mediaplayer metafile
+ url.protocol().lower()=="http")
+ {
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url);
+ QString type=mimetype->name();
+
+ if (type!="application/octet-stream")
+ return false;
+
+ QMap<QString,QString> map;
+ map["playObject"]="Arts::StreamPlayObject";
+ map["title"] = i18n("Stream from %1").arg(url.host());
+
+ KURL u(url);
+ if (!u.hasPath())
+ u.setPath("/");
+
+ map["stream_"] = map["url"] = u.url();
+
+ reset();
+ readItem(map);
+ return true;
+ }
+
+ // it is a pls, m3u or ms-media-player file by now
+ if(loadXML(url, XMLPlaylist))
+ return true;
+
+ if(loadXML(url,ASX))
+ return true;
+
+ if(loadPLS(url))
+ return true;
+
+ if(loadM3U(url))
+ return true;
+
+ return false;
+}
+
+bool PlaylistSaver::saveXML(const KURL &file, int )
+{
+ QString localFile;
+ if (file.isLocalFile())
+ localFile = QFile::encodeName(file.path());
+ else
+ localFile = napp->tempSaveName(file.path());
+
+ // QDom is a pain :)
+ QDomDocument doc("playlist");
+ doc.setContent(QString("<!DOCTYPE XMLPlaylist><playlist version=\"1.0\" client=\"noatun\"/>"));
+
+ QDomElement docElem=doc.documentElement();
+
+ reset();
+ PlaylistItem i;
+ QStringList props;
+ while ((i=writeItem()))
+ {
+ // write all properties
+ props=i.properties();
+ QDomElement elem=doc.createElement("item");
+ for (QStringList::Iterator pi(props.begin()); pi!=props.end(); ++pi)
+ {
+ QString val=i.property(*pi);
+ elem.setAttribute(*pi, val);
+
+ if ((*pi)=="url")
+ {
+ KURL u(val);
+ if (u.isLocalFile())
+ {
+ elem.setAttribute("local", u.path());
+ }
+ }
+ }
+
+ docElem.appendChild(elem);
+ props.clear();
+ }
+ Noatun::KSaver saver(localFile);
+ if (!saver.open())
+ return false;
+ saver.textStream().setEncoding(QTextStream::UnicodeUTF8);
+ saver.textStream() << doc.toString();
+ saver.close();
+
+ return true;
+}
+
+class NoatunXMLStructure : public QXmlDefaultHandler
+{
+public:
+ PlaylistSaver *saver;
+ bool fresh;
+
+ NoatunXMLStructure(PlaylistSaver *s)
+ : saver(s), fresh(true)
+ {
+ }
+
+ bool startElement(
+ const QString&, const QString &,
+ const QString &name, const QXmlAttributes &a
+ )
+ {
+ if (fresh)
+ {
+ if (name=="playlist")
+ {
+ fresh=false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ if (name != "item")
+ return true;
+
+ QMap<QString,QString> propMap;
+
+ for (int i=0; i<a.count(); i++)
+ {
+ propMap[a.qName(i)] = a.value(i);
+ }
+ saver->readItem(propMap);
+
+ return true;
+ }
+};
+
+
+class MSASXStructure : public QXmlDefaultHandler
+{
+public:
+ PlaylistSaver *saver;
+ bool fresh;
+ bool inEntry, inTitle;
+ QMap<QString,QString> propMap;
+ QString mAbsPath;
+
+ MSASXStructure(PlaylistSaver *s, const QString &absPath)
+ : saver(s), fresh(true), inEntry(false),
+ inTitle(false), mAbsPath(absPath)
+ {
+ //kdDebug(66666) << k_funcinfo << endl;
+ }
+
+ bool startElement(const QString&, const QString &,
+ const QString &name, const QXmlAttributes &a)
+ {
+ if (fresh)
+ {
+ if (name.lower()=="asx")
+ {
+ //kdDebug(66666) << "found ASX format" << endl;
+ fresh=false;
+ return true;
+ }
+ else
+ {
+ kdDebug(66666) << "This is NOT an ASX style playlist!" << endl;
+ return false;
+ }
+ }
+
+ if (name.lower()=="entry")
+ {
+ if(inEntry) // WHOOPS, we are already in an entry, this should NEVER happen
+ {
+ kdDebug(66666) << "STOP, ENTRY INSIDE ENTRY!" << endl;
+ return false;
+ }
+// kdDebug(66666) << "<ENTRY> =====================" << endl;
+ inEntry=true;
+ }
+ else
+ {
+ if (inEntry) // inside entry block
+ {
+ // known stuff inside an <entry> ... </entry> block:
+ // <title>blah</title>
+ // <param album="blub" />
+ // <param artist="blah" />
+ // <ref HREF="file:/something" />
+ if(name.lower()=="ref")
+ {
+ for (int i=0; i<a.count(); i++)
+ {
+ if(a.qName(i).lower()=="href")
+ {
+ QString filename=a.value(i);
+ if (filename.find(QRegExp("^[a-zA-Z0-9]+:/"))==0)
+ {
+ KURL url(filename);
+ KMimeType::Ptr mimetype = KMimeType::findByURL(url);
+ QString type=mimetype->name();
+ if (type != "application/octet-stream")
+ {
+ propMap["url"]=filename;
+ }
+ else
+ {
+ propMap["playObject"]="SplayPlayObject";
+ propMap["title"] = i18n("Stream from %1").arg(url.host());
+ if (!url.hasPath())
+ url.setPath("/");
+ propMap["url"] = url.url();
+ propMap["stream_"]=propMap["url"];
+// readItem(propMap);
+// continue;
+ }
+ }
+ else
+ {
+ KURL u1;
+ // we have to deal with a relative path
+ if (filename.find('/'))
+ {
+ u1.setPath(mAbsPath); //FIXME: how to get the path in this place?
+ u1.setFileName(filename);
+ }
+ else
+ {
+ u1.setPath(filename);
+ }
+ propMap["url"]=u1.url();
+ }
+// kdDebug(66666) << "adding property url, value='" << propMap["url"] << "'" << endl;
+ }
+ }
+ }
+ else if(name.lower()=="param")
+ {
+ QString keyName="", keyValue="";
+
+ for (int i=0; i<a.count(); i++)
+ {
+ if(a.value(i).lower()=="album")
+ keyName="album";
+ else if(a.value(i).lower()=="artist")
+ keyName="author";
+ else if(!keyName.isEmpty()) // successfully found a key, the next key=value pair has to be the value
+ {
+// kdDebug(66666) << "keyName=" << keyName << ", next value is '" << a.value(i) << "'" << endl;
+ keyValue=a.value(i);
+ }
+ }
+
+ if (!keyName.isEmpty() && !keyValue.isEmpty())
+ {
+// kdDebug(66666) << "adding property; key='" << keyName << "', value='" << keyValue << "'" << endl;
+ propMap[keyName]=keyValue;
+ }
+ }
+ else if(name.lower()=="title")
+ {
+ if(inTitle) // WHOOPS, we are already in an entry, this should NEVER happen
+ {
+ kdDebug(66666) << "STOP, TITLE INSIDE TITLE!" << endl;
+ return false;
+ }
+// kdDebug(66666) << "<TITLE> ======" << endl;
+ inTitle=true;
+ }
+/* else
+ {
+ kdDebug(66666) << "Unknown/unused element inside ENTRY block, NAME='" << name << "'" << endl;
+ for (int i=0; i<a.count(); i++)
+ kdDebug(66666) << " | " << a.qName(i) << " = '" << a.value(i) << "'" << endl;
+ }*/
+ }
+/* else
+ {
+ kdDebug(66666) << "Uninteresting element, NAME='" << name << "'" << endl;
+ for (int i=0; i<a.count(); i++)
+ kdDebug(66666) << " | " << a.qName(i) << " = '" << a.value(i) << "'" << endl;
+ }*/
+ }
+
+ return true;
+ }
+
+ bool endElement(const QString&,const QString&, const QString& name)
+ {
+// kdDebug(66666) << k_funcinfo << "name='" << name << "'" << endl;
+ if (name.lower()=="entry")
+ {
+ if(inEntry)
+ {
+/* kdDebug(66666) << "</ENTRY> =====================" << endl;
+ for (QMap<QString,QString>::ConstIterator it=propMap.begin(); it!=propMap.end(); ++it )
+ kdDebug(66666) << "key='" << it.key() << "', val='" << it.data() << "'" << endl;
+*/
+ saver->readItem(propMap);
+ propMap.clear();
+ inEntry=false;
+ }
+ else // found </entry> without a start
+ {
+ kdDebug(66666) << "STOP, </ENTRY> without a start" << endl;
+ return false;
+ }
+ }
+ else if (name.lower()=="title")
+ {
+ if(inTitle && inEntry)
+ {
+// kdDebug(66666) << "</TITLE> ======" << endl;
+ inTitle=false;
+ }
+ else if (inTitle) // found </title> without a start or not inside an <entry> ... </entry>
+ {
+ kdDebug(66666) << "STOP, </TITLE> without a start" << endl;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool characters(const QString &ch)
+ {
+ if(inTitle)
+ {
+ if (!ch.isEmpty())
+ {
+ propMap["title"]=ch;
+// kdDebug(66666) << "adding property; key='title', value='" << ch << "'" << endl;
+ }
+ }
+ return true;
+ }
+};
+
+
+bool PlaylistSaver::loadXML(const KURL &url, int opt)
+{
+ kdDebug(66666) << k_funcinfo <<
+ "file='" << url.url() << "', opt=" << opt << endl;
+
+ QString dest;
+ if(KIO::NetAccess::download(url, dest, 0L))
+ {
+ QFile file(dest);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ reset();
+
+ // QXml is horribly documented
+ QXmlInputSource source(&file);
+ QXmlSimpleReader reader;
+
+ if (opt == ASX ||
+ url.path().right(4).lower()==".wax" ||
+ url.path().right(4).lower()==".asx" ||
+ url.path().right(4).lower()==".wvx")
+ {
+ MSASXStructure ASXparser(this, url.path(0));
+ reader.setContentHandler(&ASXparser);
+ reader.parse(source);
+ return !ASXparser.fresh;
+ }
+ else
+ {
+ NoatunXMLStructure parser(this);
+ reader.setContentHandler(&parser);
+ reader.parse(source);
+ return !parser.fresh;
+ }
+ } // END download()
+
+ return false;
+}
+
+bool PlaylistSaver::loadM3U(const KURL &file, int /*opt*/)
+{
+ kdDebug(66666) << k_funcinfo << "file='" << file.path() << endl;
+
+ QString localFile;
+ if(!KIO::NetAccess::download(file, localFile, 0L))
+ return false;
+
+ // if it's a PLS, transfer control, again (KIO bug?)
+#if 0
+ {
+ KSimpleConfig list(local, true);
+ list.setGroup("playlist");
+
+ // some stupid Windows lusers like to be case insensitive
+ QStringList groups=list.groupList().grep(QRegExp("^playlist$", false));
+ if (groups.count())
+ {
+ KURL l;
+ l.setPath(local);
+ return loadPLS(l);
+ }
+ }
+#endif
+
+ QFile saver(localFile);
+ saver.open(IO_ReadOnly);
+ QTextStream t(&saver);
+
+ bool isExt = false; // flag telling if we load an EXTM3U file
+ QString filename;
+ QString extinf;
+ QMap<QString,QString> prop;
+ reset();
+
+ while (!t.eof())
+ {
+ if (isExt)
+ {
+ extinf = t.readLine();
+ if (!extinf.startsWith("#EXTINF:"))
+ {
+// kdDebug(66666) << "EXTM3U extinf line != extinf, assuming it's a filename." << endl;
+ filename = extinf;
+ extinf="";
+ }
+ else
+ {
+ filename = t.readLine(); // read in second line containing the filename
+ }
+ //kdDebug(66666) << "EXTM3U filename = '" << filename << "'" << endl;
+ }
+ else // old style m3u
+ {
+ filename = t.readLine();
+ }
+
+ if (filename == "#EXTM3U") // on first line
+ {
+// kdDebug(66666) << "FOUND '#EXTM3U' @ " << saver.at() << "." << endl;
+ isExt=true;
+ continue; // skip parsing the first (i.e. this) line
+ }
+
+ if (filename.isEmpty())
+ continue;
+
+ if (filename.find(QRegExp("^[a-zA-Z0-9]+:/"))==0)
+ {
+ //kdDebug(66666) << k_funcinfo << "url filename = " << filename << endl;
+
+ KURL protourl(filename);
+ KMimeType::Ptr mimetype = KMimeType::findByURL(protourl);
+
+ if (mimetype->name() != "application/octet-stream")
+ {
+ prop["url"] = filename;
+ }
+ else
+ {
+ prop["playObject"]="SplayPlayObject";
+ // Default title, might be overwritten by #EXTINF later
+ prop["title"] = i18n("Stream from %1").arg(protourl.host());
+
+ if (!protourl.hasPath())
+ protourl.setPath("/");
+
+ prop["url"] = protourl.url();
+ prop["stream_"] = prop["url"];
+ }
+ }
+ else // filename that is not of URL style (i.e. NOT "proto:/path/somefile")
+ {
+ KURL u1;
+ // we have to deal with a relative path
+ if (filename.find('/'))
+ {
+ u1.setPath(file.path(0));
+ u1.setFileName(filename);
+ }
+ else
+ {
+ u1.setPath(filename);
+ }
+ prop["url"] = u1.url();
+ }
+
+ // parse line of the following format:
+ //#EXTINF:length,displayed_title
+ if (isExt)
+ {
+ extinf.remove(0,8); // remove "#EXTINF:"
+ //kdDebug(66666) << "EXTM3U extinf = '" << extinf << "'" << endl;
+ int timeTitleSep = extinf.find(',', 0);
+
+ int length = (extinf.left(timeTitleSep)).toInt();
+ if (length>0)
+ prop["length"]=QString::number(length*1000);
+
+ QString displayTitle=extinf.mid(timeTitleSep+1);
+ if (!displayTitle.isEmpty())
+ {
+ int artistTitleSep = displayTitle.find(" - ",0);
+ if (artistTitleSep == -1) // no "artist - title" like format, just set it as title
+ {
+ prop["title"] = displayTitle;
+ }
+ else
+ {
+ prop["author"] = displayTitle.left(artistTitleSep);
+ prop["title"] = displayTitle.mid(artistTitleSep+3);
+ }
+ /*kdDebug(66666) << "EXTM3U author/artist='" << prop["author"] <<
+ "', title='" << prop["title"] << "'" << endl;*/
+ } // END !displayTitle.isEmpty()
+ } // END if(isExt)
+
+// kdDebug(66666) << k_funcinfo << "adding file '" << prop["url"] << "' to playlist" << endl;
+ readItem(prop);
+ prop.clear();
+ } // END while()
+
+ KIO::NetAccess::removeTempFile(localFile);
+
+// kdDebug(66666) << k_funcinfo << "END" << endl;
+ return true;
+}
+
+bool PlaylistSaver::saveM3U(const KURL &file, int opt)
+{
+// kdDebug(66666) << k_funcinfo << "file='" << file.path() << "', opt=" << opt << endl;
+
+ bool isExt=(opt==EXTM3U); // easier ;)
+
+ QString local(napp->tempSaveName(file.path()));
+ QFile saver(local);
+ saver.open(IO_ReadWrite | IO_Truncate);
+ QTextStream t(&saver);
+
+ reset();
+ PlaylistItem i;
+ QStringList props;
+
+ // this is more code but otoh faster than checking for isExt inside the loop
+ if(isExt)
+ {
+ t << "#EXTM3U" << '\n';
+
+ while ((i=writeItem()))
+ {
+ int length = static_cast<int>(((i.property("length")).toInt())/1000);
+ if(length==0) length=-1; // special value in an EXTM3U file, means "unknown"
+ KURL u(i.property("url"));
+ QString title;
+
+ // if a playlistitem is without a tag or ONLY title is set
+ if((i.property("author").isEmpty() && i.property("title").isEmpty())
+ || (i.property("author").isEmpty() && !i.property("title").isEmpty()) )
+ title = u.filename().left(u.filename().length()-4);
+ else
+ title = i.property("author") + " - " + i.property("title");
+
+// kdDebug(66666) << "#EXTINF:"<< QString::number(length) << "," << title << endl;
+ t << "#EXTINF:"<< QString::number(length) << "," << title << '\n';
+
+ if (u.isLocalFile())
+ t << u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ }
+ else
+ {
+ while ((i=writeItem()))
+ {
+ KURL u(i.property("url"));
+ if (u.isLocalFile())
+ t << u.path() << '\n';
+ else
+ t << u.url() << '\n';
+ }
+ }
+
+ saver.close();
+ KIO::NetAccess::upload(local, file, 0L);
+ saver.remove();
+ return true;
+}
+
+static QString findNoCase(const QMap<QString,QString> &map, const QString &key)
+{
+ for (QMap<QString,QString>::ConstIterator i=map.begin(); i!=map.end(); ++i)
+ {
+ if (i.key().lower() == key.lower())
+ return i.data();
+ }
+ return 0;
+}
+
+bool PlaylistSaver::loadPLS(const KURL &file, int /*opt*/)
+{
+ kdDebug(66666) << k_funcinfo << "file='" << file.path() << endl;
+
+ QString localFile;
+ if(!KIO::NetAccess::download(file, localFile, 0L))
+ return false;
+
+ QFile checkFile(localFile);
+ checkFile.open(IO_ReadOnly);
+ QTextStream t(&checkFile);
+ QString firstLine = t.readLine();
+ if(firstLine.lower() != "[playlist]")
+ {
+ kdDebug(66666) << k_funcinfo << "PLS didn't start with '[playlist]', aborting" << endl;
+ return false;
+ }
+
+
+ KSimpleConfig list(localFile, true);
+ //list.setGroup("playlist");
+
+ // some stupid Windows lusers like to be case insensitive
+ QStringList groups = list.groupList().grep(QRegExp("^playlist$", false));
+ /*
+ if (!groups.count()) // didn't find "[playlist]", it's not a .pls file
+ return false;
+ */
+
+ QMap<QString,QString> group = list.entryMap(groups[0]);
+
+ QString numOfEntries = findNoCase(group, "numberofentries");
+ if(numOfEntries.isEmpty())
+ return false;
+
+ reset();
+
+ unsigned int nEntries = numOfEntries.toInt();
+ for(unsigned int entry = 1; entry <= nEntries; ++entry )
+ {
+ QString str;
+ str.sprintf("file%d", entry);
+ QString cast = findNoCase(group, str.utf8());
+ str.sprintf("title%d", entry);
+ QString title = findNoCase(group, str.utf8());
+
+ // assume that everything in a pls is a streamable file
+ QMap<QString,QString> map;
+
+ KURL url(cast);
+ if (!url.hasPath())
+ url.setPath("/");
+
+ map["playObject"]="SplayPlayObject";
+ if (title.isEmpty())
+ map["title"] = i18n("Stream from %1 (port: %2)").arg( url.host() ).arg( url.port() );
+ else
+ map["title"] = i18n("Stream from %1, (ip: %2, port: %3)").arg( title ).arg( url.host() ).arg(url.port() );
+
+ map["url"] = map["stream_"]= url.url();
+
+ readItem(map);
+ }
+ return true;
+}
+
+bool PlaylistSaver::savePLS(const KURL &, int)
+{
+ return false;
+}
+
+void PlaylistSaver::setGroup(const QString &)
+{
+}
+
+
diff --git a/noatun/library/plugin.cpp b/noatun/library/plugin.cpp
new file mode 100644
index 00000000..e9090609
--- /dev/null
+++ b/noatun/library/plugin.cpp
@@ -0,0 +1,578 @@
+#include <qtimer.h>
+#include <qfile.h>
+#include <artsflow.h>
+#include <vector>
+#include <artsflow.h>
+#include <soundserver.h>
+#include <noatunarts.h>
+#include <dcopclient.h>
+#include <dispatcher.h>
+#include <kdebug.h>
+#include <kstandarddirs.h>
+
+#include <cmath>
+
+#include "plugin_deps.h"
+#include "player.h"
+#include "plugin.h"
+#include "pluginloader.h"
+#include "app.h"
+#include "engine.h"
+
+using std::vector;
+
+Arts::Dispatcher *Visualization::mDispatcher=0;
+bool Visualization::internalVis=false;
+
+Plugin::Plugin()
+{
+}
+
+Plugin::~Plugin()
+{
+}
+
+void Plugin::init()
+{
+}
+
+bool Plugin::unload()
+{
+ return napp->libraryLoader()->remove(this);
+}
+
+TimerThingy::TimerThingy(Visualization *vis)
+ : mVis(vis), id(-1)
+{
+}
+
+void TimerThingy::timerEvent(QTimerEvent *)
+{
+ mVis->timeout();
+}
+
+void TimerThingy::setInterval(int msec)
+{
+ ms=msec;
+ if (id!=-1)
+ {
+ killTimer(id);
+ id=startTimer(msec);
+ }
+}
+
+void TimerThingy::start()
+{
+ if (id==-1)
+ id=startTimer(ms);
+}
+
+void TimerThingy::stop()
+{
+ if (id==-1)
+ {
+ killTimer(id);
+ id=-1;
+ }
+}
+
+
+Visualization::Visualization(int timeout, int pid)
+{
+ mTimerThingy=new TimerThingy(this);
+ setInterval(timeout);
+
+ // if this is a fork, do a cutesy arts thingy to get a remote
+ // stack, otherwise, get it from localhost :)
+ {
+ int parent=pid ? pid : getppid();
+
+ if (getenv("NOATUN_PID"))
+ parent = QString::fromLatin1(getenv("NOATUN_PID")).toInt();
+
+ DCOPClient c;
+ c.attach();
+
+ QCString appids[2];
+ appids[0]=QString("noatun-%1").arg(parent).local8Bit();
+ appids[1]="noatun";
+ QCString &appid=appids[0];
+
+ if (!internalVis && c.isApplicationRegistered(appids[0]))
+ {
+ appid=appids[0];
+ }
+ else if (!internalVis && c.isApplicationRegistered(appids[1]))
+ {
+ appid=appids[1];
+ }
+ else
+ {
+ kdDebug(66666) << "Internal Vis" <<endl;
+ mVisualizationStack=napp->player()->engine()->visualizationStack()->toString().c_str();
+ mServer=new Arts::SoundServerV2(*(napp->player()->engine()->server()));
+ return;
+ }
+
+ {
+ QByteArray replyData;
+ QCString replyType;
+
+ if (!c.call(appid, "Noatun", "visStack()", QByteArray(), replyType, replyData))
+ {
+ kdDebug(66666) << "Error communicating to parent noatun" << endl;
+ }
+ else
+ {
+ initDispatcher();
+ mServer=new Arts::SoundServerV2;
+ *mServer = Arts::Reference("global:Arts_SoundServerV2");
+ QDataStream reply(replyData, IO_ReadOnly);
+ QCString result;
+ reply >> result;
+ mVisualizationStack=result;
+ }
+ }
+ }
+}
+
+Visualization::~Visualization()
+{
+// napp->player()->engine()->visualizationStack()->remove(mId);
+
+ delete mServer;
+ delete mTimerThingy;
+}
+
+void Visualization::start()
+{
+ mTimerThingy->start();
+}
+
+void Visualization::stop()
+{
+ mTimerThingy->stop();
+}
+
+void Visualization::setInterval(int msec)
+{
+ mTimeout=msec;
+ if (!msec)
+ stop();
+ mTimerThingy->setInterval(msec);
+}
+
+int Visualization::interval() const
+{
+ return mTimeout;
+}
+
+Noatun::StereoEffectStack Visualization::visualizationStack()
+{
+ return Arts::Reference(mVisualizationStack);
+}
+
+Arts::SoundServerV2 *Visualization::server()
+{
+ return mServer;
+}
+
+int Visualization::noatunPid()
+{
+ DCOPClient dcop;
+ dcop.attach();
+ QCStringList apps = dcop.registeredApplications();
+ for (QCStringList::Iterator i = apps.begin(); i != apps.end(); ++i )
+ if ((*i).left(9) != "anonymous" )
+ {
+ if ((*i).left(6) != "noatun")
+ continue;
+ int pid=(*i).mid((*i).find('-')+1).toInt();
+ return pid;
+ }
+ return -1;
+}
+
+bool Visualization::connected()
+{
+ server()->_interfaceName(); // makes error() work
+ return !(server()->error() || server()->isNull());
+}
+
+void Visualization::initDispatcher()
+{
+ if (!mDispatcher)
+ {
+ mDispatcher=new Arts::Dispatcher;
+ }
+}
+
+FFTScope::FFTScope(int interval, int pid) : Visualization(interval, pid)
+{
+}
+
+float FFTScope::magic(int bands)
+{
+/* QString path=locate("data", "noatun/magictable");
+ QFile magic(path);
+ if (!magic.open(IO_ReadOnly | IO_Raw))
+ return 0;
+ if (!magic.at(bands*sizeof(float)))
+ return 0;
+
+ float value;
+ if (magic.readBlock((char*)&value, sizeof(float))==-1)
+ value=0;
+*/
+ bands += 20-1; // index-1 from FFTScopes.cpp
+ float value = std::log(std::pow(2046.0, 1.0/bands));
+
+ return value;
+}
+
+
+StereoFFTScope::StereoFFTScope(int timeout, int pid) : FFTScope(timeout, pid)
+{
+ mScope=new Noatun::FFTScopeStereo;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::FFTScopeStereo"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun FFT");
+ }
+}
+
+StereoFFTScope::~StereoFFTScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void StereoFFTScope::scopeData(vector<float> *&left, vector<float> *&right)
+{
+ left=mScope->scopeLeft();
+ right=mScope->scopeRight();
+}
+
+void StereoFFTScope::timeout()
+{
+ vector<float> *left, *right;
+ scopeData(left, right);
+
+ if (left->size())
+ scopeEvent(&left->front(), &right->front(), left->size());
+ delete left;
+ delete right;
+
+}
+
+int StereoFFTScope::bands() const
+{
+ vector<float> *d=mScope->scopeLeft();
+ int size=d->size();
+ delete d;
+ return size;
+}
+
+void StereoFFTScope::setBands(float f)
+{
+ mScope->bandResolution(f);
+}
+
+
+
+MonoFFTScope::MonoFFTScope(int timeout, int pid) : FFTScope(timeout, pid)
+{
+ mScope=new Noatun::FFTScope;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::FFTScope"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun FFT");
+ }
+}
+
+MonoFFTScope::~MonoFFTScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void MonoFFTScope::scopeData(vector<float> *&data)
+{
+ data=mScope->scope();
+}
+
+void MonoFFTScope::timeout()
+{
+ vector<float> *data(mScope->scope());
+
+ float *f=&data->front();
+ if (data->size()) scopeEvent(f, data->size());
+ delete data;
+}
+
+int MonoFFTScope::bands() const
+{
+ vector<float> *d=mScope->scope();
+ int size=d->size();
+ delete d;
+ return size;
+
+}
+
+void MonoFFTScope::setBands(float f)
+{
+ mScope->bandResolution(f);
+}
+
+
+
+
+
+
+Scope::Scope(int interval, int pid) : Visualization(interval, pid)
+{
+}
+
+
+
+MonoScope::MonoScope(int timeout, int pid) : Scope(timeout, pid)
+{
+ mScope=new Noatun::RawScope;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::RawScope"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun Scope");
+ }
+}
+
+MonoScope::~MonoScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void MonoScope::scopeData(vector<float> *&data)
+{
+ data=mScope->scope();
+}
+
+void MonoScope::timeout()
+{
+ vector<float> *data(mScope->scope());
+
+ float *f=&data->front();
+ if (data->size()) scopeEvent(f, data->size());
+ delete data;
+}
+
+int MonoScope::samples() const
+{
+ return (long)mScope->buffer();
+}
+
+void MonoScope::setSamples(int len)
+{
+ mScope->buffer((long)len);
+}
+
+
+
+StereoScope::StereoScope(int timeout, int pid) : Scope(timeout, pid)
+{
+ mScope=new Noatun::RawScopeStereo;
+ *mScope=Arts::DynamicCast(server()->createObject("Noatun::RawScopeStereo"));
+
+ if ((*mScope).isNull())
+ {
+ delete mScope;
+ mScope=0;
+ }
+ else
+ {
+ mScope->start();
+ mId=visualizationStack().insertBottom(*mScope, "Noatun Stereo Scope");
+ }
+}
+
+StereoScope::~StereoScope()
+{
+ if (mScope)
+ {
+ if (connected())
+ visualizationStack().remove(mId);
+ mScope->stop();
+ delete mScope;
+ }
+}
+
+void StereoScope::scopeData(vector<float> *&left, vector<float> *&right)
+{
+ left=mScope->scopeLeft();
+ right=mScope->scopeRight();
+}
+
+void StereoScope::timeout()
+{
+ vector<float> *left(mScope->scopeLeft());
+ vector<float> *right(mScope->scopeRight());
+
+ float *l=&left->front();
+ float *r=&right->front();
+
+ if (left->size()==right->size() && left->size())
+ scopeEvent(l, r, left->size());
+ delete left;
+ delete right;
+}
+
+int StereoScope::samples() const
+{
+ return (long)mScope->buffer();
+}
+
+void StereoScope::setSamples(int len)
+{
+ mScope->buffer((long)len);
+}
+
+
+
+NoatunListener::NoatunListener(QObject *parent) : QObject(parent)
+{ }
+
+NoatunListener::~NoatunListener()
+{ }
+
+void NoatunListener::message()
+{
+ emit event();
+}
+
+NoatunListenerNotif::NoatunListenerNotif(NoatunListener *l)
+{
+ mListener=l;
+}
+
+void NoatunListenerNotif::message()
+{
+ mListener->message();
+}
+
+
+ExitNotifier::ExitNotifier(int pid, QObject *parent) : NoatunListener(parent)
+{
+ mNotif=new NoatunListenerNotif(this);
+
+ DCOPClient c;
+ c.attach();
+
+
+ QCString appids[2];
+ appids[0]=QString("noatun-%1").arg(pid).local8Bit();
+ appids[1]="noatun";
+ appid=appids[0];
+
+ if (c.isApplicationRegistered(appids[0]))
+ {
+ appid=appids[0];
+ }
+ else if (c.isApplicationRegistered(appids[1]))
+ {
+ appid=appids[1];
+ }
+ else
+ {
+ return;
+ }
+
+ QByteArray replyData;
+ QCString replyType;
+
+ QCString sessionName;
+
+ if (!c.call(appid, "Noatun", "session()", QByteArray(), replyType, replyData))
+ {
+ kdDebug(66666) << "Error communicating to parent noatun" << endl;
+ }
+ else
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ reply >> sessionName;
+ }
+
+ Visualization::initDispatcher();
+
+ Noatun::Session session=Arts::Reference(sessionName);
+ session.addListener(*mNotif);
+}
+
+ExitNotifier::~ExitNotifier()
+{
+ QByteArray replyData;
+ QCString replyType;
+
+ QCString sessionName;
+
+ DCOPClient c;
+ c.attach();
+
+ if (c.call(appid, "Noatun", "session()", QByteArray(), replyType, replyData))
+ {
+ QDataStream reply(replyData, IO_ReadOnly);
+ reply >> sessionName;
+
+ Noatun::Session session=Arts::Reference(sessionName);
+ session.removeListener(*mNotif);
+ }
+ delete mNotif;
+}
+
+BoolNotifier::BoolNotifier(bool *value, NoatunListener *listener, QObject *parent)
+ : QObject(parent)
+{
+ connect(listener, SIGNAL(event()), SLOT(event()));
+ mValue=value;
+}
+
+SessionManagement::SessionManagement() { }
+SessionManagement::~SessionManagement() { }
+void SessionManagement::restore() { }
+
+#include "plugin.moc"
+#include "plugin_deps.moc"
diff --git a/noatun/library/plugin_deps.h b/noatun/library/plugin_deps.h
new file mode 100644
index 00000000..a2f3f628
--- /dev/null
+++ b/noatun/library/plugin_deps.h
@@ -0,0 +1,44 @@
+#ifndef PLUGIN_DEPS_H
+#define PLUGIN_DEPS_H
+
+#include <qtimer.h>
+#include <qobject.h>
+#include <noatunarts.h>
+
+class Visualization;
+class NoatunListener;
+
+
+class TimerThingy : public QObject
+{
+Q_OBJECT
+public:
+ TimerThingy(Visualization*);
+
+ void setInterval(int msec);
+ void stop();
+ void start();
+
+public:
+ virtual void timerEvent(QTimerEvent *);
+
+private:
+ Visualization *mVis;
+ int id;
+ int ms;
+};
+
+class NoatunListenerNotif : public Noatun::Listener_skel
+{
+public:
+ NoatunListenerNotif(NoatunListener *);
+
+ virtual void message();
+
+ operator Noatun::Listener() { return Noatun::Listener::_from_base(_copy()); }
+private:
+ NoatunListener *mListener;
+};
+
+#endif
+
diff --git a/noatun/library/pluginloader.cpp b/noatun/library/pluginloader.cpp
new file mode 100644
index 00000000..c367228a
--- /dev/null
+++ b/noatun/library/pluginloader.cpp
@@ -0,0 +1,366 @@
+#include <qfile.h>
+#include <kglobal.h>
+#include <qdir.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <knotifyclient.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <pluginloader.h>
+#include <plugin.h>
+#include <kdebug.h>
+
+bool operator ==(const NoatunLibraryInfo &a, const NoatunLibraryInfo &b)
+{
+ // Feels like cheating, doesn't it?
+ return a.specfile == b.specfile;
+}
+
+LibraryLoader::LibraryLoader() : mPlaylist(0)
+{
+}
+
+LibraryLoader::~LibraryLoader()
+{
+ QValueList<NoatunLibraryInfo> l;
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ if((*i).type != "userinterface" && (*i).type != "playlist" && (*i).type != "systray")
+ {
+ removeNow((*i).specfile);
+ }
+ }
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ if((*i).type == "userinterface")
+ {
+ removeNow((*i).specfile);
+ }
+ }
+
+ l = loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ removeNow((*i).specfile);
+ }
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::available() const
+{
+ QValueList<NoatunLibraryInfo> items;
+ QStringList files=KGlobal::dirs()->findAllResources("appdata", "*.plugin", false, true);
+ for (QStringList::Iterator i=files.begin(); i!=files.end(); ++i)
+ items.append(getInfo(*i));
+
+ return items;
+}
+
+QPtrList<Plugin> LibraryLoader::plugins() const
+{
+ QPtrList<Plugin> list;
+ for (QDictIterator<LibraryLoader::PluginLibrary> i(mLibHash); i.current(); ++i)
+ list.append(i.current()->plugin);
+ return list;
+}
+
+bool LibraryLoader::loadAll()
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup("");
+ QStringList modules = config->readListEntry("Modules");
+ return loadAll(modules);
+}
+
+bool LibraryLoader::loadAll(const QStringList &modules)
+{
+ // Session management...
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("sm"))
+ continue;
+ loadSO(*i);
+ }
+
+ // load all the playlists in the first
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("playlist"))
+ continue;
+ loadSO(*i);
+ }
+
+ if (!mPlaylist)
+ {
+ kdWarning(66666) << "No playlist plugin loaded, defaulting to splitplaylist" << endl;
+ if (!loadSO("splitplaylist.plugin"))
+ return false;
+ }
+
+ // load all the user interfaces now
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if (!info.type.contains("userinterface"))
+ continue;
+ loadSO(*i);
+ }
+
+ if (!loadedByType("userinterface").count())
+ {
+ kdWarning(66666) << "No userinterface plugin loaded, defaulting to excellent" << endl;
+ if (!loadSO("excellent.plugin"))
+ return false;
+ }
+
+ for(QStringList::ConstIterator i=modules.begin(); i!=modules.end(); ++i)
+ {
+ NoatunLibraryInfo info=getInfo(*i);
+ if((!info.type.contains("playlist"))
+ && (!info.type.contains("userinterface"))
+ && (!info.type.contains("sm")))
+ {
+ loadSO(*i);
+ }
+ }
+
+ return true;
+}
+
+NoatunLibraryInfo LibraryLoader::getInfo(const QString &spec) const
+{
+ NoatunLibraryInfo info;
+ QString specPath = (spec[0]=='/') ? spec : KGlobal::dirs()->findResource("appdata", spec);
+ if (!QFile::exists(specPath))
+ return info;
+ KSimpleConfig file(specPath);
+ if (spec.find('/')>=0)
+ info.specfile=KURL(spec).fileName();
+ else
+ info.specfile=spec;
+ info.filename=file.readPathEntry("Filename");
+ info.author=file.readEntry("Author");
+ info.site=file.readEntry("Site");
+ info.email=file.readEntry("Email");
+ info.type=file.readEntry("Type");
+ info.name=file.readEntry("Name");
+ info.comment=file.readEntry("Comment");
+ info.require=file.readListEntry("Require");
+ info.license=file.readEntry("License");
+ return info;
+}
+
+bool LibraryLoader::isLoaded(const QString &spec) const
+{
+ PluginLibrary *lib=mLibHash[spec];
+ if (!lib) return false;
+ return lib->plugin;
+}
+
+bool LibraryLoader::loadSO(const QString &spec)
+{
+ if(!isLoaded(spec))
+ {
+ NoatunLibraryInfo info = getInfo(spec);
+ if (info.specfile != spec)
+ return false;
+
+ for (QStringList::ConstIterator it = info.require.begin(); it != info.require.end(); ++it)
+ loadSO(*it);
+
+ // get the library loader instance
+ KLibLoader *loader = KLibLoader::self();
+
+ PluginLibrary *listitem=mLibHash[spec];
+
+ if (!listitem)
+ {
+ QString filename = KGlobal::dirs()->findResource("module", info.filename);
+ KLibrary *lib = loader->library(QFile::encodeName(filename));
+ if (!lib)
+ return false;
+ listitem=new PluginLibrary;
+ listitem->library=lib;
+ mLibHash.insert(spec, listitem);
+ }
+
+ void *create=listitem->library->symbol("create_plugin");
+ if (!create)
+ return false;
+
+ Plugin* (*plugInStart)();
+ plugInStart=(Plugin* (*)()) create;
+ listitem->plugin=plugInStart();
+
+ if (info.type.contains("playlist"))
+ {
+ //kdDebug(66666) << k_funcinfo << "Assigning mPlaylist to " << info.name << endl;
+ mPlaylist=listitem->plugin->playlist();
+ }
+ listitem->plugin->init();
+
+ return true;
+ }
+ else
+ return false;
+}
+
+void LibraryLoader::add(const QString &spec)
+{
+ PluginLibrary *lib=mLibHash[spec];
+ if (lib)
+ if (lib->plugin) return;
+
+ loadSO(spec);
+}
+
+void LibraryLoader::setModules(const QStringList &mods)
+{
+ KConfig *config=KGlobal::config();
+ config->setGroup(0);
+ config->writeEntry("Modules", mods);
+ config->sync();
+}
+
+bool LibraryLoader::remove(const QString& spec, bool terminateOnLastUI)
+{
+ bool SystrayPluginEnabled=false;
+
+ NoatunLibraryInfo info = getInfo(spec);
+ // exit if this is the last UI
+ if (info.type=="userinterface" && terminateOnLastUI)
+ {
+ QValueList<NoatunLibraryInfo> l=loaded();
+
+ // Iterate over other plugins
+ for (QValueList<NoatunLibraryInfo>::Iterator i=l.begin(); i!=l.end(); ++i)
+ {
+ // Is this a UI plugin?
+ if ((*i).specfile!=spec && (*i).type=="userinterface")
+ {
+ // Good, we don't have to exit
+ removeNow(spec);
+ return true;
+ }
+ else if((*i).type=="systray") // Is there a Systray plugin?
+ {
+ SystrayPluginEnabled=true;
+ }
+ }
+
+ // Don't terminate, when there is a systray plugin.
+ if(SystrayPluginEnabled)
+ {
+ napp->toggleInterfaces();
+ return true;
+ }
+ else
+ {
+ // No other UIs, terminate
+ kapp->exit();
+ }
+ }
+ else if (info.type=="playlist")
+ {
+ //kdWarning(66666) << "Unloading playlist, this might be dangerous" << endl;
+ mPlaylist=0;
+ }
+
+ removeNow(spec);
+ return true;
+}
+
+bool LibraryLoader::remove(const QString &spec)
+{
+ remove(spec, true);
+ return true;
+}
+
+bool LibraryLoader::remove(const PluginLibrary *pl)
+{
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (i.current()==pl)
+ return remove(i.currentKey());
+ }
+ return false;
+}
+
+bool LibraryLoader::remove(const Plugin *plugin)
+{
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (i.current()->plugin==plugin)
+ return remove(i.currentKey());
+ }
+ return false;
+
+}
+
+Playlist *LibraryLoader::playlist() const
+{
+ // playlist should never be NULL when this method is called
+ Q_ASSERT(mPlaylist);
+ return mPlaylist;
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::loaded() const
+{
+ QValueList<NoatunLibraryInfo> items;
+
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ if (isLoaded(i.currentKey()))
+ items.append(getInfo(i.currentKey()));
+
+ return items;
+}
+
+QValueList<NoatunLibraryInfo> LibraryLoader::loadedByType(const QString &type) const
+{
+ QValueList<NoatunLibraryInfo> items;
+
+ for (QDictIterator<PluginLibrary> i(mLibHash); i.current(); ++i)
+ {
+ if (isLoaded(i.currentKey()))
+ {
+ NoatunLibraryInfo info = getInfo(i.currentKey());
+ if (info.type.contains(type))
+ items.append(info);
+ }
+ }
+
+ return items;
+}
+
+void LibraryLoader::removeNow(const QString &spec)
+{
+ NoatunLibraryInfo info = getInfo(spec);
+ if (info.specfile == spec)
+ {
+ QValueList<NoatunLibraryInfo> l = loaded();
+ for (QValueList<NoatunLibraryInfo>::Iterator i = l.begin(); i != l.end(); ++i)
+ {
+ for (QStringList::ConstIterator it = (*i).require.begin(); it != (*i).require.end(); ++it)
+ {
+ if (*it == spec)
+ removeNow((*i).specfile);
+ }
+ }
+ }
+
+ PluginLibrary *lib=mLibHash[spec];
+
+ if (!lib) return;
+
+ delete lib->plugin;
+ lib->plugin=0;
+ mLibHash.remove(spec);
+ delete lib;
+}
+
+
diff --git a/noatun/library/pluginmodule.cpp b/noatun/library/pluginmodule.cpp
new file mode 100644
index 00000000..aec76e44
--- /dev/null
+++ b/noatun/library/pluginmodule.cpp
@@ -0,0 +1,406 @@
+// Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2000-2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#include <noatun/app.h>
+
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <qtabwidget.h>
+#include <qheader.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include "noatunlistview.h"
+#include "pluginmodule.h"
+
+#include <qwhatsthis.h>
+
+#include "common.h"
+
+PluginListItem::PluginListItem(const bool _exclusive, bool _checked, const NoatunLibraryInfo &_info, QListView *_parent)
+ : QCheckListItem(_parent, _info.name, CheckBox)
+ , mInfo(_info)
+ , silentStateChange(false)
+ , exclusive(_exclusive)
+{
+ setChecked(_checked);
+ if(_checked) static_cast<PluginListView *>(listView())->count++;
+}
+
+
+void PluginListItem::setChecked(bool b)
+{
+ silentStateChange = true;
+ setOn(b);
+ silentStateChange = false;
+}
+
+void PluginListItem::stateChange(bool b)
+{
+ if(!silentStateChange)
+ static_cast<PluginListView *>(listView())->stateChanged(this, b);
+}
+
+void PluginListItem::paintCell(QPainter *p, const QColorGroup &cg, int a, int b, int c)
+{
+ if(exclusive) myType = RadioButton;
+ QCheckListItem::paintCell(p, cg, a, b, c);
+ if(exclusive) myType = CheckBox;
+}
+
+PluginListView::PluginListView(unsigned _min, unsigned _max, QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(true)
+ , max(_max)
+ , min(_min <= _max ? _min : _max)
+ , count(0)
+{
+}
+
+PluginListView::PluginListView(unsigned _min, QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(false)
+ , min(_min)
+ , count(0)
+{
+}
+
+PluginListView::PluginListView(QWidget *_parent, const char *_name)
+ : KListView(_parent, _name)
+ , hasMaximum(false)
+ , min(0)
+ , count(0)
+{
+}
+
+void PluginListView::clear()
+{
+ count = 0;
+ KListView::clear();
+}
+
+void PluginListView::stateChanged(PluginListItem *item, bool b)
+{
+ if(b)
+ {
+ count++;
+ emit stateChange(item, b);
+
+ if(hasMaximum && count > max)
+ {
+ // Find a different one and turn it off
+
+ QListViewItem *cur = firstChild();
+ PluginListItem *curItem = dynamic_cast<PluginListItem *>(cur);
+
+ while(cur == item || !curItem || !curItem->isOn())
+ {
+ cur = cur->nextSibling();
+ curItem = dynamic_cast<PluginListItem *>(cur);
+ }
+
+ curItem->setOn(false);
+ }
+ }
+ else
+ {
+ if(count == min)
+ {
+ item->setChecked(true);
+ }
+ else
+ {
+ count--;
+ emit stateChange(item, b);
+ }
+ }
+}
+
+Plugins::Plugins(QObject *_parent)
+ : CModule(i18n("Plugins"), i18n("Select Your Plugins"), "gear", _parent)
+ , shown(false)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ QTabWidget *tabControl = new QTabWidget(this,"tabControl");
+
+ QFrame *interfaceTab = new QFrame(tabControl);
+ (new QVBoxLayout(interfaceTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select one or more interfaces to use:</b>"), interfaceTab);
+ // At least one interface is required
+ interfaceList = new PluginListView(1, interfaceTab);
+ interfaceList->addColumn(i18n("Name"));
+ interfaceList->addColumn(i18n("Description"));
+ interfaceList->addColumn(i18n("Author"));
+ interfaceList->addColumn(i18n("License"));
+ connect(interfaceList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(interfaceTab, i18n("&Interfaces"));
+
+ QFrame *playlistTab = new QFrame(tabControl);
+ (new QVBoxLayout(playlistTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select one playlist to use:</b>"), playlistTab);
+ // Exactly one playlist is required
+ playlistList = new PluginListView(1, 1, playlistTab);
+ playlistList->addColumn(i18n("Name"));
+ playlistList->addColumn(i18n("Description"));
+ playlistList->addColumn(i18n("Author"));
+ playlistList->addColumn(i18n("License"));
+ connect(playlistList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(playlistTab, i18n("&Playlist"));
+
+ QFrame *visTab = new QFrame(tabControl);
+ (new QVBoxLayout(visTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select any visualizations to use:</b>"), visTab);
+ visList = new PluginListView(0, visTab);
+ visList->addColumn(i18n("Name"));
+ visList->addColumn(i18n("Description"));
+ visList->addColumn(i18n("Author"));
+ visList->addColumn(i18n("License"));
+ connect(visList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(visTab, i18n("&Visualizations"));
+
+ // Other plugins are not restricted
+ QFrame *otherTab = new QFrame(tabControl);
+ (new QVBoxLayout(otherTab, KDialog::marginHint(), KDialog::spacingHint()))->setAutoAdd(true);
+ (void)new QLabel(i18n("<b>Select any other plugins to use:</b>"), otherTab);
+ otherList = new PluginListView(0, otherTab);
+ otherList->addColumn(i18n("Name"));
+ otherList->addColumn(i18n("Description"));
+ otherList->addColumn(i18n("Author"));
+ otherList->addColumn(i18n("License"));
+ connect(otherList, SIGNAL(stateChange(PluginListItem *, bool)), this, SLOT(stateChange(PluginListItem *, bool)));
+ tabControl->addTab(otherTab, i18n("O&ther Plugins"));
+}
+
+void Plugins::reopen()
+{
+ playlistList->clear();
+ interfaceList->clear();
+ otherList->clear();
+ visList->clear();
+
+ QValueList<NoatunLibraryInfo> available = napp->libraryLoader()->available();
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ for(QValueList<NoatunLibraryInfo>::Iterator i = available.begin(); i != available.end(); ++i)
+ {
+ PluginListView *parent;
+ bool exclusive = false;
+
+ if((*i).type == "userinterface")
+ {
+ parent = interfaceList;
+ }
+ else if((*i).type == "playlist")
+ {
+ parent = playlistList;
+ exclusive = true;
+ }
+ else if((*i).type == "sm" || (*i).type=="hidden")
+ {
+ parent = 0;
+ }
+ else if ((*i).type == "visualization")
+ {
+ parent = visList;
+ }
+ else
+ {
+ parent = otherList;
+ }
+
+ if(parent)
+ {
+ PluginListItem *item = new PluginListItem(exclusive, loaded.contains(*i), *i, parent);
+ item->setText(0, (*i).name);
+ item->setText(1, (*i).comment);
+ item->setText(2, (*i).author);
+ item->setText(3, (*i).license);
+ }
+ }
+}
+
+void Plugins::stateChange(PluginListItem *item, bool b)
+{
+ if(b)
+ addPlugin(item->info());
+ else
+ removePlugin(item->info());
+}
+
+void Plugins::addPlugin(const NoatunLibraryInfo &info)
+{
+ // Load any that this one depends upon
+ for(QStringList::ConstIterator i = info.require.begin(); i != info.require.end(); ++i)
+ {
+ NoatunLibraryInfo requiredInfo = napp->libraryLoader()->getInfo(*i);
+ PluginListItem *item = findItem(requiredInfo);
+ if(item) item->setOn(true);
+ }
+
+ if(mDeleted.contains(info.specfile))
+ mDeleted.remove(info.specfile);
+ else if(!mAdded.contains(info.specfile))
+ mAdded.append(info.specfile);
+}
+
+void Plugins::removePlugin(const NoatunLibraryInfo &info)
+{
+ LibraryLoader &loader = *(napp->libraryLoader());
+
+ // Here are the ones loaded
+ QValueList<NoatunLibraryInfo> loaded = napp->libraryLoader()->loaded();
+
+ // Add the ones marked for loading
+ for(QStringList::ConstIterator i = mAdded.begin(); i != mAdded.end(); ++i)
+ loaded.append(loader.getInfo(*i));
+
+ // Subtract the ones marked for removal
+ for(QStringList::ConstIterator i = mDeleted.begin(); i != mDeleted.end(); ++i)
+ loaded.remove(loader.getInfo(*i));
+
+ // If any depend on this plugin, mark them for removal (or remove them from mAdded)
+ for(QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ for(QStringList::ConstIterator j = (*i).require.begin(); j != (*i).require.end(); ++j)
+ {
+ if(*j == info.specfile)
+ {
+ PluginListItem *item = findItem(*i);
+ if(item) item->setOn(false);
+ }
+ }
+ }
+
+ if (mAdded.contains(info.specfile))
+ mAdded.remove(info.specfile);
+ else if(!mDeleted.contains(info.specfile))
+ mDeleted.append(info.specfile);
+}
+
+PluginListItem *Plugins::findItem(const NoatunLibraryInfo &info) const
+{
+ for(QListViewItem *cur = otherList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // visualizations
+ for(QListViewItem *cur = visList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // If our only interface has a dependency removed, that's a double dose of trouble
+ // We may as well have this here for completeness, though
+ for(QListViewItem *cur = interfaceList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ // If a playlist is added or removed due to a dependency, we're doom-diddly-oomed
+ // We may as well have this here for completeness, though
+ for(QListViewItem *cur = playlistList->firstChild(); cur != 0; cur = cur->itemBelow())
+ {
+ PluginListItem *item = dynamic_cast<PluginListItem *>(cur);
+ if(item && item->info() == info)
+ return item;
+ }
+
+ return 0;
+}
+
+void Plugins::save()
+{
+ LibraryLoader &loader = *(napp->libraryLoader());
+
+ // Load the plugins the user added
+ //loader.loadAll(mAdded);
+
+ QString oldPlaylist, newPlaylist;
+
+ // first load all non playlist things
+ for (QStringList::Iterator i = mAdded.begin(); i != mAdded.end(); ++i)
+ {
+ NoatunLibraryInfo info = loader.getInfo(*i);
+ if(info.type != "playlist")
+ loader.loadAll(QStringList(*i));
+ else
+ newPlaylist = (*i);
+ }
+
+ // Remove the plugins the user removed
+ for (QStringList::Iterator i = mDeleted.begin(); i != mDeleted.end(); ++i)
+ {
+ NoatunLibraryInfo info = loader.getInfo(*i);
+ if(info.type != "playlist")
+ loader.remove(*i);
+ else
+ oldPlaylist = *i;
+ }
+
+ // Loading normal plugins works the other way round!
+ // If you unload a playlist it sets the global playlist pointer to NULL,
+ // that also means you cannot first load the new and then unload the old one.
+ if(!newPlaylist.isEmpty() && !oldPlaylist.isEmpty())
+ {
+ kdDebug(66666) << k_funcinfo << "Unloading " << oldPlaylist << endl;
+ loader.remove(oldPlaylist);
+ kdDebug(66666) << k_funcinfo << "Loading " << oldPlaylist << endl;
+ loader.loadAll(QStringList(newPlaylist));
+ }
+
+
+ // Round up the ones that weren't loaded right now, for saving in the configuration
+ QStringList specList(mAdded);
+
+ QValueList<NoatunLibraryInfo> loaded = loader.loaded();
+ for(QValueList<NoatunLibraryInfo>::Iterator i = loaded.begin(); i != loaded.end(); ++i)
+ {
+ if(!specList.contains((*i).specfile) && loader.isLoaded((*i).specfile))
+ specList += (*i).specfile;
+ }
+
+ // Now we actually save
+ loader.setModules(specList);
+
+ mDeleted.clear();
+ mAdded.clear();
+}
+
+void Plugins::showEvent(QShowEvent *e)
+{
+ if(!shown)
+ {
+ shown = true;
+ KMessageBox::information(this, i18n("<qt>Changing your playlist plugin will stop playback. Different playlists may use different methods of storing information, so after changing playlists you may have to recreate your playlist.</qt>"), QString::null, "Plugin warning");
+ }
+ CModule::showEvent(e);
+}
+
+#include "pluginmodule.moc"
diff --git a/noatun/library/pluginmodule.h b/noatun/library/pluginmodule.h
new file mode 100644
index 00000000..75d3e04d
--- /dev/null
+++ b/noatun/library/pluginmodule.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2000-2001 Charles Samuels <charles@kde.org>
+// Copyright (c) 2000-2001 Neil Stevens <multivac@fcmail.com>
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIAB\ILITY, WHETHER IN
+// AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+#ifndef PLUGINMODULE_H
+#define PLUGINMODULE_H
+
+#include "noatun/pref.h"
+#include "noatun/pluginloader.h"
+
+#include <klistview.h>
+#include <qframe.h>
+#include <qstringlist.h>
+
+class KTabCtl;
+
+class PluginListItem : public QCheckListItem
+{
+public:
+ PluginListItem(const bool _exclusive, bool _checked, const NoatunLibraryInfo &_info, QListView *_parent);
+ const NoatunLibraryInfo &info() const { return mInfo; }
+
+ // This will toggle the state without "emitting" the stateChange
+ void setChecked(bool);
+
+protected:
+ virtual void stateChange(bool);
+ virtual void paintCell(QPainter *, const QColorGroup &, int, int, int);
+private:
+ NoatunLibraryInfo mInfo;
+ bool silentStateChange;
+ bool exclusive;
+};
+
+class PluginListView : public KListView
+{
+Q_OBJECT
+
+friend class PluginListItem;
+
+public:
+ PluginListView(QWidget *_parent = 0, const char *_name = 0);
+ PluginListView(unsigned _min, QWidget *_parent = 0, const char *_name = 0);
+ PluginListView(unsigned _min, unsigned _max, QWidget *_parent = 0, const char *_name = 0);
+
+ virtual void clear();
+
+signals:
+ void stateChange(PluginListItem *, bool);
+
+private:
+ void stateChanged(PluginListItem *, bool);
+
+ bool hasMaximum;
+ unsigned max, min;
+ unsigned count;
+};
+
+class Plugins : public CModule
+{
+Q_OBJECT
+public:
+ Plugins(QObject *_parent = 0);
+ virtual void save();
+ virtual void reopen();
+
+protected:
+ virtual void showEvent(QShowEvent *);
+
+private slots:
+ void stateChange(PluginListItem *, bool);
+
+private:
+ void addPlugin(const NoatunLibraryInfo &);
+ void removePlugin(const NoatunLibraryInfo &);
+ PluginListItem *findItem(const NoatunLibraryInfo &) const;
+
+ QStringList mAdded, mDeleted;
+ PluginListView *interfaceList, *playlistList, *otherList, *visList;
+
+ bool shown;
+};
+
+#endif
diff --git a/noatun/library/pref.cpp b/noatun/library/pref.cpp
new file mode 100644
index 00000000..98e71645
--- /dev/null
+++ b/noatun/library/pref.cpp
@@ -0,0 +1,95 @@
+#include "pref.h"
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kdebug.h>
+#include <qlayout.h>
+//#include <qlabel.h>
+#include "cmodule.h"
+
+NoatunPreferences::NoatunPreferences(QWidget *parent)
+ : KDialogBase(TreeList, i18n("Preferences - Noatun"),
+ Ok|Apply|Cancel|Help, Ok, parent, "NoatunPreferences", false, true)
+{
+ resize(640, 480); // KDE is required to support 800x600 min.
+ setShowIconsInTreeList(true);
+ setRootIsDecorated(false);
+}
+
+void NoatunPreferences::slotOk()
+{
+ slotApply();
+ hide();
+}
+
+void NoatunPreferences::show()
+{
+ for (CModule *i=mModules.first(); i != 0; i=mModules.next())
+ i->reopen();
+ KDialogBase::show();
+}
+
+void NoatunPreferences::show(CModule *page)
+{
+ int index = pageIndex( static_cast<QWidget *>(page->parent()) );
+ if (index != -1)
+ showPage(index);
+ show();
+}
+
+void NoatunPreferences::slotApply()
+{
+ for (CModule *i=mModules.first(); i != 0; i=mModules.next())
+ i->save();
+}
+
+void NoatunPreferences::add(CModule *page)
+{
+ mModules.append(page);
+}
+
+void NoatunPreferences::remove(CModule *page)
+{
+ mModules.removeRef(page);
+}
+
+CModule::CModule(const QString &name, const QString &description, const QString &icon, QObject *owner)
+ : QWidget(napp->preferencesBox()->addPage(name, description, KGlobal::iconLoader()->loadIcon(
+ icon, KIcon::Small,0, KIcon::DefaultState,0, true)))
+{
+ if (owner)
+ connect(owner, SIGNAL(destroyed()), SLOT(ownerDeleted()));
+
+ //kdDebug(66666) << k_funcinfo << "name = " << name << endl;
+
+ napp->preferencesBox()->add(this);
+
+ QFrame *page=static_cast<QFrame*>(parent());
+ (new QHBoxLayout(page))->addWidget(this);
+}
+
+CModule::~CModule()
+{
+ //kdDebug(66666) << k_funcinfo << endl;
+#if QT_VERSION < 0x030102 && KDE_VERSION < KDE_MAKE_VERSION( 3, 1, 90 )
+ // Due to a bug in Qt 3.1 and 3.1.1 no close events are sent to hidden
+ // widgets, causing the KJanusWidget to crash. This workaround is
+ // rather intrusive and should be used only in the affected versions
+ // to avoid hard to track bugs in the future. KDE HEAD (to become 3.2)
+ // has a workaround for this problem, and additionally it's fixed in
+ // Qt 3.1.2.
+ napp->sendPostedEvents();
+#endif
+
+ napp->preferencesBox()->remove(this);
+}
+
+void CModule::ownerDeleted()
+{
+ QObject *p=parent();
+ delete this;
+ p->deleteLater();
+}
+
+#include "pref.moc"
diff --git a/noatun/library/scrollinglabel.cpp b/noatun/library/scrollinglabel.cpp
new file mode 100644
index 00000000..d7bdd643
--- /dev/null
+++ b/noatun/library/scrollinglabel.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2000 Rik Hemsley (rikkus) <rik@kde.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <noatun/scrollinglabel.h>
+
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+class ScrollingLabel::Private
+{
+ public:
+
+ Private()
+ : scrollSize (0),
+ pos (0),
+ add (false),
+ scroll (true)
+ {
+ }
+
+ // Order dependency.
+ QString text;
+ QString originalText;
+ QPixmap buf;
+ QTimer scrollTimer;
+ QTimer resetTimer;
+ int scrollSize;
+ int pos;
+ bool add;
+ bool scroll;
+ // End order dependency.
+};
+
+ScrollingLabel::ScrollingLabel
+(
+ const QString & initialText,
+ QWidget * parent,
+ const char * name
+)
+ : QWidget(parent, name)
+{
+ d = new Private;
+
+ connect(&d->scrollTimer, SIGNAL(timeout()), this, SLOT(scroll()));
+ connect(&d->resetTimer, SIGNAL(timeout()), this, SLOT(restoreText()));
+
+ setText(initialText);
+}
+
+ScrollingLabel::~ScrollingLabel()
+{
+ delete d;
+}
+
+ void
+ScrollingLabel::setText(const QString & t, int time)
+{
+ d->resetTimer.stop();
+
+ if (-1 != time)
+ {
+ restoreText();
+ d->originalText = d->text;
+ d->text = t;
+ d->resetTimer.start(time, true);
+ _update();
+ }
+ else
+ {
+ d->text = d->originalText = t;
+ _update();
+ }
+
+ QToolTip::remove(this);
+ QToolTip::add(this, d->text);
+}
+
+ void
+ScrollingLabel::restoreText()
+{
+ d->text = d->originalText;
+ _update();
+}
+
+ void
+ScrollingLabel::_update()
+{
+ d->scrollTimer.stop();
+
+ d->pos = 0;
+ d->add = false;
+
+ int w = fontMetrics().width(d->text);
+ int h = fontMetrics().height();
+
+ setFixedHeight(h);
+
+ d->scrollSize = QMAX(0, w - width());
+
+ d->buf.resize(w, h);
+ d->buf.fill(colorGroup().background());
+
+ QPainter p(&d->buf);
+ p.setFont(font());
+ p.drawText(0, fontMetrics().ascent(), d->text);
+
+ if (d->scroll && (d->scrollSize > 0))
+ d->scrollTimer.start(100, true);
+
+ repaint(false);
+}
+
+ void
+ScrollingLabel::paintEvent(QPaintEvent *)
+{
+ bitBlt
+ (this, 0, 0, &d->buf, d->pos, 0, d->pos + width(), height(), Qt::CopyROP);
+}
+
+ void
+ScrollingLabel::resizeEvent(QResizeEvent *)
+{
+ _update();
+}
+
+ void
+ScrollingLabel::scroll()
+{
+ d->scrollTimer.stop();
+
+ repaint(false);
+
+ int scrollTime = 100;
+
+ if (d->pos == d->scrollSize || d->pos == 0)
+ {
+ d->add = !d->add;
+ scrollTime = 800;
+ }
+
+ d->pos += (d->add ? 1 : -1);
+
+ if (d->scroll)
+ d->scrollTimer.start(scrollTime, true);
+}
+
+ QSize
+ScrollingLabel::sizeHint() const
+{
+ return fontMetrics().boundingRect(d->text).size();
+}
+
+ QSize
+ScrollingLabel::minimumSizeHint() const
+{
+ return QSize(0, fontMetrics().height());
+}
+
+ QString
+ScrollingLabel::text() const
+{
+ return d->text;
+}
+
+ void
+ScrollingLabel::setScroll(bool b)
+{
+ d->scroll = b;
+ _update();
+}
+
+#include "scrollinglabel.moc"
+
+// vim:ts=2:sw=2:tw=78:noet
+
diff --git a/noatun/library/spline.cpp b/noatun/library/spline.cpp
new file mode 100644
index 00000000..d09c12dc
--- /dev/null
+++ b/noatun/library/spline.cpp
@@ -0,0 +1,194 @@
+/*
+ Copyright (C) 1998 Jürgen Hochwald <juergen.hochwald@privat.kkf.net>
+ Copyright (C) 2003 Charles Samuels <charles@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+
+
+#include <stdio.h>
+#include "spline.h"
+
+const bool Spline::natural=false;
+
+Spline::Spline()
+{
+ yp1=0.0;
+ ypn=0.0;
+ mRecalc = true;
+}
+
+Spline::Spline(const Spline &copy)
+{
+ operator=(copy);
+}
+
+Spline &Spline::operator =(const Spline &copy)
+{
+ mPoints = copy.mPoints;
+ mRecalc = copy.mRecalc;
+ yp1 = copy.yp1;
+ ypn = copy.ypn;
+ return *this;
+}
+
+Spline::~Spline()
+{
+
+}
+
+void Spline::add(double x, double y)
+{
+ Group g = { x, y, 0.0 };
+ mPoints.push_back(g);
+ mRecalc=true;
+}
+
+std::vector<double> Spline::points(int count) const
+{
+ std::vector<double> points;
+ if (count == numPoints())
+ {
+ for (int i=0; i < count; ++i)
+ {
+ points.push_back(mPoints[i].y);
+ }
+ }
+ else
+ {
+ double min = mPoints[0].x;
+ double max = mPoints[numPoints()-1].x;
+ double dcount = double(count);
+
+ for (int i=0; i<count; ++i)
+ {
+ points.push_back(spline( (max-min)/dcount*i + min));
+ }
+ }
+
+ return points;
+}
+
+void Spline::calcSpline()
+{
+ const int np=numPoints();
+ double *u = new double[np];
+
+ if (natural)
+ {
+ mPoints[0].y2 = u[0] = 0.0;
+ }
+ else
+ {
+ mPoints[0].y2 = -0.5;
+ u[0] =
+ (3.0/(mPoints[1].x-mPoints[0].x))*((mPoints[1].y-mPoints[0].y)
+ / (mPoints[1].x-mPoints[0].x)-yp1);
+ }
+
+ double sig,p,qn,un;
+ for (int i=1; i<=np-2; i++)
+ {
+ sig=(mPoints[i].x-mPoints[i-1].x) / (mPoints[i+1].x-mPoints[i-1].x);
+ p = sig*mPoints[i-1].y2+2.0;
+ mPoints[i].y2 = (sig-1.0)/p;
+ u[i] =
+ (mPoints[i+1].y - mPoints[i].y) / (mPoints[i+1].x-mPoints[i].x)
+ - (mPoints[i].y - mPoints[i-1].y) / (mPoints[i].x - mPoints[i-1].x);
+ u[i] = (6.0*u[i] / (mPoints[i+1].x - mPoints[i-1].x) - sig *u[i-1])/p;
+ }
+
+ if (natural)
+ {
+ qn = un = 0.0;
+ }
+ else
+ {
+ qn = 0.5;
+ un =
+ (3.0 / (mPoints[np-1].x - mPoints[np-2].x))
+ * (ypn - (mPoints[np-1].y - mPoints[np-2].y)
+ / (mPoints[np-1].x - mPoints[np-2].x));
+ }
+ mPoints[np-1].y2 = (un - qn * u[np-2]) / (qn*mPoints[np-2].y2 +1.0 );
+
+ for (int i=np-2; i>=0; i--)
+ {
+ mPoints[i].y2 = mPoints[i].y2 * mPoints[i+1].y2+u[i];
+ }
+ mRecalc = false;
+ delete [] u;
+}
+
+double Spline::spline(double xarg) const
+{
+ if (numPoints()==0) return 0.0;
+ if (numPoints()==1) return mPoints[0].y;
+
+ if (mRecalc) calcSpline();
+
+ int klo=0;
+ int khi=numPoints()-1;
+ int k;
+ while (khi-klo > 1)
+ {
+ k = khi+klo;
+ if (k % 2)
+ k = (k+1) / 2;
+ else
+ k = k/2;
+
+ if (mPoints[k].x > xarg) khi=k;
+ else klo=k;
+ }
+
+ double h = mPoints[khi].x - mPoints[klo].x;
+ if (h==0)
+ {
+ // failed
+ return 0.0;
+ }
+
+ double a = (mPoints[khi].x - xarg) / h;
+ double b = (xarg - mPoints[klo].x) / h;
+ return
+ a * mPoints[klo].y + b*mPoints[khi].y
+ + ((a*a*a-a) * mPoints[klo].y2
+ + (b*b*b-b) * mPoints[khi].y2) * (h*h) / 6.0;
+}
+
+double Spline::x(int num) const
+{
+ if (numPoints()<num) return 0.0;
+ return mPoints[num].x;
+}
+
+double Spline::y(int num) const
+{
+ if (numPoints()<num) return 0.0;
+ return mPoints[num].y;
+}
+
+void Spline::clear()
+{
+ mPoints.resize(0);
+ ypn=0.0;
+ yp1=0.0;
+ mRecalc=true;
+}
+
+
diff --git a/noatun/library/spline.h b/noatun/library/spline.h
new file mode 100644
index 00000000..db1bb03b
--- /dev/null
+++ b/noatun/library/spline.h
@@ -0,0 +1,74 @@
+/*
+Copyright (C) 1998 Jürgen Hochwald <juergen.hochwald@privat.kkf.net>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this library; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+*/
+
+
+
+#ifndef SPLINE_H
+#define SPLINE_H
+
+#include <stdlib.h>
+#include <vector>
+
+class Spline
+{
+ struct Group
+ {
+ double x, y, y2;
+ };
+
+ std::vector<Spline::Group> mPoints;
+ bool mRecalc;
+ double yp1;
+ double ypn;
+
+ // stupid AIX[?] compiler won't let me give it a value here
+ static const bool natural;
+
+public:
+ Spline();
+ Spline(const Spline &copy);
+
+ Spline &operator =(const Spline &copy);
+ ~Spline();
+
+ /**
+ * if the curve had @p count points, return them
+ **/
+ std::vector<double> points(int count) const;
+
+ void add(double x, double y);
+ double spline(double xarg) const;
+ double operator[] (double xarg) const { return spline(xarg); }
+ int numPoints() const { return mPoints.size(); }
+ double x(int num) const;
+ double y(int num) const;
+
+ void clear();
+
+private:
+ void calcSpline() const
+ {
+ const_cast<Spline*>(this)->calcSpline();
+ }
+ void calcSpline();
+
+};
+
+#endif
+
diff --git a/noatun/library/stereobuttonaction.cpp b/noatun/library/stereobuttonaction.cpp
new file mode 100644
index 00000000..bfb688f7
--- /dev/null
+++ b/noatun/library/stereobuttonaction.cpp
@@ -0,0 +1,47 @@
+#include "stereobuttonaction.h"
+
+namespace NoatunStdAction
+{
+
+StereoButtonAction::StereoButtonAction(const QString& text, int accel, QObject* parent, const char* name )
+ : KAction(text, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, accel, receiver, slot, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QIconSet& pix, int accel, QObject* parent, const char* name )
+ : KAction(text, pix, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QString& pix, int accel, QObject* parent, const char* name )
+ : KAction(text, pix, accel, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QIconSet& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, pix, accel, receiver, slot, parent, name)
+{}
+
+StereoButtonAction::StereoButtonAction(const QString& text, const QString& pix, int accel, const QObject* receiver, const char* slot, QObject* parent, const char* name )
+ : KAction(text, pix, accel, receiver, slot, parent, name)
+{}
+
+void StereoButtonAction::disable(void)
+{
+ setEnabled(false);
+}
+
+void StereoButtonAction::enable(void)
+{
+ setEnabled(true);
+}
+
+void StereoButtonAction::toggleEnabled(void)
+{
+ setEnabled(!isEnabled());
+}
+
+}
+
+#include "stereobuttonaction.moc"
diff --git a/noatun/library/titleproxy.cpp b/noatun/library/titleproxy.cpp
new file mode 100644
index 00000000..7515a35a
--- /dev/null
+++ b/noatun/library/titleproxy.cpp
@@ -0,0 +1,376 @@
+/***************************************************************************
+ Proxy.cpp - description
+ -------------------
+begin : Nov 20 14:35:18 CEST 2003
+copyright : (C) 2003 by Mark Kretschmann
+email : markey@web.de
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include "titleproxy.h"
+
+#include <kdebug.h>
+#include <kprotocolmanager.h>
+#include <kmdcodec.h>
+
+#include <qstring.h>
+#include <qtimer.h>
+#include "noatun/app.h"
+
+using namespace TitleProxy;
+
+static const uint MIN_PROXYPORT = 6700;
+static const uint MAX_PROXYPORT = 7777;
+static const int BUFSIZE = 32768;
+
+Proxy::Proxy( KURL url )
+ : QObject()
+ , m_url( url )
+ , m_initSuccess( true )
+ , m_metaInt( 0 )
+ , m_byteCount( 0 )
+ , m_metaLen( 0 )
+ , m_usedPort( 0 )
+ , m_pBuf( 0 )
+{
+ kdDebug(66666) << k_funcinfo << endl;
+
+ m_pBuf = new char[ BUFSIZE ];
+ // Don't try to get metdata for ogg streams (different protocol)
+ m_icyMode = url.path().endsWith( ".ogg" ) ? false : true;
+ // If no port is specified, use default shoutcast port
+ if ( m_url.port() < 1 )
+ m_url.setPort( 80 );
+
+ connect( &m_sockRemote, SIGNAL( error( int ) ), this, SLOT( connectError() ) );
+ connect( &m_sockRemote, SIGNAL( connected() ), this, SLOT( sendRequest() ) );
+ connect( &m_sockRemote, SIGNAL( readyRead() ), this, SLOT( readRemote() ) );
+
+ uint i = 0;
+ Server* server = 0;
+ for ( i = MIN_PROXYPORT; i <= MAX_PROXYPORT; i++ )
+ {
+ server = new Server( i, this );
+ kdDebug(66666) << k_funcinfo <<
+ "Trying to bind to port: " << i << endl;
+ if ( server->ok() ) // found a free port
+ break;
+ delete server;
+ }
+
+ if ( i > MAX_PROXYPORT )
+ {
+ kdWarning(66666) << k_funcinfo <<
+ "Unable to find a free local port. Aborting." << endl;
+ m_initSuccess = false;
+ return;
+ }
+ m_usedPort = i;
+ connect( server, SIGNAL( connected( int ) ), this, SLOT( accept( int ) ) );
+}
+
+
+Proxy::~Proxy()
+{
+ kdDebug(66666) << k_funcinfo << endl;
+ delete[] m_pBuf;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC
+//////////////////////////////////////////////////////////////////////////////////////////
+
+KURL Proxy::proxyUrl()
+{
+ if ( m_initSuccess )
+ {
+ KURL url;
+ url.setPort( m_usedPort );
+ url.setHost( "localhost" );
+ url.setProtocol( "http" );
+ return url;
+ }
+ else
+ return m_url;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PRIVATE SLOTS
+//////////////////////////////////////////////////////////////////////////////////////////
+
+void Proxy::accept( int socket ) //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+ m_sockProxy.setSocket( socket );
+ m_sockProxy.waitForMore( KProtocolManager::readTimeout() * 1000 );
+ connectToHost();
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::connectToHost() //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+
+ { //initialisations
+ m_connectSuccess = false;
+ m_headerFinished = false;
+ m_headerStr = "";
+ }
+
+ { //connect to server
+ QTimer::singleShot( KProtocolManager::connectTimeout() * 1000,
+ this, SLOT( connectError() ) );
+
+ kdDebug(66666) << k_funcinfo << "Connecting to " <<
+ m_url.host() << ":" << m_url.port() << endl;
+
+ m_sockRemote.connectToHost( m_url.host(), m_url.port() );
+ }
+
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::sendRequest() //SLOT
+{
+ //kdDebug(66666) << "BEGIN " << k_funcinfo << endl;
+
+ QCString username = m_url.user().utf8();
+ QCString password = m_url.pass().utf8();
+ QCString authString = KCodecs::base64Encode( username + ":" + password );
+ bool auth = !( username.isEmpty() && password.isEmpty() );
+
+ QString request = QString( "GET %1 HTTP/1.0\r\n"
+ "Host: %2\r\n"
+ "User-Agent: Noatun/%5\r\n"
+ "%3"
+ "%4"
+ "\r\n" )
+ .arg( m_url.path( -1 ).isEmpty() ? "/" : m_url.path( -1 ) )
+ .arg( m_url.host() )
+ .arg( m_icyMode ? QString( "Icy-MetaData:1\r\n" ) : QString::null )
+ .arg( auth ? QString( "Authorization: Basic " ).append( authString ) : QString::null )
+ .arg( NOATUN_VERSION );
+
+ m_sockRemote.writeBlock( request.latin1(), request.length() );
+
+ //kdDebug(66666) << "END " << k_funcinfo << endl;
+}
+
+
+void Proxy::readRemote() //SLOT
+{
+ m_connectSuccess = true;
+ Q_LONG index = 0;
+ Q_LONG bytesWrite = 0;
+ Q_LONG bytesRead = m_sockRemote.readBlock( m_pBuf, BUFSIZE );
+ if ( bytesRead == -1 )
+ {
+ kdDebug(66666) << k_funcinfo << "Could not read remote data from socket, aborting" << endl;
+ m_sockRemote.close();
+ emit proxyError();
+ return;
+ }
+
+ if ( !m_headerFinished )
+ {
+ if ( !processHeader( index, bytesRead ) )
+ return;
+ }
+
+ //This is the main loop which processes the stream data
+ while ( index < bytesRead )
+ {
+ if ( m_icyMode && m_metaInt && ( m_byteCount == m_metaInt ) )
+ {
+ m_byteCount = 0;
+ m_metaLen = m_pBuf[ index++ ] << 4;
+ }
+ else if ( m_icyMode && m_metaLen )
+ {
+ m_metaData.append( m_pBuf[ index++ ] );
+ --m_metaLen;
+
+ if ( !m_metaLen )
+ {
+ transmitData( m_metaData );
+ m_metaData = "";
+ }
+ }
+ else
+ {
+ bytesWrite = bytesRead - index;
+
+ if ( m_icyMode && bytesWrite > m_metaInt - m_byteCount )
+ bytesWrite = m_metaInt - m_byteCount;
+ bytesWrite = m_sockProxy.writeBlock( m_pBuf + index, bytesWrite );
+
+ if ( bytesWrite == -1 )
+ {
+ error();
+ return;
+ }
+
+ index += bytesWrite;
+ m_byteCount += bytesWrite;
+ }
+ }
+}
+
+
+void Proxy::connectError() //SLOT
+{
+ if ( !m_connectSuccess )
+ {
+ kdWarning(66666) <<
+ "TitleProxy error: Unable to connect to this stream " <<
+ "server. Can't play the stream!" << endl;
+
+ emit proxyError();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// PRIVATE
+//////////////////////////////////////////////////////////////////////////////////////////
+
+bool Proxy::processHeader( Q_LONG &index, Q_LONG bytesRead )
+{
+ while ( index < bytesRead )
+ {
+ m_headerStr.append( m_pBuf[ index++ ] );
+ if ( m_headerStr.endsWith( "\r\n\r\n" ) )
+ {
+ /*kdDebug(66666) << k_funcinfo <<
+ "Got shoutcast header: '" << m_headerStr << "'" << endl;*/
+
+ // Handle redirection
+ QString loc( "Location: " );
+ int index = m_headerStr.find( loc );
+ if ( index >= 0 )
+ {
+ int start = index + loc.length();
+ int end = m_headerStr.find( "\n", index );
+ m_url = m_headerStr.mid( start, end - start - 1 );
+
+ kdDebug(66666) << k_funcinfo <<
+ "Stream redirected to: " << m_url << endl;
+
+ m_sockRemote.close();
+ connectToHost();
+ return false;
+ }
+
+
+ if (m_headerStr.startsWith("ICY"))
+ {
+ m_metaInt = m_headerStr.section( "icy-metaint:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 ).toInt();
+ m_bitRate = m_headerStr.section( "icy-br:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamName = m_headerStr.section( "icy-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "icy-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "icy-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ else // not ShoutCast
+ {
+ QString serverName = m_headerStr.section( "Server:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ kdDebug(66666) << k_funcinfo << "Server name: " << serverName << endl;
+
+ if (serverName == "Icecast")
+ {
+ m_metaInt = 0;
+ m_streamName = m_headerStr.section( "ice-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "ice-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "ice-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ else if (serverName.startsWith("icecast/1."))
+ {
+ m_metaInt = 0;
+ m_bitRate = m_headerStr.section( "x-audiocast-bitrate:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamName = m_headerStr.section( "x-audiocast-name:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamGenre = m_headerStr.section( "x-audiocast-genre:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ m_streamUrl = m_headerStr.section( "x-audiocast-url:", 1, 1, QString::SectionCaseInsensitiveSeps ).section( "\r", 0, 0 );
+ }
+ }
+
+ if ( m_streamUrl.startsWith( "www.", true ) )
+ m_streamUrl.prepend( "http://" );
+
+ m_sockProxy.writeBlock( m_headerStr.latin1(), m_headerStr.length() );
+ m_headerFinished = true;
+
+ if ( m_icyMode && !m_metaInt )
+ {
+ error();
+ return false;
+ }
+
+ connect( &m_sockRemote, SIGNAL( connectionClosed() ),
+ this, SLOT( connectError() ) );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Proxy::transmitData( const QString &data )
+{
+ /*kdDebug(66666) << k_funcinfo <<
+ " received new metadata: '" << data << "'" << endl;*/
+
+ //prevent metadata spam by ignoring repeated identical data
+ //(some servers repeat it every 10 seconds)
+ if ( data == m_lastMetadata )
+ return;
+
+ m_lastMetadata = data;
+
+ emit metaData(
+ m_streamName, m_streamGenre, m_streamUrl, m_bitRate,
+ extractStr(data, QString::fromLatin1("StreamTitle")),
+ extractStr(data, QString::fromLatin1("StreamUrl")));
+}
+
+
+void Proxy::error()
+{
+ kdDebug(66666) <<
+ "TitleProxy error: Stream does not support shoutcast metadata. " <<
+ "Restarting in non-metadata mode." << endl;
+
+ m_sockRemote.close();
+ m_icyMode = false;
+
+ //open stream again, but this time without metadata, please
+ connectToHost();
+}
+
+
+QString Proxy::extractStr( const QString &str, const QString &key )
+{
+ int index = str.find( key, 0, true );
+ if ( index == -1 )
+ {
+ return QString::null;
+ }
+ else
+ {
+ index = str.find( "'", index ) + 1;
+ int indexEnd = str.find( "'", index );
+ return str.mid( index, indexEnd - index );
+ }
+}
+
+#include "titleproxy.moc"
diff --git a/noatun/library/titleproxy.h b/noatun/library/titleproxy.h
new file mode 100644
index 00000000..058943d6
--- /dev/null
+++ b/noatun/library/titleproxy.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ titleproxy.h - description
+ -------------------
+begin : Nov 20 14:35:18 CEST 2003
+copyright : (C) 2003 by Mark Kretschmann
+email :
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef AMAROK_TITLEPROXY_H
+#define AMAROK_TITLEPROXY_H
+
+#include <kurl.h> //stack allocated
+
+#include <qobject.h>
+#include <qserversocket.h> //baseclass
+#include <qsocket.h> //stack allocated
+
+class QString;
+
+namespace TitleProxy
+{
+ /**
+ * Proxy Concept:
+ * 1. Connect to streamserver
+ * 2. Listen on localhost, let aRts connect to proxyserver
+ * 3. Write GET request to streamserver, containing Icy-MetaData:1 token
+ * 4. Read MetaInt token from streamserver (==metadata offset)
+ *
+ * 5. Read stream data (mp3 + metadata) from streamserver
+ * 6. Filter out metadata, send to app
+ * 7. Write mp3 data to proxyserver
+ * 8. Goto 5
+ *
+ * Some info on the shoutcast metadata protocol can be found at:
+ * @see http://www.smackfu.com/stuff/programming/shoutcast.html
+ *
+ * @short A proxy server for extracting metadata from Shoutcast streams.
+ */
+
+ class Proxy : public QObject
+ {
+ Q_OBJECT
+ public:
+ Proxy( KURL url );
+ ~Proxy();
+
+ bool initSuccess() { return m_initSuccess; }
+ KURL proxyUrl();
+
+ signals:
+ void metaData(
+ const QString &streamName,
+ const QString &streamGenre,
+ const QString &streamUrl,
+ const QString &streamBitrate,
+ const QString &trackTitle,
+ const QString &trackUrl);
+ void proxyError();
+
+ private slots:
+ void accept( int socket );
+ void connectToHost();
+ void sendRequest();
+ void readRemote();
+ void connectError();
+
+ private:
+ bool processHeader( Q_LONG &index, Q_LONG bytesRead );
+ void transmitData( const QString &data );
+ void error();
+ QString extractStr( const QString &str, const QString &key );
+
+ //ATTRIBUTES:
+ KURL m_url;
+ int m_streamingMode;
+ bool m_initSuccess;
+ bool m_connectSuccess;
+
+ int m_metaInt;
+ QString m_bitRate;
+ int m_byteCount;
+ uint m_metaLen;
+
+ QString m_metaData;
+ bool m_headerFinished;
+ QString m_headerStr;
+ int m_usedPort;
+ QString m_lastMetadata;
+ bool m_icyMode;
+
+ QString m_streamName;
+ QString m_streamGenre;
+ QString m_streamUrl;
+
+ char *m_pBuf;
+
+ QSocket m_sockRemote;
+ QSocket m_sockProxy;
+ };
+
+
+ class Server : public QServerSocket
+ {
+ Q_OBJECT
+
+ public:
+ Server( Q_UINT16 port, QObject* parent )
+ : QServerSocket( port, 1, parent, "TitleProxyServer" ) {};
+
+ signals:
+ void connected( int socket );
+
+ private:
+ void newConnection( int socket ) { emit connected( socket ); }
+ };
+
+} //namespace TitleProxy
+
+#endif /*AMAROK_TITLEPROXY_H*/
+
diff --git a/noatun/library/vequalizer.cpp b/noatun/library/vequalizer.cpp
new file mode 100644
index 00000000..4b131e8a
--- /dev/null
+++ b/noatun/library/vequalizer.cpp
@@ -0,0 +1,936 @@
+/*
+ * Copyright (c) 2003 Charles Samuels <charles@kde.org>
+ *
+ * This file is hereby licensed under the GNU General Public License version
+ * 2 or later at your option.
+ *
+ * This file is licensed under the Qt Public License version 1 with the
+ * condition that the licensed will be governed under the Laws of California
+ * (USA) instead of Norway. Disputes will be settled in Santa Clara county
+ * courts.
+ *
+ * This file is licensed under the following additional license. Be aware
+ * that it is identical to the BSD license, except for the added clause 3:
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. By integrating this software into any other software codebase, you waive
+ all rights to any patents associated with the stated codebase.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "vequalizer.h"
+#include "engine.h"
+#include "spline.h"
+#include "ksaver.h"
+
+#include <noatunarts.h>
+#include <app.h>
+#include <player.h>
+
+#include <common.h>
+#include <dynamicrequest.h>
+#include <artsflow.h>
+#include <soundserver.h>
+
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kstandarddirs.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+#include <qdom.h>
+#include <qtextstream.h>
+#include <qfile.h>
+
+#include <math.h>
+
+#define EQ (napp->vequalizer())
+#define EQBACK (napp->player()->engine()->equalizer())
+
+using std::vector;
+
+static const double splineTension = 4.0;
+
+VBandsInterface::VBandsInterface()
+{
+
+}
+
+VBandsInterface::~VBandsInterface()
+{
+
+}
+
+
+VBand VBandsInterface::operator [] (int num)
+{
+ return band(num);
+}
+
+struct VBand::Private
+{
+ int refs;
+
+ int index;
+ int start, end;
+ VBandsInterface *bands;
+};
+
+VBand::VBand(VBandsInterface *bands, int index, int start, int end)
+{
+ d = new Private;
+ d->refs=1;
+ d->index = index;
+ d->start = start;
+ d->end = end;
+ d->bands = bands;
+}
+
+VBand::~VBand()
+{
+ if (--d->refs == 0)
+ {
+ delete d;
+ }
+}
+
+VBand::VBand(const VBand &copy)
+{
+ d=0;
+ operator=(copy);
+}
+
+VBand & VBand::operator =(const VBand &copy)
+{
+ if (d && --d->refs == 0)
+ {
+ delete d;
+ }
+
+ d= copy.d;
+ d->refs++;
+ return *this;
+}
+
+int VBand::level() const
+{
+ return d->bands->level(d->index);
+}
+
+void VBand::setLevel(int l)
+{
+ d->bands->setLevel(d->index, l);
+}
+
+int VBand::start() const
+{
+ return d->start;
+}
+
+int VBand::end() const
+{
+ return d->end;
+}
+
+int VBand::center() const
+{
+ return (d->start + d->end)/2;
+}
+
+static QString formatFreq(int f, bool withHz)
+{
+ QString format;
+ if (f<991)
+ format=QString::number(f);
+ else
+ format=QString::number((int)((f+500)/1000.0))+"k";
+
+ if (withHz)
+ format+="Hz";
+
+ return format;
+}
+
+QString VBand::formatStart(bool withHz) const
+{
+ return formatFreq(d->start, withHz);
+}
+
+QString VBand::formatEnd(bool withHz) const
+{
+ return formatFreq(d->end, withHz);
+}
+
+QString VBand::format(bool withHz) const
+{
+ return formatFreq(center(), withHz);
+}
+
+
+
+
+struct VInterpolation::Private
+{
+ int bands;
+ Spline spline;
+
+};
+
+VInterpolation::VInterpolation(int bands)
+{
+ d = new Private;
+ d->bands = bands;
+}
+
+VInterpolation::~VInterpolation()
+{
+ delete d;
+}
+
+int VInterpolation::bands() const
+{
+ return d->bands;
+}
+
+void VInterpolation::getFrequencies(int num, int *low, int *high) const
+{
+ QValueList<int> fs = VEqualizer::frequencies(bands());
+
+ if (num == 0) *low = 1;
+ else *low = fs[num-1]+1;
+ *high=fs[num];
+}
+
+
+VBand VInterpolation::band(int num)
+{
+ int low, high;
+ getFrequencies(num, &low, &high);
+ return VBand(this, num, low, high);
+}
+
+int VInterpolation::level(int index) const
+{
+ const_cast<VInterpolation*>(this)->refresh();
+ double x = onSpline(index);
+
+ return int(d->spline[x*splineTension]);
+}
+
+void VInterpolation::setLevel(int index, int level)
+{
+ refresh();
+
+ double numbands = double(bands());
+ Spline spline;
+
+ for (int i=0; i < numbands; ++i)
+ {
+ VBand b = band(i);
+ spline.add(i*splineTension, double(index == i ? level : b.level()));
+ }
+
+ int realbands = EQ->bands();
+ QValueList<int> values;
+ for (int i=0; i < realbands; ++i)
+ {
+ // i
+ // realbands numbands
+ double x = double(i)*numbands/double(realbands)*splineTension;
+ double value = spline[x];
+ values.append(int(value));
+ }
+ EQ->setLevels(values);
+}
+
+double VInterpolation::onSpline(int bandNum) const
+{
+ double maxReal(EQ->bands()); // 2
+ double maxInter(bands()); // 4
+ double offset(bandNum); // 4
+
+ return maxReal/maxInter*offset;
+}
+
+void VInterpolation::refresh()
+{
+ d->spline.clear();
+
+ VEqualizer *eq = EQ;
+
+ for (int i=0; i < eq->bands(); ++i)
+ {
+ VBand band = eq->band(i);
+ d->spline.add(double(i*splineTension), double(band.level()));
+ }
+
+}
+
+struct VEqualizer::Private
+{
+ struct BandInfo
+ {
+ int level;
+ int start;
+ int end;
+ };
+
+ std::vector<BandInfo> bands;
+ int preamp;
+};
+
+/* rate 4
+27 54 0-108 108
+81 163 109-217 108
+243 514 218-810 269
+729 1621 811-2431 1620
+2187 4661 2432-7290 4858
+6561 13645 7291+ 12708
+*/
+VEqualizer::VEqualizer()
+{
+ d = new Private;
+ d->preamp=1;
+ setBands(6, false);
+}
+
+void VEqualizer::init()
+{
+ KURL url;
+ url.setPath(kapp->dirs()->localkdedir()+"/share/apps/noatun/equalizer");
+ if(!load(url))
+ {
+ setPreamp(0);
+ disable();
+ }
+ else
+ {
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ setEnabled(config->readBoolEntry("enabled", false));
+ }
+}
+
+
+VEqualizer::~VEqualizer()
+{
+ KURL url;
+ url.setPath(kapp->dirs()->localkdedir()+"/share/apps/noatun/equalizer");
+ save(url, "auto");
+
+ delete d;
+}
+
+int VEqualizer::start()
+{
+ return 20;
+}
+
+int VEqualizer::end()
+{
+ return 20000;
+}
+
+int VEqualizer::maxBands() const
+{
+ return 14;
+}
+
+int VEqualizer::minBands() const
+{
+ return 2;
+}
+
+
+QValueList<int> VEqualizer::frequencies(int _num)
+{
+#if 0
+ QValueList<int> fs;
+ fs += 108;
+ fs += 217;
+ fs += 810;
+ fs += 2431;
+ fs += 7290;
+ fs += 19999;
+ return fs;
+
+#else
+
+
+
+ // we're looking for
+ // ?^_num = end()-start()
+ // so log[?] (end()-start()) = _num
+ // log(end()-start()) / log(?) = _num
+ // log(end()-start()) = _num * log(?)
+ // log(end()-start()) / _num = log(?)
+ // ? = 10^(log(end()-start()) / _num)
+
+ double num = double(_num);
+ double vstart = double(start());
+ double vend = double(end());
+ const double base = ::pow(10.0, ::log10(vend-vstart)/num);
+
+ QValueList<int> fs;
+
+ for (double i=1.0; i <= num; i++)
+ {
+ double f = ::pow(base, i);
+ f += vstart;
+ fs.append(int(f));
+ }
+
+ return fs;
+#endif
+}
+
+void VEqualizer::setBands(int num, bool interpolate)
+{
+ if (interpolate)
+ {
+ setBands(num);
+ return;
+ }
+
+ if (num > maxBands())
+ num=maxBands();
+ else if (num < minBands()) num = minBands();
+
+ if (num == bands()) return;
+
+ QValueList<int> fs = VEqualizer::frequencies(num);
+ std::vector<Private::BandInfo> modified;
+
+ int bstart=0;
+ for (QValueList<int>::Iterator i(fs.begin()); i != fs.end(); ++i)
+ {
+ Private::BandInfo info;
+ info.start = bstart+1;
+ info.level = 0;
+ info.end = *i;
+ bstart = info.end;
+
+ modified.push_back(info);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+}
+
+void VEqualizer::setPreamp(int preamp)
+{
+ d->preamp = preamp;
+ EQBACK->preamp(pow(2,float(preamp)/100.0));
+ emit changed();
+ emit preampChanged();
+ emit preampChanged(preamp);
+}
+
+
+void VEqualizer::setBands(int num)
+{
+ if (num == bands()) return;
+ VInterpolation ip(num);
+
+ std::vector<Private::BandInfo> modified;
+
+ for (int i=0; i < num; ++i)
+ {
+ Private::BandInfo info;
+ VBand b = ip[i];
+ info.level = b.level();
+ info.start = b.start();
+ info.end = b.end();
+
+ modified.push_back(info);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+}
+
+VBand VEqualizer::band(int num)
+{
+ return VBand(this, num, d->bands[num].start, d->bands[num].end);
+}
+
+int VEqualizer::bands() const
+{
+ return d->bands.size();
+}
+
+bool VEqualizer::isEnabled() const
+{
+ return bool(EQBACK->enabled());
+}
+
+int VEqualizer::preamp() const
+{
+ return d->preamp;
+}
+
+void VEqualizer::enable()
+{
+ setEnabled(true);
+}
+
+void VEqualizer::disable()
+{
+ setEnabled(false);
+}
+
+int VEqualizer::level(int index) const
+{
+ return d->bands[index].level;
+}
+
+void VEqualizer::setLevel(int index, int level)
+{
+ d->bands[index].level = level;
+ update();
+ emit changed();
+ emit modified();
+}
+
+void VEqualizer::setLevels(const QValueList<int> &levels)
+{
+ int index=0;
+ for (
+ QValueList<int>::ConstIterator i(levels.begin());
+ i != levels.end(); ++i
+ )
+ {
+ d->bands[index].level = *i;
+ index++;
+ }
+ update();
+ emit changed();
+ emit modified();
+}
+
+
+void VEqualizer::setEnabled(bool e)
+{
+ update(true); // just in case
+ EQBACK->enabled((long)e);
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ config->writeEntry("enabled", e);
+ config->sync();
+
+ emit enabled(e);
+ if (e)
+ emit enabled();
+ else
+ emit disabled();
+}
+
+void VEqualizer::update(bool full)
+{
+ std::vector<float> levels;
+ std::vector<float> mids;
+ std::vector<float> widths;
+
+ for (unsigned int i=0; i < d->bands.size(); ++i)
+ {
+ Private::BandInfo &info = d->bands[i];
+ levels.push_back(::pow(2.0, float(info.level)/50.0));
+ if (full)
+ {
+ int mid = info.start + info.end;
+ mids.push_back(float(mid)*0.5);
+ widths.push_back(float(info.end - info.start));
+ }
+ }
+ if (full)
+ EQBACK->set(levels, mids, widths);
+ else
+ EQBACK->levels(levels);
+}
+
+
+
+bool VEqualizer::save(const KURL &filename, const QString &friendly) const
+{
+ Noatun::KSaver saver(filename);
+ if(!saver.open()) return false;
+
+ saver.textStream() << toString(friendly);
+ saver.close();
+
+ return saver.close();
+}
+
+bool VEqualizer::load(const KURL &filename)
+{
+ QString dest;
+ if(KIO::NetAccess::download(filename, dest, 0L))
+ {
+ QFile file(dest);
+ if (!file.open(IO_ReadOnly))
+ return false;
+
+ QTextStream t(&file);
+ QString str = t.read();
+ fromString(str);
+ return true;
+ }
+ return false;
+}
+
+QString VEqualizer::toString(const QString &name) const
+{
+ QDomDocument doc("noatunequalizer");
+ doc.setContent(QString("<!DOCTYPE NoatunEqualizer><noatunequalizer/>"));
+ QDomElement docElem = doc.documentElement();
+
+ {
+ docElem.setAttribute("level", preamp());
+ docElem.setAttribute("name", name);
+ docElem.setAttribute("version", napp->version());
+ }
+
+ int bandc = bands();
+ for (int i=0; i < bandc; ++i)
+ {
+ VBand band = const_cast<VEqualizer*>(this)->operator[](i);
+ QDomElement elem = doc.createElement("band");
+ elem.setAttribute("start", band.start());
+ elem.setAttribute("end", band.end());
+ elem.setAttribute("level", band.level());
+
+ docElem.appendChild(elem);
+ }
+
+ return doc.toString();
+}
+
+bool VEqualizer::fromString(const QString &str)
+{
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(str))
+ return false;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer")
+ return false;
+
+ setPreamp(docElem.attribute("level", "0").toInt());
+
+ std::vector<Private::BandInfo> modified;
+ for (QDomNode n = docElem.firstChild(); !n.isNull(); n = n.nextSibling())
+ {
+ QDomElement e = n.toElement();
+ if(e.isNull()) continue;
+ if (e.tagName().lower() != "band") continue;
+
+ Private::BandInfo data;
+
+ data.level = e.attribute("level", "0").toInt();
+ data.start = e.attribute("start", "1").toInt();
+ data.end = e.attribute("end", "19999").toInt();
+
+ modified.push_back(data);
+ }
+ d->bands = modified;
+ update(true);
+
+ emit changedBands();
+ emit changed();
+ return true;
+}
+
+static QString makePresetFile()
+{
+ QString basedir=kapp->dirs()->localkdedir()+"/share/apps/noatun/eq.preset/";
+ // now append a filename that doesn't exist
+ KStandardDirs::makeDir(basedir);
+ QString fullpath;
+ int num=0;
+ do
+ {
+ if (num)
+ fullpath=basedir+"preset."+QString::number(num);
+ else
+ fullpath=basedir+"preset";
+ num++;
+ }
+ while (QFile(fullpath).exists());
+ return fullpath;
+}
+
+VPreset VEqualizer::createPreset(const QString &name, bool smart)
+{
+ if (presetExists(name) && !smart) return VPreset();
+ QString nameReal=name;
+ {
+ int number=1;
+ while (presetExists(nameReal))
+ {
+ nameReal=name+" ("+QString::number(number)+')';
+ number++;
+ }
+ }
+
+ VPreset preset(makePresetFile());
+ preset.setName(nameReal);
+
+ save(preset.file(), nameReal);
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ QStringList list = config->readListEntry("presets");
+ list += preset.file();
+ config->writeEntry("presets", list);
+ config->sync();
+
+ emit created(preset);
+ return preset;
+}
+
+
+QValueList<VPreset> VEqualizer::presets() const
+{
+ KConfig *conf=KGlobal::config();
+ conf->setGroup("Equalizer");
+
+ QStringList list;
+ if (conf->hasKey("presets"))
+ {
+ list=conf->readListEntry("presets");
+ }
+ else
+ {
+ list=kapp->dirs()->findAllResources("data", "noatun/eq.preset/*");
+ conf->writeEntry("presets", list);
+ conf->sync();
+ }
+
+ QValueList<VPreset> presets;
+
+ for (QStringList::Iterator i = list.begin(); i!=list.end(); ++i)
+ {
+ QFile file(*i);
+ if (!file.open(IO_ReadOnly)) continue;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) continue;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") continue;
+
+ presets.append(VPreset(*i));
+ }
+ return presets;
+}
+
+VPreset VEqualizer::presetByName(const QString &name)
+{
+ QValueList<VPreset> pr = presets();
+ for (
+ QValueList<VPreset>::Iterator i(pr.begin());
+ i != pr.end(); ++i
+ )
+ {
+ if ((*i).name() == name)
+ return *i;
+ }
+ return VPreset();
+}
+
+VPreset VEqualizer::presetByFile(const QString &file)
+{
+ KConfig *conf=KGlobal::config();
+ conf->setGroup("Equalizer");
+ QStringList list=kapp->config()->readListEntry("presets");
+ if (list.contains(file))
+ return VPreset(file);
+ return VPreset();
+}
+
+bool VEqualizer::presetExists(const QString &name) const
+{
+ QValueList<VPreset> list=presets();
+ for (
+ QValueList<VPreset>::Iterator i(list.begin());
+ i != list.end(); ++i
+ )
+ {
+ if ((*i).name() == name)
+ return true;
+ }
+ return false;
+}
+
+
+
+struct VPreset::Private
+{
+ QString file;
+};
+
+
+
+VPreset::VPreset(const QString &file)
+{
+ d = new Private;
+ d->file = file;
+
+}
+
+
+VPreset::VPreset()
+{
+ d = new Private;
+}
+
+VPreset::VPreset(const VPreset &copy)
+{
+ d = new Private;
+ operator =(copy);
+}
+
+VPreset::~VPreset()
+{
+ delete d;
+}
+
+bool VPreset::operator ==(const VPreset &other) const
+{
+ return name() == other.name();
+}
+
+VPreset & VPreset::operator=(const VPreset &copy)
+{
+ d->file = copy.file();
+ return *this;
+}
+
+QString VPreset::name() const
+{
+ QFile file(d->file);
+ if (!file.open(IO_ReadOnly)) return 0;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) return 0;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") return 0;
+ bool standard=docElem.attribute("default", "0")=="0";
+ QString n=docElem.attribute("name", 0);
+
+ { // All the translations for the presets
+# ifdef I18N_STUFF
+ I18N_NOOP("Trance");
+ I18N_NOOP("Dance");
+ I18N_NOOP("Metal");
+ I18N_NOOP("Jazz");
+ I18N_NOOP("Zero");
+ I18N_NOOP("Eclectic Guitar");
+# endif
+ }
+
+ if (standard)
+ n=i18n(n.local8Bit());
+
+ return n;
+}
+
+bool VPreset::setName(const QString &name)
+{
+ QFile file(d->file);
+ if (!file.open(IO_ReadOnly)) return false;
+
+ QDomDocument doc("noatunequalizer");
+ if (!doc.setContent(&file)) return false;
+
+ QDomElement docElem = doc.documentElement();
+ if (docElem.tagName()!="noatunequalizer") return false;
+
+ if (docElem.attribute("name") == name) return true;
+ if (EQ->presetByName(name)) return false;
+
+ docElem.setAttribute("name", name);
+ file.close();
+
+ if (!file.open(IO_ReadWrite | IO_Truncate)) return false;
+
+ QTextStream s(&file);
+ s << doc.toString();
+ file.close();
+
+ emit EQ->renamed(*this);
+
+ return true;
+}
+
+bool VPreset::isValid() const
+{
+ return d->file.length();
+}
+
+void VPreset::save()
+{
+ KURL url;
+ url.setPath(d->file);
+ EQ->load(url);
+}
+
+void VPreset::load() const
+{
+ KURL url;
+ url.setPath(d->file);
+ EQ->load(url);
+}
+
+QString VPreset::file() const
+{
+ return d->file;
+}
+
+void VPreset::remove()
+{
+ KConfig *config=kapp->config();
+ config->setGroup("Equalizer");
+ QStringList items=config->readListEntry("presets");
+ items.remove(file());
+ config->writeEntry("presets", items);
+ config->sync();
+
+ emit EQ->removed(*this);
+
+ if (file().find(kapp->dirs()->localkdedir())==0)
+ {
+ QFile f(file());
+ f.remove();
+ }
+ d->file = "";
+}
+
+
+#undef EQ
+#undef EQBACK
+
+#include "vequalizer.moc"
+
diff --git a/noatun/library/video.cpp b/noatun/library/video.cpp
new file mode 100644
index 00000000..c259d4ba
--- /dev/null
+++ b/noatun/library/video.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+ Copyright (C) 2002 Charles Samuels
+ this file is on the BSD license, sans advertisement clause
+ *****************************************************************************/
+
+#include <noatun/video.h>
+#include <noatun/app.h>
+#include <noatun/player.h>
+#include <noatun/engine.h>
+
+#include <qpopupmenu.h>
+#include <kaction.h>
+#include <klocale.h>
+
+#include "globalvideo.h"
+
+// sorry :)
+QPtrList<VideoFrame> VideoFrame::frames;
+
+VideoFrame *VideoFrame::whose=0;
+
+struct VideoFrame::Private
+{
+};
+
+
+VideoFrame::VideoFrame(KXMLGUIClient *clientParent, QWidget *parent, const char*name, WFlags f)
+ : KVideoWidget(clientParent, parent, name, f)
+{
+ d = new Private;
+ connect(napp->player(), SIGNAL(newSong()), SLOT(changed()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(stopped()));
+ frames.append(this);
+}
+
+VideoFrame::VideoFrame(QWidget *parent, const char *name, WFlags f)
+ : KVideoWidget(parent, name, f)
+{
+ d = new Private;
+ connect(napp->player(), SIGNAL(newSong()), SLOT(changed()));
+ connect(napp->player(), SIGNAL(stopped()), SLOT(stopped()));
+ frames.append(this);
+}
+
+VideoFrame::~VideoFrame()
+{
+ if (whose == this)
+ {
+ embed(Arts::VideoPlayObject::null());
+ whose=0;
+ }
+
+ frames.removeRef(this);
+ VideoFrame *l = frames.last();
+ if (l) l->give();
+ else whose=0;
+ delete d;
+}
+
+VideoFrame *VideoFrame::playing()
+{
+ return whose;
+}
+
+QPopupMenu *VideoFrame::popupMenu(QWidget *parent)
+{
+ QPopupMenu *view = new QPopupMenu(parent);
+ action( "half_size" )->plug( view );
+ action( "normal_size" )->plug( view );
+ action( "double_size" )->plug( view );
+ view->insertSeparator();
+ action( "fullscreen_mode" )->plug( view );
+ return view;
+}
+
+void VideoFrame::give()
+{
+ VideoFrame *old=whose;
+ whose = this;
+
+ if (whose != old && old != 0)
+ {
+ old->embed(Arts::VideoPlayObject::null());
+ emit old->lost();
+ }
+
+ Arts::PlayObject po = napp->player()->engine()->playObject();
+ if (po.isNull()) return;
+
+ Arts::VideoPlayObject vpo = Arts::DynamicCast(po);
+ if (!vpo.isNull())
+ {
+ embed(vpo);
+ emit acquired();
+ }
+}
+
+void VideoFrame::changed()
+{
+ if (whose==this)
+ give();
+}
+
+void VideoFrame::stopped()
+{
+ if (whose==this)
+ {
+ embed(Arts::VideoPlayObject::null());
+ emit lost();
+ }
+}
+
+#include <qlayout.h>
+
+
+GlobalVideo::GlobalVideo()
+ : QWidget( 0, 0, WType_TopLevel | WStyle_Customize | WStyle_DialogBorder | WStyle_Title )
+{
+ setCaption(i18n("Video - Noatun"));
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ video = new VideoFrame(this);
+ menu = video->popupMenu(this);
+
+ // FIXME: How to obtain minimum size for top-level widgets?
+// video->setMinimumSize(minimumSizeHint());
+// video->setMinimumSize(101,35);
+ video->setMinimumSize(128,96);
+
+ connect(video, SIGNAL(acquired()), SLOT(appear()));
+ connect(video, SIGNAL(lost()), SLOT(hide()));
+ connect(video, SIGNAL(adaptSize(int,int)), this, SLOT(slotAdaptSize(int,int)));
+
+ video->setNormalSize();
+ video->give();
+}
+
+void GlobalVideo::slotAdaptSize(int w, int h)
+{
+ resize(w, h);
+}
+
+void GlobalVideo::appear()
+{
+ QWidget::show();
+}
+
+void GlobalVideo::hide()
+{
+ QWidget::hide();
+}
+
+void GlobalVideo::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ {
+ menu->exec(mapToGlobal(e->pos()));
+ }
+}
+
+#include "globalvideo.moc"
+#include "video.moc"
+