summaryrefslogtreecommitdiffstats
path: root/noatun-plugins/nexscope
diff options
context:
space:
mode:
Diffstat (limited to 'noatun-plugins/nexscope')
-rw-r--r--noatun-plugins/nexscope/Makefile.am22
-rw-r--r--noatun-plugins/nexscope/bitmappool.cpp54
-rw-r--r--noatun-plugins/nexscope/convolve.c297
-rw-r--r--noatun-plugins/nexscope/convolve.h61
-rw-r--r--noatun-plugins/nexscope/gui.cpp197
-rw-r--r--noatun-plugins/nexscope/gui.h54
-rw-r--r--noatun-plugins/nexscope/input.cpp238
-rw-r--r--noatun-plugins/nexscope/nex.cpp597
-rw-r--r--noatun-plugins/nexscope/nex.h352
-rw-r--r--noatun-plugins/nexscope/nexscope.plugin72
-rw-r--r--noatun-plugins/nexscope/noatunplugin.cpp49
-rw-r--r--noatun-plugins/nexscope/noatunplugin.h25
-rw-r--r--noatun-plugins/nexscope/output.cpp48
-rw-r--r--noatun-plugins/nexscope/regionwidget.cpp77
-rw-r--r--noatun-plugins/nexscope/regionwidget.h47
-rw-r--r--noatun-plugins/nexscope/renderers.cpp369
-rw-r--r--noatun-plugins/nexscope/renderers.h34
17 files changed, 2593 insertions, 0 deletions
diff --git a/noatun-plugins/nexscope/Makefile.am b/noatun-plugins/nexscope/Makefile.am
new file mode 100644
index 0000000..d182ff1
--- /dev/null
+++ b/noatun-plugins/nexscope/Makefile.am
@@ -0,0 +1,22 @@
+INCLUDES= $(all_includes) $(SDL_CFLAGS)
+
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = noatunnexscope.la
+
+noatunnexscope_la_SOURCES = noatunplugin.cpp
+noatunnexscope_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined
+noatunnexscope_la_LIBADD = $(LIB_KFILE) -lnoatun
+
+bin_PROGRAMS = nexscope.bin
+
+#renderers.cpp must be first for enable-final
+nexscope_bin_SOURCES = renderers.cpp bitmappool.cpp nex.cpp output.cpp input.cpp gui.cpp regionwidget.cpp convolve.c
+nexscope_bin_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(SDL_LIBS)
+nexscope_bin_LDADD = $(LIB_KDEUI) -lm -lnoatun $(SDL_LIBS)
+
+noatun_DATA = nexscope.plugin
+noatundir = $(kde_datadir)/noatun
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/nexscope.pot
diff --git a/noatun-plugins/nexscope/bitmappool.cpp b/noatun-plugins/nexscope/bitmappool.cpp
new file mode 100644
index 0000000..0a0ad78
--- /dev/null
+++ b/noatun-plugins/nexscope/bitmappool.cpp
@@ -0,0 +1,54 @@
+#include "nex.h"
+
+struct BitmapPool::PoolItem
+{
+ PoolItem() : used(false) {}
+
+ bool used;
+ Bitmap bitmap;
+};
+
+BitmapPool::BitmapPool()
+{
+
+}
+
+BitmapPool::~BitmapPool()
+{
+
+}
+
+Bitmap *BitmapPool::get(bool clear)
+{
+ mMutex.lock();
+ BitmapPool::PoolItem *p=0;
+ for (QPtrListIterator<BitmapPool::PoolItem> i(mBitmaps); i.current(); ++i)
+ {
+ if (!(*i)->used)
+ p=*i;
+ }
+ if (!p)
+ {
+ p=new BitmapPool::PoolItem;
+ p->bitmap.resize(width, height);
+ }
+
+ p->used=true;
+
+ if (clear) p->bitmap.clear();
+
+ mMutex.unlock();
+ return &(p->bitmap);
+}
+
+void BitmapPool::release(Bitmap *bitmap)
+{
+ mMutex.lock();
+ for (QPtrListIterator<BitmapPool::PoolItem> i(mBitmaps); i.current(); ++i)
+ {
+ if (&((*i)->bitmap)==bitmap)
+ (*i)->used=false;
+ }
+ mMutex.unlock();
+}
+
diff --git a/noatun-plugins/nexscope/convolve.c b/noatun-plugins/nexscope/convolve.c
new file mode 100644
index 0000000..03509eb
--- /dev/null
+++ b/noatun-plugins/nexscope/convolve.c
@@ -0,0 +1,297 @@
+/* Karatsuba convolution
+ *
+ * Copyright (C) 1999 Ralph Loader <suckfish@ihug.co.nz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* The algorithm is based on the following. For the convolution of a pair
+ * of pairs, (a,b) * (c,d) = (0, a.c, a.d+b.c, b.d), we can reduce the four
+ * multiplications to three, by the formulae a.d+b.c = (a+b).(c+d) - a.c -
+ * b.d. A similar relation enables us to compute a 2n by 2n convolution
+ * using 3 n by n convolutions, and thus a 2^n by 2^n convolution using 3^n
+ * multiplications (as opposed to the 4^n that the quadratic algorithm
+ * takes. */
+
+/* For large n, this is slower than the O(n log n) that the FFT method
+ * takes, but we avoid using complex numbers, and we only have to compute
+ * one convolution, as opposed to 3 FFTs. We have good locality-of-
+ * reference as well, which will help on CPUs with tiny caches. */
+
+/* E.g., for a 512 x 512 convolution, the FFT method takes 55 * 512 = 28160
+ * (real) multiplications, as opposed to 3^9 = 19683 for the Karatsuba
+ * algorithm. We actually want 257 outputs of a 256 x 512 convolution;
+ * that doesn't appear to give an easy advantage for the FFT algorithm, but
+ * for the Karatsuba algorithm, it's easy to use two 256 x 256
+ * convolutions, taking 2 x 3^8 = 12312 multiplications. [This difference
+ * is that the FFT method "wraps" the arrays, doing a 2^n x 2^n -> 2^n,
+ * while the Karatsuba algorithm pads with zeros, doing 2^n x 2^n -> 2.2^n
+ * - 1]. */
+
+/* There's a big lie above, actually... for a 4x4 convolution, it's quicker
+ * to do it using 16 multiplications than the more complex Karatsuba
+ * algorithm... So the recursion bottoms out at 4x4s. This increases the
+ * number of multiplications by a factor of 16/9, but reduces the overheads
+ * dramatically. */
+
+/* The convolution algorithm is implemented as a stack machine. We have a
+ * stack of commands, each in one of the forms "do a 2^n x 2^n
+ * convolution", or "combine these three length 2^n outputs into one
+ * 2^{n+1} output." */
+
+#include <stdlib.h>
+#include "convolve.h"
+
+/*
+ * Initialisation routine - sets up tables and space to work in.
+ * Returns a pointer to internal state, to be used when performing calls.
+ * On error, returns NULL.
+ * The pointer should be freed when it is finished with, by convolve_close().
+ */
+convolve_state *convolve_init(void)
+{
+ return (convolve_state *) malloc (sizeof(convolve_state));
+}
+
+/*
+ * Free the state allocated with convolve_init().
+ */
+void convolve_close(convolve_state *state)
+{
+ if (state)
+ free(state);
+}
+
+static void convolve_4 (double * out, const double * left, const double * right)
+/* This does a 4x4 -> 7 convolution. For what it's worth, the slightly odd
+ * ordering gives about a 1% speed up on my Pentium II. */
+{
+ double l0, l1, l2, l3, r0, r1, r2, r3;
+ double a;
+ l0 = left[0];
+ r0 = right[0];
+ a = l0 * r0;
+ l1 = left[1];
+ r1 = right[1];
+ out[0] = a;
+ a = (l0 * r1) + (l1 * r0);
+ l2 = left[2];
+ r2 = right[2];
+ out[1] = a;
+ a = (l0 * r2) + (l1 * r1) + (l2 * r0);
+ l3 = left[3];
+ r3 = right[3];
+ out[2] = a;
+
+ out[3] = (l0 * r3) + (l1 * r2) + (l2 * r1) + (l3 * r0);
+ out[4] = (l1 * r3) + (l2 * r2) + (l3 * r1);
+ out[5] = (l2 * r3) + (l3 * r2);
+ out[6] = l3 * r3;
+}
+
+static void convolve_run (stack_entry * top, unsigned size, double * scratch)
+/* Interpret a stack of commands. The stack starts with two entries; the
+ * convolution to do, and an illegal entry used to mark the stack top. The
+ * size is the number of entries in each input, and must be a power of 2,
+ * and at least 8. It is OK to have out equal to left and/or right.
+ * scratch must have length 3*size. The number of stack entries needed is
+ * 3n-4 where size=2^n. */
+{
+ do {
+ const double * left;
+ const double * right;
+ double * out;
+
+ /* When we get here, the stack top is always a convolve,
+ * with size > 4. So we will split it. We repeatedly split
+ * the top entry until we get to size = 4. */
+
+ left = top->v.left;
+ right = top->v.right;
+ out = top->v.out;
+ top++;
+
+ do {
+ double * s_left, * s_right;
+ int i;
+
+ /* Halve the size. */
+ size >>= 1;
+
+ /* Allocate the scratch areas. */
+ s_left = scratch + size * 3;
+ /* s_right is a length 2*size buffer also used for
+ * intermediate output. */
+ s_right = scratch + size * 4;
+
+ /* Create the intermediate factors. */
+ for (i = 0; i < size; i++) {
+ double l = left[i] + left[i + size];
+ double r = right[i] + right[i + size];
+ s_left[i + size] = r;
+ s_left[i] = l;
+ }
+
+ /* Push the combine entry onto the stack. */
+ top -= 3;
+ top[2].b.main = out;
+ top[2].b.null = NULL;
+
+ /* Push the low entry onto the stack. This must be
+ * the last of the three sub-convolutions, because
+ * it may overwrite the arguments. */
+ top[1].v.left = left;
+ top[1].v.right = right;
+ top[1].v.out = out;
+
+ /* Push the mid entry onto the stack. */
+ top[0].v.left = s_left;
+ top[0].v.right = s_right;
+ top[0].v.out = s_right;
+
+ /* Leave the high entry in variables. */
+ left += size;
+ right += size;
+ out += size * 2;
+
+ } while (size > 4);
+
+ /* When we get here, the stack top is a group of 3
+ * convolves, with size = 4, followed by some combines. */
+ convolve_4 (out, left, right);
+ convolve_4 (top[0].v.out, top[0].v.left, top[0].v.right);
+ convolve_4 (top[1].v.out, top[1].v.left, top[1].v.right);
+ top += 2;
+
+ /* Now process combines. */
+ do {
+ /* b.main is the output buffer, mid is the middle
+ * part which needs to be adjusted in place, and
+ * then folded back into the output. We do this in
+ * a slightly strange way, so as to avoid having
+ * two loops. */
+ double * out = top->b.main;
+ double * mid = scratch + size * 4;
+ unsigned int i;
+ top++;
+ out[size * 2 - 1] = 0;
+ for (i = 0; i < size-1; i++) {
+ double lo;
+ double hi;
+ lo = mid[0] - (out[0] + out[2 * size]) + out[size];
+ hi = mid[size] - (out[size] + out[3 * size]) + out[2 * size];
+ out[size] = lo;
+ out[2 * size] = hi;
+ out++;
+ mid++;
+ }
+ size <<= 1;
+ } while (top->b.null == NULL);
+ } while (top->b.main != NULL);
+}
+
+int convolve_match (float * lastchoice,
+ float * input,
+ convolve_state * state)
+/* lastchoice is a 256 sized array. input is a 512 array. We find the
+ * contiguous length 256 sub-array of input that best matches lastchoice.
+ * A measure of how good a sub-array is compared with the lastchoice is
+ * given by the sum of the products of each pair of entries. We maximise
+ * that, by taking an appropriate convolution, and then finding the maximum
+ * entry in the convolutions. state is a (non-NULL) pointer returned by
+ * convolve_init. */
+{
+ double avg;
+ double best;
+ int p;
+ int i;
+ double * left = state->left;
+ double * right = state->right;
+ double * scratch = state->scratch;
+ stack_entry * top = state->stack + STACK_SIZE - 1;
+
+ for (i=0; i<512; i++)
+ left[i]=input[i];
+
+ avg = 0;
+ for (i = 0; i < 256; i++)
+ {
+ double a = lastchoice[255 - i];
+ right[i] = a;
+ avg += a;
+ }
+
+ /* We adjust the smaller of the two input arrays to have average
+ * value 0. This makes the eventual result insensitive to both
+ * constant offsets and positive multipliers of the inputs. */
+ avg /= 256;
+ for (i = 0; i < 256; i++)
+ right[i] -= avg;
+
+ /* End-of-stack marker. */
+ top[1].b.null = scratch;
+ top[1].b.main = NULL;
+
+ /* The low 256x256, of which we want the high 256 outputs. */
+ top->v.left = left;
+ top->v.right = right;
+ top->v.out = right + 256;
+ convolve_run (top, 256, scratch);
+
+ /* The high 256x256, of which we want the low 256 outputs. */
+ top->v.left = left + 256;
+ top->v.right = right;
+ top->v.out = right;
+ convolve_run (top, 256, scratch);
+
+ /* Now find the best position amoungs this. Apart from the first
+ * and last, the required convolution outputs are formed by adding
+ * outputs from the two convolutions above. */
+ best = right[511];
+ right[767] = 0;
+ p = -1;
+ for (i = 0; i < 256; i++) {
+ double a = right[i] + right[i + 512];
+ if (a > best) {
+ best = a;
+ p = i;
+ }
+ }
+ p++;
+
+#if 0
+ {
+ /* This is some debugging code... */
+ int bad = 0;
+ best = 0;
+ for (i = 0; i < 256; i++)
+ best += ((double) input[i+p]) * ((double) lastchoice[i] - avg);
+
+ for (i = 0; i < 257; i++) {
+ double tot = 0;
+ unsigned int j;
+ for (j = 0; j < 256; j++)
+ tot += ((double) input[i+j]) * ((double) lastchoice[j] - avg);
+ if (tot > best)
+ printf ("(%i)", i);
+ if (tot != left[i + 255])
+ printf ("!");
+ }
+
+ printf ("%i\n", p);
+ }
+#endif
+
+ return p;
+}
diff --git a/noatun-plugins/nexscope/convolve.h b/noatun-plugins/nexscope/convolve.h
new file mode 100644
index 0000000..4940c62
--- /dev/null
+++ b/noatun-plugins/nexscope/convolve.h
@@ -0,0 +1,61 @@
+/* convolve.h: Header for convolutions.
+ *
+ * Copyright (C) 1999 Ralph Loader <suckfish@ihug.co.nz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef CONVOLVE_H
+#define CONVOLVE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* convolve_match takes two blocks, one twice the size of the other. The
+ * sizes of these are CONVOLVE_BIG and CONVOLVE_SMALL respectively. */
+#define CONVOLVE_DEPTH 8
+#define CONVOLVE_SMALL (1 << CONVOLVE_DEPTH)
+#define CONVOLVE_BIG (CONVOLVE_SMALL * 2)
+
+typedef union stack_entry_s
+{
+ struct {const double * left, * right; double * out;} v;
+ struct {double * main, * null;} b;
+
+} stack_entry;
+
+#define STACK_SIZE (CONVOLVE_DEPTH * 3)
+
+typedef struct convolve_state {
+ double left [CONVOLVE_BIG];
+ double right [CONVOLVE_SMALL * 3];
+ double scratch [CONVOLVE_SMALL * 3];
+ stack_entry stack[STACK_SIZE];
+} convolve_state;
+
+
+convolve_state *convolve_init (void);
+void convolve_close (convolve_state * state);
+
+int convolve_match (float * lastchoice,
+ float * input,
+ convolve_state * state);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/noatun-plugins/nexscope/gui.cpp b/noatun-plugins/nexscope/gui.cpp
new file mode 100644
index 0000000..912d8ea
--- /dev/null
+++ b/noatun-plugins/nexscope/gui.cpp
@@ -0,0 +1,197 @@
+#include <klocale.h>
+#include <qheader.h>
+#include <qlayout.h>
+#include <qdragobject.h>
+#include <kfiledialog.h>
+#include <kstdaction.h>
+#include <kaction.h>
+
+#include "gui.h"
+#include "renderers.h"
+#include "nex.h"
+
+class CreatorItem : public QListViewItem
+{
+public:
+ CreatorItem(QListView *parent, const QString &title)
+ : QListViewItem(parent, title)
+ {}
+};
+
+class TreeItem : public QListViewItem
+{
+public:
+ TreeItem(TreeItem *parent, TreeItem *after, CreatorItem *creator)
+ : QListViewItem(parent, after, creator->text(0))
+ {
+ RendererList *list;
+ if (parent)
+ list=static_cast<RendererList*>(parent->mRenderer);
+ else
+ list=nex->rendererList();
+
+ Renderer *afterRenderer=0;
+ if (after)
+ afterRenderer=after->mRenderer;
+
+ list->lock();
+ int pos=list->renderers().findRef(afterRenderer);
+ if (pos==-1) pos=list->renderers().count();
+
+ list->renderers().insert((uint)pos, mRenderer=nex->renderer(creator->text(0)));
+ list->unlock();
+ }
+
+ TreeItem(QListView *parent, TreeItem *after, const QString &title)
+ : QListViewItem(parent, after, title)
+ {
+ mRenderer=nex->rendererList();
+ setExpandable(true);
+ }
+
+ ~TreeItem()
+ {
+ RendererList *list;
+ if (parent())
+ list=static_cast<RendererList*>(static_cast<TreeItem*>(parent())->mRenderer);
+ else
+ return;
+
+ list->lock();
+ list->renderers().removeRef(mRenderer);
+ list->unlock();
+ delete mRenderer;
+ }
+
+ Renderer *renderer() { return mRenderer; }
+private:
+ Renderer *mRenderer;
+};
+
+Control::Control() : mConfigurator(0)
+{
+ {
+ KToolBar *tools=toolBar();
+ KStdAction::save(this, SLOT(save()), actionCollection())->plug(tools);
+ KStdAction::saveAs(this, SLOT(saveAs()), actionCollection())->plug(tools);
+ KStdAction::open(this, SLOT(open()), actionCollection())->plug(tools);
+
+ }
+
+ setCaption(i18n("Nex Configuration"));
+ QSplitter *mainSplitter=new QSplitter(this);
+ setCentralWidget(mainSplitter);
+ QSplitter *left=new QSplitter(Qt::Vertical, mainSplitter);
+ {
+ mTree=new RendererListView(left);
+ mTree->setItemsMovable(true);
+ mTree->setSorting(-1);
+ mTree->setRootIsDecorated(true);
+
+ connect(mTree, SIGNAL(dropped(QDropEvent*, QListViewItem*, QListViewItem*)),
+ SLOT(dropEvent(QDropEvent*, QListViewItem*, QListViewItem*)));
+
+ connect(mTree, SIGNAL(currentChanged(QListViewItem*)),
+ SLOT(currentChanged(QListViewItem*)));
+
+ mCreatorsList=new RendererListView(left);
+ mCreatorsList->addColumn(i18n("Name"));
+ mCreatorsList->header()->hide();
+ mCreatorsList->setDropVisualizer(false);
+ }
+
+ mRight=new QWidget(mainSplitter);
+ (new QHBoxLayout(mRight))->setAutoAdd(true);
+
+ QStringList list=nex->renderers();
+ for (QStringList::Iterator i=list.begin(); i!=list.end(); ++i)
+ addCreator(*i);
+
+ new TreeItem(mTree, 0, i18n("Main"));
+}
+
+void Control::save(const KURL &file)
+{
+// TODO
+}
+
+void Control::save()
+{
+ if (mCurrentURL.isEmpty())
+ saveAs();
+ else
+ save(mCurrentURL);
+}
+
+void Control::saveAs()
+{
+ KURL url;
+ url=KFileDialog::getSaveURL(0, "application/x-nexscope", this);
+ if (!url.isEmpty())
+ save(mCurrentURL=url);
+}
+
+void Control::open()
+{
+ KURL url=KFileDialog::getOpenURL(0, "application/x-nexscope");
+ if (!url.isEmpty())
+ open(mCurrentURL=url);
+}
+
+void Control::open(const KURL &file)
+{
+// TODO
+}
+
+
+void Control::addCreator(const QString &title)
+{
+ new CreatorItem(mCreatorsList, title);
+}
+
+void Control::dropEvent(QDropEvent *e, QListViewItem *parent, QListViewItem *pafter)
+{
+// if ((e->source() == mCreatorsList) && parent)
+ {
+ CreatorItem *i=static_cast<CreatorItem*>(mCreatorsList->currentItem());
+ if (!i)
+ {
+ std::cerr << "no creatoritem" << std::endl;
+
+ }
+ new TreeItem(static_cast<TreeItem*>(parent), static_cast<TreeItem*>(pafter), i);
+ }
+}
+
+void Control::currentChanged(QListViewItem *item)
+{
+ TreeItem *treeItem=static_cast<TreeItem*>(item);
+ delete mConfigurator;
+ mConfigurator=treeItem->renderer()->configure(mRight);
+ if (mConfigurator)
+ mConfigurator->show();
+}
+
+
+RendererListView::RendererListView(QWidget *p) : KListView(p)
+{
+ addColumn(i18n("Name"));
+ header()->hide();
+ setDragEnabled(true);
+ setAcceptDrops(true);
+ setSelectionMode(QListView::Single);
+}
+
+bool RendererListView::acceptDrag(QDropEvent *event) const
+{
+ return true; //QCString(event->format()) == "application/x-nex-rendererdrag";
+}
+
+QDragObject *RendererListView::dragObject() const
+{
+ if (!currentItem()) return 0;
+ return new QStoredDrag("application/x-nex-rendererdrag", (QWidget*)this);
+}
+
+
+#include "gui.moc"
diff --git a/noatun-plugins/nexscope/gui.h b/noatun-plugins/nexscope/gui.h
new file mode 100644
index 0000000..84fd2bf
--- /dev/null
+++ b/noatun-plugins/nexscope/gui.h
@@ -0,0 +1,54 @@
+#ifndef GUI_H
+#define GUI_H
+
+#include <kmainwindow.h>
+#include <klistview.h>
+#include <qsplitter.h>
+#include <qdict.h>
+#include <dcopobject.h>
+#include <kurl.h>
+
+class Renderer;
+
+class Control : public KMainWindow
+{
+Q_OBJECT
+
+public:
+ Control();
+
+ void addCreator(const QString &title);
+
+public slots:
+ void save(const KURL &file);
+ void save();
+ void saveAs();
+
+ void open();
+ void open(const KURL &file);
+
+protected slots:
+ void dropEvent(QDropEvent *e, QListViewItem *parent, QListViewItem *pafter);
+ void currentChanged(QListViewItem *item);
+
+private:
+ QWidget *mRight;
+ KListView *mTree, *mCreatorsList;
+ QWidget *mConfigurator;
+
+ KURL mCurrentURL;
+};
+
+class RendererListView : public KListView
+{
+Q_OBJECT
+public:
+ RendererListView(QWidget *p);
+
+protected:
+ virtual bool acceptDrag(QDropEvent *event) const;
+ virtual QDragObject *dragObject() const;
+};
+
+
+#endif
diff --git a/noatun-plugins/nexscope/input.cpp b/noatun-plugins/nexscope/input.cpp
new file mode 100644
index 0000000..4c787e7
--- /dev/null
+++ b/noatun-plugins/nexscope/input.cpp
@@ -0,0 +1,238 @@
+
+#include "nex.h"
+#include "convolve.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <noatun/plugin.h>
+
+Input::Input()
+{
+ memset(haystack, 0, sizeof(float)*512);
+ state=new convolve_state;
+
+ float d, *costab, *sintab;
+ long ul, ndiv2 = 512 / 2;
+
+ for (costab = fhtTab, sintab = fhtTab + 512 / 2 + 1, ul = 0; ul < 512; ul++)
+ {
+ d = PI * ul / ndiv2;
+ *costab = *sintab = ::cos(d);
+
+ costab += 2, sintab += 2;
+ if (sintab > fhtTab + 512 * 2)
+ sintab = fhtTab + 1;
+ }
+
+ notifier=0;
+ mScope=0;
+
+ connect();
+}
+
+Input::~Input()
+{
+ delete mScope;
+ std::cerr<< "Scope gone"<< std::endl;
+ delete notifier;
+// delete [] audio[2];
+// delete [] audio[3];
+// delete [] audio[4];
+// delete [] audio[5];
+}
+
+void Input::setConvolve(bool on)
+{
+ mConvolve=on;
+}
+
+void Input::connect()
+{
+ do
+ {
+ delete notifier;
+ delete mScope;
+ ok=true;
+ mScope=new StereoScope(10, Visualization::noatunPid());
+ mScope->setSamples(samples>(512+256) ? samples : samples+256);
+
+ notifier=new QObject;
+ new BoolNotifier(&ok, new ExitNotifier(Visualization::noatunPid(), notifier), notifier);
+ } while (!mScope->connected());
+}
+
+// [0] pcm left
+// [1] pcm right
+// [2] pcm center
+// [3] FFT (FHT) left
+// [4] FFT right
+// [5] FFT center
+
+void Input::getAudio(float **audio)
+{
+ static bool first=true;
+ if (first)
+ {
+ audio[2]=new float[samples];
+ audio[3]=new float[fhtsamples];
+ audio[4]=new float[fhtsamples];
+ audio[5]=new float[fhtsamples];
+ first=false;
+ }
+
+ if (!ok || !mScope->connected())
+ {
+ std::cerr << "reconnect" <<std::endl;
+ connect();
+ return;
+// delete mScope;
+// ::sleep(7);
+// mScope=new StereoScope(10, Visualization::noatunPid());
+// mScope->setSamples(samples);
+ }
+ std::vector<float> *left, *right;
+ mScope->scopeData(left, right);
+
+ register float *inleft=&*left->begin();
+ register float *inright=&*right->begin();
+
+
+ int offset=0;
+ if (mConvolve)
+ { // find the offset
+ for (register int i=0; i<512+256; ++i)
+ temp[i]=inleft[i]+inright[i];
+ offset=::convolve_match(haystack, temp, state);
+ if (offset==-1) offset=0;
+ inleft+=offset;
+ inright+=offset;
+
+ for (register int i=0; i<512; ++i)
+ {
+ haystack[i]*=.5;
+ haystack[i]+=temp[i+offset];
+ }
+ }
+
+ memcpy(outleft, inleft, samples*sizeof(float));
+ memcpy(outright, inright, samples*sizeof(float));
+
+ audio[0]=outleft;
+ audio[1]=outright;
+
+ // center channel
+ for (int i=0; i<samples; ++i)
+ audio[2][i]=(audio[0][i]+audio[1][i])*.5;
+
+ // perform the FFT (FHT in this case)
+ memcpy(audio[3], audio[0], fhtsamples);
+ memcpy(audio[4], audio[1], fhtsamples);
+
+ fht(audio[3]);
+ fht(audio[4]);
+
+ for (int i=0; i<fhtsamples; ++i)
+ {
+ audio[5][i]=(audio[3][i]+audio[4][i])*.5;
+ }
+
+
+ delete left;
+ delete right;
+}
+
+void Input::fht(float *p)
+{
+ long i;
+ float *q;
+ transform(p, fhtsamples, 0);
+
+ *p = (*p * *p), *p += *p, p++;
+
+ for (i = 1, q = p + fhtsamples - 2; i < (fhtsamples / 2); i++, --q)
+ *p++ = (*p * *p) + (*q * *q);
+
+ for (long i = 0; i < (fhtsamples / 2); i++)
+ *p++ *= .5;
+}
+
+void Input::transform(float *p, long n, long k)
+{
+ if (n == 8)
+ {
+ transform8(p + k);
+ return;
+ }
+
+ long i, j, ndiv2 = n / 2;
+ float a, *t1, *t2, *t3, *t4, *ptab, *pp;
+
+ for (i = 0, t1 = fhtBuf, t2 = fhtBuf + ndiv2, pp = &p[k]; i < ndiv2; i++)
+ *t1++ = *pp++, *t2++ = *pp++;
+
+ memcpy(p + k, fhtBuf, sizeof(float) * n);
+
+ transform(p, ndiv2, k);
+ transform(p, ndiv2, k + ndiv2);
+
+ j = 512 / ndiv2 - 1;
+ t1 = fhtBuf;
+ t2 = t1 + ndiv2;
+ t3 = p + k + ndiv2;
+ ptab = fhtTab;
+ pp = p + k;
+
+ a = *ptab++ * *t3++;
+ a += *ptab * *pp;
+ ptab += j;
+
+ *t1++ = *pp + a;
+ *t2++ = *pp++ - a;
+
+ for (i = 1, t4 = p + k + n; i < ndiv2; i++, ptab += j)
+ {
+ a = *ptab++ * *t3++;
+ a += *ptab * *--t4;
+
+ *t1++ = *pp + a;
+ *t2++ = *pp++ - a;
+ }
+
+ memcpy(p + k, fhtBuf, sizeof(float) * n);
+
+}
+
+void Input::transform8(float *p)
+{
+ float a, b, c, d, e, f, g, h,
+ b_f2, d_h2, sqrt2 = M_SQRT2,
+ a_c_eg, a_ce_g, ac_e_g, aceg,
+ b_df_h, bdfh;
+
+ a = *p++, b = *p++, c = *p++, d = *p++;
+ e = *p++, f = *p++, g = *p++, h = *p;
+ b_f2 = (b - f) * sqrt2;
+ d_h2 = (d - h) * sqrt2;
+
+ a_c_eg = a - c - e + g;
+ a_ce_g = a - c + e - g;
+ ac_e_g = a + c - e - g;
+ aceg = a + c + e + g;
+
+ b_df_h = b - d + f - h;
+ bdfh = b + d + f + h;
+
+ *p = a_c_eg - d_h2;
+ *--p = a_ce_g - b_df_h;
+ *--p = ac_e_g - b_f2;
+ *--p = aceg - bdfh;
+ *--p = a_c_eg + d_h2;
+ *--p = a_ce_g + b_df_h;
+ *--p = ac_e_g + b_f2;
+ *--p = aceg + bdfh;
+}
+
+
+
+
diff --git a/noatun-plugins/nexscope/nex.cpp b/noatun-plugins/nexscope/nex.cpp
new file mode 100644
index 0000000..af19409
--- /dev/null
+++ b/noatun-plugins/nexscope/nex.cpp
@@ -0,0 +1,597 @@
+#include "nex.h"
+#include "gui.h"
+
+#include <unistd.h>
+
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qmultilineedit.h>
+
+#include "renderers.h"
+
+Mutex runLock;
+Nex *Nex::sNex=0;
+QTime timer;
+
+Renderer::Renderer() {}
+Renderer::~Renderer() {}
+
+Thread::Thread() : mThread(0)
+{
+
+}
+
+Thread::~Thread()
+{
+ if (mThread)
+ kill();
+}
+
+void Thread::start()
+{
+ mThread=SDL_CreateThread(&threadRun, (void*)this);
+}
+
+void Thread::kill()
+{
+ SDL_KillThread(mThread);
+}
+
+int Thread::wait()
+{
+ int val;
+ SDL_WaitThread(mThread, &val);
+ return val;
+}
+
+int Thread::threadRun(void *v)
+{
+ Thread *t=(Thread*)v;
+ return t->run();
+}
+
+NexCheckBox::NexCheckBox(QWidget *parent,
+ const QString &name, bool *v)
+ : QCheckBox(name, parent)
+{
+ value=v;
+ setChecked(*v);
+ connect(this, SIGNAL(toggled(bool)), SLOT(change(bool)));
+}
+void NexCheckBox::change(bool b)
+{
+ *value=b;
+}
+
+
+
+NexColorButton::NexColorButton(QWidget *parent, Pixel *color)
+ : KColorButton(parent)
+{
+ c=color;
+ QColor temp( (*c >> 16) & 0xFF, (*c >> 8) & 0xFF, *c & 0xFF);
+ setColor(temp);
+ connect(this, SIGNAL(changed(const QColor&)), SLOT(change(const QColor&)));
+}
+
+void NexColorButton::change(const QColor &co)
+{
+ *c= COLOR(qRed(co.rgb()), qGreen(co.rgb()), qBlue(co.rgb()));
+}
+
+void Bitmap::resize(int w, int h)
+{
+ delete [] mData;
+ mData=new Pixel[w*h];
+}
+
+void Bitmap::clear()
+{
+ memset(mData, 0, bytes());
+}
+
+void Bitmap::drawCircle(int x, int y, int r, Pixel color)
+{
+ int16_t cx = 0;
+ int16_t cy = r;
+ int16_t ocx = -1;
+ int16_t ocy = -1;
+ int16_t df = 1 - r;
+ int16_t d_e = 3;
+ int16_t d_se = -2 * r + 5;
+ int16_t xpcx, xmcx, xpcy, xmcy;
+ int16_t ypcy, ymcy, ypcx, ymcx;
+
+ do
+ { // Draw
+ if ((ocy!=cy) || (ocx!=cx))
+ {
+ xpcx=x+cx;
+ xmcx=x-cx;
+ if (cy>0)
+ {
+ ypcy=y+cy;
+ ymcy=y-cy;
+ setPixel(xmcx,ypcy,color);
+ setPixel(xpcx,ypcy,color);
+ setPixel(xmcx,ymcy,color);
+ setPixel(xpcx,ymcy,color);
+ }
+ else
+ {
+ setPixel(xmcx,y,color);
+ setPixel(xpcx,y,color);
+ }
+
+ ocy=cy;
+ xpcy=x+cy;
+ xmcy=x-cy;
+ if (cx>0)
+ {
+ ypcx=y+cx;
+ ymcx=y-cx;
+ setPixel(xmcy,ypcx,color);
+ setPixel(xpcy,ypcx,color);
+ setPixel(xmcy,ymcx,color);
+ setPixel(xpcy,ymcx,color);
+ }
+ else
+ {
+ setPixel(xmcy,y,color);
+ setPixel(xpcy,y,color);
+ }
+ ocx=cx;
+ }
+ // Update
+ if (df < 0)
+ {
+ df += d_e;
+ d_e += 2;
+ d_se += 2;
+ }
+ else
+ {
+ df += d_se;
+ d_e += 2;
+ d_se += 4;
+ cy--;
+ }
+ cx++;
+ } while(cx <= cy);
+}
+
+
+void Bitmap::fillCircle(int x, int y, int r, Pixel color)
+{
+ int16_t cx = 0;
+ int16_t cy = r;
+ int16_t ocx = -1;
+ int16_t ocy = -1;
+ int16_t df = 1 - r;
+ int16_t d_e = 3;
+ int16_t d_se = -2 * r + 5;
+ int16_t xpcx, xmcx, xpcy, xmcy;
+ int16_t ypcy, ymcy, ypcx, ymcx;
+
+ do
+ { // Draw
+ if ((ocy!=cy) || (ocx!=cx))
+ {
+ xpcx=x+cx;
+ xmcx=x-cx;
+ if (cy>0)
+ {
+ ypcy=y+cy;
+ ymcy=y-cy;
+ setPixel(xmcx,ypcy,color);
+ setPixel(xpcx,ypcy,color);
+ setPixel(xmcx,ymcy,color);
+ setPixel(xpcx,ymcy,color);
+ for (int h=xmcx; h<xpcx; h++)
+ setPixel(h, ypcy, color);
+
+ for (int h=xmcx; h<xpcx; h++)
+ setPixel(h, ymcy, color);
+ }
+ else
+ {
+ setPixel(xmcx,y,color);
+ setPixel(xpcx,y,color);
+ for (int h=xmcx; h<xpcx; h++)
+ setPixel(h, y, color);
+ }
+
+
+ ocy=cy;
+ xpcy=x+cy;
+ xmcy=x-cy;
+ if (cx>0)
+ {
+ ypcx=y+cx;
+ ymcx=y-cx;
+ setPixel(xmcy,ypcx,color);
+ setPixel(xpcy,ypcx,color);
+ setPixel(xmcy,ymcx,color);
+ setPixel(xpcy,ymcx,color);
+ for (int h=xmcy; h<xpcy; h++)
+ setPixel(h, ypcx, color);
+
+ for (int h=xmcy; h<xpcy; h++)
+ setPixel(h, ymcx, color);
+ }
+ else
+ {
+ setPixel(xmcy,y,color);
+ setPixel(xpcy,y,color);
+ for (int h=xmcy; h<xpcy; h++)
+ setPixel(h, y, color);
+ }
+ ocx=cx;
+ }
+ // Update
+ if (df < 0)
+ {
+ df += d_e;
+ d_e += 2;
+ d_se += 2;
+ }
+ else
+ {
+ df += d_se;
+ d_e += 2;
+ d_se += 4;
+ cy--;
+ }
+ cx++;
+ } while(cx <= cy);
+}
+
+
+void Bitmap::drawLine(int x1, int y1, int x2, int y2, Pixel color)
+{
+ // Variable setup
+ int dx = x2 - x1;
+ int sx = (dx >= 0) ? 1 : -1;
+ dx = sx * dx + 1;
+
+ int dy = y2 - y1;
+ int sy = (dy >= 0) ? 1 : -1;
+ dy = sy * dy + 1;
+
+
+ int pixx = sizeof(Pixel);
+ int pixy = width*sizeof(Pixel);
+ uint8_t *pixel = (uint8_t*)pixels() + pixx * x1 + pixy * y1;
+ pixx *= sx;
+ pixy *= sy;
+
+ if (dx < dy)
+ {
+ int swaptmp = dx;
+ dx = dy;
+ dy = swaptmp;
+ swaptmp = pixx;
+ pixx = pixy;
+ pixy = swaptmp;
+ }
+
+
+ // Draw
+ int y=0;
+
+ for(int x=0; x < dx; x++, pixel += pixx)
+ {
+ *(Pixel*)pixel=color;
+ y += dy;
+ if (y >= dx)
+ {
+ y -= dx;
+ pixel += pixy;
+ }
+ }
+
+}
+
+RendererList::RendererList() : mClearAfter(false)
+{
+ mFrame=nex->bitmapPool()->get(true);
+}
+
+RendererList::~RendererList()
+{
+ nex->bitmapPool()->release(mFrame);
+}
+
+Bitmap *RendererList::render(float *pcm[4], Bitmap *source)
+{
+ if (mClearAfter) mFrame->clear();
+
+ lock();
+ for (QPtrListIterator<Renderer> i(mRendererList); i.current(); ++i)
+ {
+ Bitmap *newframe=(*i)->render(pcm, mFrame);
+
+ if (newframe!=mFrame)
+ {
+ nex->bitmapPool()->release(mFrame);
+ mFrame=newframe;
+ }
+ }
+
+ unlock();
+
+ // add source+=source; return source;
+
+ uint8_t *d=(uint8_t*)source->pixels();
+ uint8_t *end=(uint8_t*)((uint8_t*)d+source->bytes());
+ uint8_t *s=(uint8_t*)mFrame->pixels();
+
+ while (d<end)
+ {
+ register int dest=*d;
+ if (dest && ((dest | *s) & 128))
+ {
+ // there's danger of going past 0xFF
+ dest+=*s;
+ if (dest & 256)
+ *d=0xFF; // oops, we did!
+ else
+ *d=dest;
+ }
+ else
+ {
+ // if neither touch the 128th bit, then the sum
+ // can't possibly be more than 0xFF
+ *d=dest+*s;
+ }
+
+
+ ++s;
+ ++d;
+ }
+
+ return source;
+}
+
+void RendererList::save(QDomElement &e)
+{
+ lock();
+ e.setTagName("List");
+
+ for (QPtrListIterator<Renderer> i(mRendererList); *i; ++i)
+ {
+ QDomElement item;
+ (*i)->save(item);
+ e.appendChild(item);
+ }
+
+ unlock();
+}
+
+void RendererList::load(const QDomElement &e)
+{
+ lock();
+
+ for (QDomNode n=e.firstChild(); !n.isNull(); n=n.nextSibling())
+ {
+ if (!n.isElement()) continue;
+ QDomElement child=n.toElement();
+
+ Renderer *r=0;
+
+ if (e.tagName()=="List")
+ r=new RendererList;
+ else
+ r=nex->renderer(e.tagName());
+
+ if (!r) continue;
+
+ r->load(child);
+ mRendererList.append(r);
+ }
+
+ unlock();
+}
+
+
+
+QWidget *RendererList::configure(QWidget *parent)
+{
+ return new RendererListConfigurator(this, parent);
+}
+
+RendererListConfigurator::RendererListConfigurator(RendererList *l, QWidget *parent)
+ : QWidget(parent), mList(l)
+{
+ (new QVBoxLayout(this))->setAutoAdd(true);
+ mErase=new QCheckBox(i18n("&Erase between frames"), this);
+ connect(mErase, SIGNAL(toggled(bool)), SLOT(eraseOn(bool)));
+ mErase->setChecked(mList->mClearAfter);
+
+ if (nex->rendererList()==l)
+ {
+ QCheckBox *mConvolve=new QCheckBox(i18n("&Convolve audio"), this);
+ connect(mConvolve, SIGNAL(toggled(bool)), SLOT(convolve(bool)));
+ mConvolve->setChecked(nex->input()->convolve());
+ }
+
+ new QLabel(i18n("Comments"), this);
+
+ mComments=new QMultiLineEdit(this);
+ mComments->setText(l->mComments);
+ mComments->setWordWrap(QMultiLineEdit::WidgetWidth);
+}
+
+RendererListConfigurator::~RendererListConfigurator()
+{
+ mList->mComments=mComments->text();
+}
+
+void RendererListConfigurator::eraseOn(bool state)
+{
+ mList->mClearAfter=state;
+}
+
+void RendererListConfigurator::convolve(bool state)
+{
+ nex->input()->setConvolve(state);
+}
+
+
+
+#define INSERT(name, func) mCreators.insert(name, new CreatorSig*(&func))
+
+Nex::Nex()
+{
+ sNex=this;
+ mBitmapPool=0;
+ mRendererList=0;
+
+ setupSize(width, height);
+
+ INSERT("Fade", Creators::fade);
+ INSERT("Doubler", Creators::doubler);
+ INSERT("Waveform", Creators::waveform);
+ INSERT("Hartley", Creators::hartley);
+}
+#undef INSERT
+
+void Nex::setupSize(int , int )
+{
+ mInput=new Input;
+ delete mBitmapPool;
+ delete mRendererList;
+ mBitmapPool=new BitmapPool();
+ mRendererList=new RendererList;
+}
+
+Nex::~Nex()
+{
+ delete mRendererList;
+ delete mBitmapPool;
+}
+
+#define NOTHREAD
+
+void Nex::go()
+{
+ runLock.unlock();
+ float *audio[6];
+
+ Bitmap *frame;
+ frame=mBitmapPool->get(true);
+
+ int frames=0;
+ QTime start(QTime::currentTime());
+
+ while (1)
+ {
+ mInput->getAudio(audio);
+ mRendererList->render(audio, frame);
+ int result=mOutput.display(frame);
+
+ frames++;
+
+ switch (result)
+ {
+ case OutputSDL::Exit:
+ std::cerr << "Trying" << std::endl;
+ delete mInput;
+ std::cerr << "Deleted" << std::endl;
+
+ std::cout << "Frames per Second: "
+ << frames/start.secsTo(QTime::currentTime()) << std::endl;
+ return;
+ case OutputSDL::Resize:
+// setupSize(width, height);
+ break;
+ }
+#ifdef NOTHREAD
+ kapp->processEvents();
+#endif
+ frame->clear();
+ }
+}
+
+
+Renderer *Nex::renderer(const QString &name)
+{
+ CreatorSig **sig=mCreators[name];
+ if (sig)
+ return (**sig)();
+ else
+ return 0;
+}
+
+QStringList Nex::renderers() const
+{
+ QDictIterator<CreatorSig*> i(mCreators);
+ QStringList list;
+
+ for (;i.current(); ++i)
+ list += i.currentKey();
+
+ return list;
+}
+
+
+#ifndef NOTHREAD
+class VisThread : public Thread
+{
+public:
+ virtual int run()
+ {
+ Nex::sNex->go();
+ exit(0);
+ return 0;
+ }
+};
+#endif
+
+int main(int argc, char **argv)
+{
+ Nex theNex;
+ Nex::sNex=&theNex;
+
+#ifndef NOTHREAD
+ runLock.lock();
+
+ VisThread vis;
+ vis.start();
+
+ runLock.lock();
+#endif
+
+ KAboutData aboutData("nex", I18N_NOOP("Nex"), "0.0.1",
+ I18N_NOOP("The awesome customizable scope"),
+ KAboutData::License_LGPL, "(C) 2001 Charles Samuels", 0,
+ "http://noatun.kde.org");
+
+ aboutData.addAuthor("Charles Samuels", I18N_NOOP("Nex Author"),
+ "charles@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KApplication app;
+
+
+ (new Control)->show();
+#ifdef NOTHREAD
+ theNex.go();
+
+#else
+ app.exec();
+
+ vis.wait();
+#endif
+ exit(0); //prevent segfault on exit, for some reason
+ return 0;
+}
+
+#include "nex.moc"
+
diff --git a/noatun-plugins/nexscope/nex.h b/noatun-plugins/nexscope/nex.h
new file mode 100644
index 0000000..05c7f81
--- /dev/null
+++ b/noatun-plugins/nexscope/nex.h
@@ -0,0 +1,352 @@
+#ifndef NEX_H
+#define NEX_H
+
+#include <qwidget.h>
+#include <qptrlist.h>
+#include <stdint.h>
+#include <vector>
+#include <qdict.h>
+
+#include <qdatetime.h>
+#include <iostream>
+
+#include <qdom.h>
+
+#include "SDL.h"
+#include "SDL_thread.h"
+
+typedef uint32_t Pixel;
+typedef uint8_t Byte;
+
+#define COLOR(r,g,b) ((r<<16) | (g<<8) | (b))
+#define COLORSTR(pixel) \
+ QString("#%1%2%3").arg(QString::number((pixel>>16) & 8, 16)) \
+ .arg(QString::number((pixel>>8) & 8, 16)).arg(QString::number(pixel& 8, 16))
+
+#define STRCOLOR(pixel) \
+ Pixel(((pixel.mid(1,2).toInt(0, 16)) <<16) \
+ | ((pixel.mid(3,2).toInt(0, 16)) <<8) \
+ | (pixel.mid(5,2).toInt(0, 16)))
+
+
+const int samples=512+256;
+const int fhtsamples=512;
+const int width=320*2;
+const int height=240*2;
+
+#define PI 3.141592654
+
+class Mutex
+{
+public:
+ Mutex() { mMutex=::SDL_CreateMutex(); }
+ ~Mutex() { ::SDL_DestroyMutex(mMutex); }
+
+ inline bool lock() { return !::SDL_mutexP(mMutex); }
+ inline bool unlock() { return !::SDL_mutexV(mMutex); }
+
+private:
+ SDL_mutex *mMutex;
+};
+
+class Thread
+{
+public:
+ Thread();
+
+ /**
+ * kill() the thread
+ **/
+ virtual ~Thread();
+
+ void start();
+ void kill();
+ int wait();
+
+protected:
+ virtual int run()=0;
+
+private:
+ static int threadRun(void *);
+
+ SDL_Thread *mThread;
+};
+
+class Spacer : public QWidget
+{
+Q_OBJECT
+public:
+ Spacer(QWidget *parent) : QWidget(parent)
+ {
+ setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
+ QSizePolicy::MinimumExpanding));
+ }
+};
+
+class Bitmap
+{
+public:
+ Bitmap() : mData(0)
+ { }
+ ~Bitmap() { delete [] mData; }
+
+ void resize(int w, int h);
+
+ inline void setPixel(int x, int y, Pixel c)
+ { mData[y*width+x] = c; }
+
+ inline void drawVerticalLine(int x, int yBottom, int yTop, Pixel c)
+ {
+ register int w=width;
+ Pixel *d=mData+x+yTop*w;
+ yBottom-=yTop;
+ do
+ {
+ *d=c;
+ d+=width;
+ } while (yBottom--);
+ }
+
+ inline void drawHorizontalLine(int left, int right, int y, Pixel c)
+ {
+ Pixel *d=mData+y*width+left;
+ right-=left;
+ do
+ {
+ *(d++)=c;
+ } while (right--);
+ }
+
+ void drawCircle(int x, int y, int radius, Pixel color);
+ void fillCircle(int x, int y, int radius, Pixel color);
+ void drawLine(int x1, int y1, int x2, int y2, Pixel color);
+
+ inline int bytes() const { return width*height*sizeof(Pixel); }
+ inline Pixel *pixels(int x, int y) { return &(mData[y*width+x]); }
+ inline Pixel *pixels() const { return mData; }
+ inline Pixel pixel(int x, int y) { return mData[y*width+x]; }
+ inline Pixel *lastPixel() { return pixels(width-1, height-1); }
+
+ void clear();
+
+private:
+ Pixel *mData;
+};
+
+
+
+
+/**
+ * maintains a list of bitmaps, so you
+ * can recycle allocated segments
+ *
+ * Thread safe
+ **/
+class BitmapPool
+{
+public:
+ BitmapPool();
+ ~BitmapPool();
+
+ Bitmap *get(bool clear=false);
+ void release(Bitmap *bitmap);
+
+private:
+ struct PoolItem;
+ QPtrList<BitmapPool::PoolItem> mBitmaps;
+ Mutex mMutex;
+};
+
+class StereoScope;
+
+struct convolve_state;
+
+class Input
+{
+public:
+ Input();
+ ~Input();
+
+ /**
+ * audio is a pair of pointers to the buffers,
+ * one for each channel
+ *
+ * get the amount stated in the const global
+ * samples
+ **/
+ void getAudio(float **audio);
+
+ void setConvolve(bool state);
+
+ bool convolve() { return state; }
+
+private:
+ void fht(float *p);
+ void transform(float *p, long n, long k);
+ void transform8(float *p);
+ void connect();
+
+private:
+ StereoScope *mScope;
+ float outleft[samples], outright[samples];
+ float haystack[512];
+ float temp[samples+256];
+ convolve_state *state;
+ volatile bool mConvolve;
+
+ float fhtBuf[samples-256];
+ float fhtTab[(samples-256)*2];
+
+ bool ok;
+ QObject *notifier;
+};
+
+class OutputSDL
+{
+public:
+ enum Event
+ { None=0, Resize, Exit };
+
+ OutputSDL();
+ ~OutputSDL();
+
+ int display(Bitmap *source);
+
+private:
+ // static for speed, since there can be only one anyway because SDL sucks
+ static SDL_Surface *surface;
+};
+
+#include <qcheckbox.h>
+#include <kcolorbutton.h>
+
+class NexCheckBox : public QCheckBox
+{
+Q_OBJECT
+public:
+ NexCheckBox(QWidget *parent, const QString &, bool *v);
+
+private slots:
+ void change(bool b);
+
+private:
+ bool *value;
+
+};
+
+class NexColorButton : public KColorButton
+{
+Q_OBJECT
+public:
+ NexColorButton(QWidget *parent, Pixel *color);
+
+private slots:
+ void change(const QColor &c);
+
+private:
+ Pixel *c;
+};
+
+class Renderer
+{
+public:
+ Renderer();
+ virtual ~Renderer();
+
+ virtual Bitmap *render(float *pcm[4], Bitmap *source) = 0;
+
+ virtual QWidget *configure(QWidget*) { return 0; }
+
+ virtual void save(QDomElement &) {}
+
+ virtual void load(const QDomElement &) {}
+};
+
+class QCheckBox;
+class QMultiLineEdit;
+class RendererList;
+
+class RendererListConfigurator : public QWidget
+{
+Q_OBJECT
+public:
+ RendererListConfigurator(RendererList *l, QWidget *parent);
+ ~RendererListConfigurator();
+
+public slots:
+ void eraseOn(bool state);
+ void convolve(bool state);
+
+private:
+ QCheckBox *mErase;
+ QMultiLineEdit *mComments;
+
+ RendererList *mList;
+};
+
+class RendererList : public Renderer, public Mutex
+{
+ friend class RendererListConfigurator;
+
+public:
+ RendererList();
+ virtual ~RendererList();
+ virtual Bitmap *render(float *pcm[4], Bitmap *source);
+
+ QPtrList<Renderer> &renderers() { return mRendererList; }
+ const QPtrList<Renderer> &renderers() const { return mRendererList; }
+
+ bool clearAfter() const { return mClearAfter; }
+ void setClearAfter(bool b) { mClearAfter=b; }
+
+ virtual QWidget *configure(QWidget *parent);
+
+ virtual void save(QDomElement &e);
+
+ virtual void load(const QDomElement &e);
+
+private:
+ QPtrList<Renderer> mRendererList;
+ volatile bool mClearAfter;
+ QString mComments;
+
+ Bitmap *mFrame;
+};
+
+typedef Renderer* (CreatorSig)();
+
+class Nex
+{
+public:
+ Nex();
+ ~Nex();
+
+ void go();
+ void setupSize(int w, int h);
+
+ static Nex *nex() { return sNex; }
+ BitmapPool *bitmapPool() { return mBitmapPool; }
+
+ RendererList *rendererList() { return mRendererList; }
+
+ Input *input() { return mInput; }
+
+ Renderer *renderer(const QString &name);
+
+ QStringList renderers() const;
+
+public:
+ static Nex *sNex;
+
+private:
+ Input *mInput;
+ OutputSDL mOutput;
+ BitmapPool *mBitmapPool;
+ RendererList *mRendererList;
+ QDict<CreatorSig*> mCreators;
+};
+
+#define nex Nex::nex()
+
+
+#endif
diff --git a/noatun-plugins/nexscope/nexscope.plugin b/noatun-plugins/nexscope/nexscope.plugin
new file mode 100644
index 0000000..ccd8ce2
--- /dev/null
+++ b/noatun-plugins/nexscope/nexscope.plugin
@@ -0,0 +1,72 @@
+Filename=noatunnexscope.la
+Author=Charles Samuels
+Site=http://noatun.kde.org/
+Email=charles@kde.org
+Type=visualization
+License=LGPL
+Name=NexScope
+Name[eo]=NeksoSkopo
+Name[es]=Siguiente Búsqueda
+Name[hi]=नेक्स्कोप
+Name[ru]=Зрительный образ Nex
+Name[sv]=Nexskop
+Name[ta]=Nex வரையெல்லை
+Name[tg]=Намуди тамошобини Nex
+Name[tr]=Nekskop
+Name[xh]=Iscope esilandelayo
+Comment=The ultra-customizable visualization
+Comment[az]=Ultra-ə'la əyani efektlər
+Comment[bg]=Визуализация, която може да се настройва лесно
+Comment[bs]=Ultra-prilagodljiva vizualizacija
+Comment[ca]=La visualització ultra-personalitzable
+Comment[cs]=Ultra přizpůsobitelná vizualizace
+Comment[cy]=Y dychmygydd tra-addasadwy
+Comment[da]=Den ultra-brugerdefinérbare visualisering
+Comment[de]=Sehr anpassbare Visualisierung
+Comment[el]=Η απόλυτα-ρυθμιζόμενη οπτικοποίηση
+Comment[en_GB]=The ultra-customisable visualisation
+Comment[es]=La visualización totalmente personalizable
+Comment[et]=Äärmiselt kohandatav ostsilloskoop
+Comment[eu]=Bisualizazio ultra pertsonalizagarria
+Comment[fa]=تجسم بسیار قابل سفارشی
+Comment[fi]=Erittäin säädettävä visualisaatio
+Comment[fr]=L'affichage ultra-configurable
+Comment[fy]=Tige goed oan te passen fisualisaasje
+Comment[ga]=An t-amharcléiriú fíor-in-saincheaptha
+Comment[gl]=A visualización ultra-configurábel
+Comment[he]=המחשה הניתנת להתאמה אישית מרבית
+Comment[hi]=एक अल्ट्रा-कस्टमाइजेबल विज़ुअलाइज़ेशन
+Comment[hr]=Totalno prilagodiva vizualizacija
+Comment[hu]=Százféle módon variálható vizualizációs modul
+Comment[is]=Sjónræm myndbrella sem er mjög stillanleg
+Comment[it]=Un visualizzatore ultra personalizzabile
+Comment[ja]=超カスタマイズ可能な視覚効果
+Comment[ka]=ულტრა მორგებადი ვიზუალიზაცია
+Comment[kk]=Баптауы бай көрінісі
+Comment[km]=រូបភាព​មើល​ឃើញ​​ដែល​អាច​ប្ដូរ​តាម​បំណងបាន​​ច្រើន​ហួស​​ពេក
+Comment[lt]=Ypač derinama vizualizacija
+Comment[mk]=Ултра-приспособлива визуелизација
+Comment[ms]=Visualisasi ultra boleh selenggara
+Comment[nb]=Den ekstremt tilpasningsdyktige visualiseringen
+Comment[nds]=Filmmaker mit bannig vele Instellen
+Comment[ne]=अति-अनुकूलनयोग्य दृश्टीकरण
+Comment[nl]=Zeer goed aan te passen visualisatie
+Comment[nn]=Den ekstremt tiplasningsdyktige visualiseringa
+Comment[pl]=Wizualizacja z bardzo wieloma możliwościami dostosowania
+Comment[pt]=A visualização ultra-configurável
+Comment[pt_BR]=Uma visualização ultra-personalizável
+Comment[ro]=O vizualizare sunet extrem de configurabilă
+Comment[ru]=Тонко настраиваемый зрительный образ
+Comment[sk]=Veľmi dobre nastaviteľná vizualizácia
+Comment[sl]=Ultra prilagodljiva vizualizacija
+Comment[sr]=Ултраприлагодљива визуелизација
+Comment[sr@Latn]=Ultraprilagodljiva vizuelizacija
+Comment[sv]=Den extremt anpassningsbara visualiseringen
+Comment[ta]=அல்ட்ரா-தனிப்பயனாக்கு காட்சியமைப்புகள்
+Comment[tg]=Мизроби борики намуди тамошобин
+Comment[tr]=Ultra-özelleştirilebilir görsellik
+Comment[uk]=Гнучкий втулок представлення
+Comment[vi]=Ô xem có thể tùy chỉnh suốt
+Comment[xh]=Okubonakalayo okungaphezulu kwemfuneko
+Comment[zh_CN]=极可定制的视觉化显示
+Comment[zh_TW]=可調整視覺化
diff --git a/noatun-plugins/nexscope/noatunplugin.cpp b/noatun-plugins/nexscope/noatunplugin.cpp
new file mode 100644
index 0000000..8bbc860
--- /dev/null
+++ b/noatun-plugins/nexscope/noatunplugin.cpp
@@ -0,0 +1,49 @@
+#include "nex.h"
+#include "noatunplugin.h"
+
+#include <kprocess.h>
+#include <kmessagebox.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+extern "C"
+{
+Plugin *create_plugin()
+{
+ KGlobal::locale()->insertCatalogue("nexscope");
+ return new NexPlugin();
+}
+}
+
+
+NexPlugin::NexPlugin()
+{
+ connect(&process, SIGNAL(processExited(KProcess *)), this, SLOT(processExited(KProcess *)));
+}
+
+NexPlugin::~NexPlugin()
+{
+ process.kill();
+}
+
+void NexPlugin::init()
+{
+ process << KStandardDirs::findExe("nexscope.bin");
+
+ // Note that process.start() will fail if findExe fails, so there's no real need
+ // for two separate checks.
+ if(!process.start(KProcess::NotifyOnExit, (KProcess::Communication)(KProcess::Stdin | KProcess::Stdout)))
+ {
+ KMessageBox::error(0, i18n("Unable to start noatunNex. Check your installation."));
+ unload();
+ }
+
+}
+
+void NexPlugin::processExited(KProcess *)
+{
+ unload();
+}
+
+#include "noatunplugin.moc"
+
diff --git a/noatun-plugins/nexscope/noatunplugin.h b/noatun-plugins/nexscope/noatunplugin.h
new file mode 100644
index 0000000..8f8abb2
--- /dev/null
+++ b/noatun-plugins/nexscope/noatunplugin.h
@@ -0,0 +1,25 @@
+#ifndef NEXPLUG_H
+#define NEXPLUG_H
+
+#include <kprocess.h>
+#include <noatun/plugin.h>
+
+class NexPlugin : public QObject, public Plugin
+{
+Q_OBJECT
+
+public:
+ NexPlugin();
+ virtual ~NexPlugin();
+
+ void init();
+
+private slots:
+ void processExited(KProcess *);
+
+private:
+ KProcess process;
+};
+
+
+#endif
diff --git a/noatun-plugins/nexscope/output.cpp b/noatun-plugins/nexscope/output.cpp
new file mode 100644
index 0000000..84885e2
--- /dev/null
+++ b/noatun-plugins/nexscope/output.cpp
@@ -0,0 +1,48 @@
+#include "nex.h"
+#include <stdlib.h>
+
+SDL_Surface *OutputSDL::surface=0;
+
+OutputSDL::OutputSDL()
+{
+ SDL_Init(SDL_INIT_VIDEO);
+
+ SDL_WM_SetCaption("Nex","noatun");
+
+ bool fullscreen=false;
+ Uint32 flags = SDL_SWSURFACE | (fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE);
+
+ surface = SDL_SetVideoMode(width, height, 32, flags);
+
+ SDL_ShowCursor(1);
+
+}
+
+
+OutputSDL::~OutputSDL()
+{
+ SDL_FreeSurface(surface);
+}
+
+
+int OutputSDL::display(Bitmap *source)
+{
+ memcpy(surface->pixels, source->pixels(), source->bytes());
+ SDL_UpdateRect(surface, 0, 0, 0, 0);
+
+ SDL_Event event;
+
+ while ( SDL_PollEvent(&event) > 0 )
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ return Exit;
+ default:
+ break;
+ }
+ }
+ return None;
+}
+
+
diff --git a/noatun-plugins/nexscope/regionwidget.cpp b/noatun-plugins/nexscope/regionwidget.cpp
new file mode 100644
index 0000000..32e9987
--- /dev/null
+++ b/noatun-plugins/nexscope/regionwidget.cpp
@@ -0,0 +1,77 @@
+
+#include "regionwidget.h"
+
+RegionWidget::RegionWidget(QWidget *parent)
+{
+
+}
+
+RegionWidget::RegionWidget(const QSize &viewsize, QWidget *parent)
+{
+
+}
+RegionWidget::RegionWidget(int x, int y, int w, int h, const QSize &viewsize,
+ QWidget *parent)
+{
+
+}
+
+RegionWidget::RegionWidget(const QRect &region, const QSize &viewsize,
+ QWidget *parent)
+{
+
+}
+
+RegionWidget::~RegionWidget()
+{
+
+}
+
+
+QRect RegionWidget::region() const
+{
+ return QRect(0,0,0,0);
+}
+
+void RegionWidget::setX(int x)
+{
+
+}
+
+void RegionWidget::setY(int y)
+{
+
+}
+
+void RegionWidget::setWidth(int w)
+{
+
+}
+
+void RegionWidget::setHeight(int h)
+{
+
+}
+
+void RegionWidget::setRegion(const QRect &region)
+{
+
+}
+
+void RegionWidget::setViewSize(const QSize &size)
+{
+
+}
+
+void RegionWidget::moved(int x, int y)
+{
+
+}
+
+void RegionWidget::resized(int w, int h)
+{
+
+}
+
+#include "regionwidget.moc"
+
diff --git a/noatun-plugins/nexscope/regionwidget.h b/noatun-plugins/nexscope/regionwidget.h
new file mode 100644
index 0000000..eeac8b4
--- /dev/null
+++ b/noatun-plugins/nexscope/regionwidget.h
@@ -0,0 +1,47 @@
+#ifndef REGIONWIDGET_H
+#define REGIONWIDGET_H
+
+#include <qframe.h>
+
+/**
+ * show a widget with a field rectangle, and a mini-region inside
+ *
+ * text boxes allow the region to be resized and moved, along with
+ * click-n-drag
+ **/
+class RegionWidget : public QFrame
+{
+Q_OBJECT
+public:
+ RegionWidget(QWidget *parent);
+ RegionWidget(const QSize &viewsize, QWidget *parent);
+ RegionWidget(int x, int y, int w, int h, const QSize &viewsize,
+ QWidget *parent);
+ RegionWidget(const QRect &region, const QSize &viewsize, QWidget *parent);
+
+ ~RegionWidget();
+
+ QRect region() const;
+
+public slots:
+ void setX(int x);
+ void setY(int y);
+ void setWidth(int w);
+ void setHeight(int h);
+
+ void setRegion(const QRect &region);
+
+ void setViewSize(const QSize &size);
+
+signals:
+ void changed();
+ void changed(int x, int y, int w, int h);
+ void changed(const QRect &region);
+
+protected:
+ virtual void moved(int x, int y);
+ virtual void resized(int w, int h);
+};
+
+
+#endif
diff --git a/noatun-plugins/nexscope/renderers.cpp b/noatun-plugins/nexscope/renderers.cpp
new file mode 100644
index 0000000..830024b
--- /dev/null
+++ b/noatun-plugins/nexscope/renderers.cpp
@@ -0,0 +1,369 @@
+#include "nex.h"
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+#include <iostream>
+#include <qlayout.h>
+#include <klocale.h>
+
+struct HorizontalPair : public Renderer
+{
+ HorizontalPair() : color(0x578cd8)
+ {
+ // 0x578cd8 0x3cff3f 0x10FFFF
+ horizontal=false;
+ pair=true;
+ }
+
+ QWidget *configure(QWidget *parent)
+ {
+ QWidget *config=new QWidget(parent);
+ (new QVBoxLayout(config))->setAutoAdd(true);
+
+ new NexColorButton(config, &color);
+ new NexCheckBox(config, i18n("Horizontal"), &horizontal);
+ new NexCheckBox(config, i18n("Pair"), &pair);
+ new NexCheckBox(config, i18n("Solid"), &solid);
+ new Spacer(config);
+
+ return config;
+ }
+
+ static inline void processV(int h, int start, int end, Bitmap *d,
+ float *ch, Pixel c, register bool solid)
+ {
+ int oldx=(start+end)/2;
+ int mid=oldx;
+ float quarter=(end-start)/2.0;
+ for (int y=0; y<h; ++y)
+ {
+ int newx=mid+(int)(ch[y]*quarter);
+ if (newx<start) newx=start;
+ if (newx>end) newx=end;
+ if (newx>oldx)
+ d->drawHorizontalLine(oldx, newx, y, c);
+ else
+ d->drawHorizontalLine(newx, oldx, y, c);
+ if (!solid)
+ oldx=newx;
+ }
+ }
+
+ static inline void processH(int h, int start, int end, Bitmap *d,
+ float *ch, Pixel c, register bool solid)
+ {
+ int oldx=(start+end)/2;
+ int mid=oldx;
+ float quarter=(end-start)/2.0;
+ for (int y=0; y<h; ++y)
+ {
+ int newx=mid+(int)(ch[y]*quarter);
+ if (newx<start) newx=start;
+ if (newx>end) newx=end;
+ if (newx>oldx)
+ d->drawVerticalLine(y, newx, oldx, c);
+ else
+ d->drawVerticalLine(y, oldx, newx, c);
+ if (!solid)
+ oldx=newx;
+ }
+ }
+
+ virtual Bitmap *render(float **pcm, Bitmap *src)
+ {
+ if (horizontal)
+ {
+ if (pair)
+ {
+ processH(width, 0, height/2, src, pcm[0], color, solid);
+ processH(width, height/2, height, src, pcm[1], color, solid);
+ }
+ else
+ {
+ processH(width, 0, height, src, pcm[0], color, solid);
+ }
+ }
+ else
+ {
+ if (pair)
+ {
+ processV(height, 0, width/2, src, pcm[0], color, solid);
+ processV(height, width/2, width, src, pcm[1], color, solid);
+ }
+ else
+ {
+ processV(height, 0, width, src, pcm[0], color, solid);
+ }
+ }
+
+ return src;
+ }
+
+ virtual void save(QDomElement &e)
+ {
+ e.setTagName("Waveform");
+ e.setAttribute("horizontal", (int)horizontal);
+ e.setAttribute("pair", (int)pair);
+ e.setAttribute("solid", (int)solid);
+ e.setAttribute("color", COLORSTR(color));
+ }
+
+ virtual void load(const QDomElement &e)
+ {
+ horizontal=(bool)e.attribute("horizontal", 0).toInt();
+ pair=(bool)e.attribute("pair", 0).toInt();
+ solid=(bool)e.attribute("solid", 0).toInt();
+ color=STRCOLOR(e.attribute("color", "#FFFFFF"));
+ }
+
+ Pixel color;
+ bool horizontal;
+ bool pair;
+ bool solid;
+};
+
+struct Hartley : public Renderer
+{
+ virtual Bitmap *render(float **pcm, Bitmap *src)
+ {
+ float mult=height/2.0;
+ for (int i=0; i<width; i++)
+ {
+ int h=pcm[3][i]*mult;
+ if (h>height) h=height;
+
+ src->drawVerticalLine(i, height, height-h,0xFFFFFF);
+ }
+ return src;
+ }
+};
+
+class Fade : public Renderer
+{
+public:
+ virtual Bitmap *render(float *[2], Bitmap *src)
+ {
+ register int i=src->bytes()/sizeof(Pixel);
+
+ register Pixel *d=src->pixels();
+ do
+ {
+ *d -= ((*d & 0xf0f0f0f0) >> 4) +
+ ((*d & 0xe0e0e0e0) >> 5);
+ ++d;
+ } while (--i > 0);
+ return src;
+ }
+};
+
+
+struct Doubler : public Renderer
+{
+ Doubler()
+ {
+ srcX=20;
+ srcY=100;
+ destX=240;
+ destY=160;
+ srcWidth=40;
+ srcHeight=40;
+
+ }
+
+ inline static void copyAndDouble(Pixel *src, Pixel *dest1,
+ Pixel *dest2, int w)
+ {
+ while (w--)
+ {
+ *dest1=*dest2=*src;
+ dest1++;
+ dest2++;
+ *dest1=*dest2=*src;
+ dest1++;
+ dest2++;
+ src++;
+ }
+ }
+
+ virtual Bitmap *render(float *[2], Bitmap *src)
+ {
+ for (int y=0; y<srcHeight; y++)
+ {
+ copyAndDouble(src->pixels(srcX, y+srcY),
+ src->pixels(destX, destY+y*2),
+ src->pixels(destX, destY+y*2+1),
+ srcWidth);
+ }
+ return src;
+ }
+
+ int srcX, srcY;
+ int destX, destY;
+ int srcWidth, srcHeight;
+};
+
+struct Blur : public Renderer
+{
+ virtual Bitmap *render(float *[2], Bitmap *src)
+ {
+ Bitmap *b=nex->bitmapPool()->get();
+
+ register Byte *buffer=(Byte*)b->pixels();
+ register unsigned short pitch=width*sizeof(Pixel);
+
+ // skip the first row
+ buffer+=pitch;
+
+ // skip the first pixel on the second row;
+ buffer+=sizeof(Pixel);
+
+ // we also don't want to do the last row
+ register Pixel *end=b->lastPixel();
+ end-=pitch;
+ // and the last pixel on the second-to-last row
+ end-=sizeof(Pixel);
+
+ while (buffer<(Byte*)end)
+ {
+
+
+ }
+ return b;
+ }
+};
+
+#ifdef cow
+struct FadeHeat : public Renderer
+{
+ inline void fadePixelHeat(Bitmap *out, int x, int y)
+ {
+ Pixel up = lastOutput->pixel(x, y-1);
+ Pixel down = lastOutput->pixel(x, y+1);
+ Pixel left = lastOutput->pixel(x-1, y);
+ Pixel right = lastOutput->pixel(x+1, y);
+
+ // assuming Pixel AARRGGBB
+ Pixel r, g, b, pixel;
+ const int rMask = 0xFFFF0000;
+ const int gMask = 0x00FFFF00;
+ const int bMask = 0x0000FFFF;
+
+ // average the up down left right for each component
+ r = up & rMask +
+ right & rMask +
+ left & rMask +
+ bottom & rMask;
+ r >>= 2;
+ r &= rMask;
+
+ g = up & gMask +
+ right & gMask +
+ left & gMask +
+ bottom & gMask;
+ g >>= 2;
+ g &= gMask;
+
+ b = up & bMask +
+ right & bMask +
+ left & bMask +
+ bottom & bMask;
+ b >>= 2;
+ b &= bMask;
+
+ Pixel all=r|g|b;
+ if(!all)
+ {
+ out->pixel(x, y, 0);
+ return;
+ }
+
+ // some more input
+ Pixel c=lastLastOutput->getPixel(x, y);
+ c+=0x00010101;
+
+
+
+ all -= c;
+
+ // bounds check
+/* slow: if (all & 0xFF000000) all=0;
+ else if (all & 0x7F000000) all = 0x00FFFFFF;
+ out->setPixel(x, y, all); */
+
+ // everything is normal
+ if(!(all & 0xFF000000)) out->setPixel(x,y, all);
+ // wraparound occured (0x80==0b10000000)
+ else if(all & 0x80000000) out->setPixel(x,y, 0);
+ else out->setPixel(x,y, 0x00FFFFFF);
+ }
+
+ virtual Bitmap *render(float *[2], Bitmap *src)
+ {
+ Bitmap *lastLastTemp = lastLastOutput;
+ lastLastOutput = lastOutput;
+ lastOutputBmp.data = src;
+ src = lastLastTemp;
+
+ int x,y,i,j,start,end;
+ int step = outWidth*2;
+ for(x=i=0, j=outWidth*(outHeight-1)*2;
+ x<outWidth;
+ x++, i+=2, j+=2)
+ {
+ fadePixelHeat(src, x,0,i);
+ fadePixelHeat(src, x,0,i+1);
+ fadePixelHeat(src, x,outHeight-1,j);
+ fadePixelHeat(src, x,outHeight-1,j+1);
+ }
+
+ for(y=1, i=outWidth*2, j=outWidth*4-2;
+ y<outHeight;
+ y++, i+=step, j+=step)
+ {
+ fadePixelHeat(src, 0, y,i);
+ fadePixelHeat(src, 0, y,i+1);
+ fadePixelHeat(src, outWidth-1,y,j);
+ fadePixelHeat(src, outWidth-1,y,j+1);
+ }
+
+
+ for(y=1,start=outWidth*2+2,
+ end=outWidth*4-2; y<outHeight-1;
+ y++,start+=step,end+=step)
+ {
+ int i = start;
+ do
+ {
+ short j =
+ ( short(Dlo[i-2])+
+ Dlo[i+2]+
+ +Dlo[i-step]
+ +Dlo[i+step]
+ >> 2)
+ +Dlo[i];
+ if (!j)
+ {
+ Do[i] = 0;
+ }
+ else
+ {
+ j = j-Dllo[i]+(Dllo[i] -Dlo[i]>>2)-1;
+ if (j < 0) Do[i] = 0;
+ else if (j & (255*256)) Do[i] = 255;
+ else Do[i] = j;
+ }
+ } while(++i < end);
+ }
+
+ return src;
+ }
+
+ Bitmap *lastOutput, Bitmap *lastLastOutput;
+};
+#endif
+
+#define RENDERERS_CPP
+#include "renderers.h"
+#undef RENDERERS_CPP
+
+
diff --git a/noatun-plugins/nexscope/renderers.h b/noatun-plugins/nexscope/renderers.h
new file mode 100644
index 0000000..3105849
--- /dev/null
+++ b/noatun-plugins/nexscope/renderers.h
@@ -0,0 +1,34 @@
+#ifndef RENDERERS_H
+#define RENDERERS_H
+
+#include <qstring.h>
+
+class Renderer;
+
+namespace Creators
+{
+
+
+
+#ifdef RENDERERS_CPP
+#define REGISTER(function, cl) \
+Renderer *function() \
+{ \
+ return new cl; \
+}
+#else
+#define REGISTER(function, cl) \
+Renderer *function();
+#endif
+
+REGISTER(fade, Fade)
+REGISTER(doubler, Doubler)
+REGISTER(waveform, HorizontalPair);
+REGISTER(hartley, Hartley);
+
+#undef REGISTER
+};
+
+
+#endif
+