diff options
Diffstat (limited to 'noatun-plugins/nexscope')
-rw-r--r-- | noatun-plugins/nexscope/Makefile.am | 22 | ||||
-rw-r--r-- | noatun-plugins/nexscope/bitmappool.cpp | 54 | ||||
-rw-r--r-- | noatun-plugins/nexscope/convolve.c | 297 | ||||
-rw-r--r-- | noatun-plugins/nexscope/convolve.h | 61 | ||||
-rw-r--r-- | noatun-plugins/nexscope/gui.cpp | 197 | ||||
-rw-r--r-- | noatun-plugins/nexscope/gui.h | 54 | ||||
-rw-r--r-- | noatun-plugins/nexscope/input.cpp | 238 | ||||
-rw-r--r-- | noatun-plugins/nexscope/nex.cpp | 597 | ||||
-rw-r--r-- | noatun-plugins/nexscope/nex.h | 352 | ||||
-rw-r--r-- | noatun-plugins/nexscope/nexscope.plugin | 72 | ||||
-rw-r--r-- | noatun-plugins/nexscope/noatunplugin.cpp | 49 | ||||
-rw-r--r-- | noatun-plugins/nexscope/noatunplugin.h | 25 | ||||
-rw-r--r-- | noatun-plugins/nexscope/output.cpp | 48 | ||||
-rw-r--r-- | noatun-plugins/nexscope/regionwidget.cpp | 77 | ||||
-rw-r--r-- | noatun-plugins/nexscope/regionwidget.h | 47 | ||||
-rw-r--r-- | noatun-plugins/nexscope/renderers.cpp | 369 | ||||
-rw-r--r-- | noatun-plugins/nexscope/renderers.h | 34 |
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 ®ion, 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 ®ion) +{ + +} + +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 ®ion, 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 ®ion); + + void setViewSize(const QSize &size); + +signals: + void changed(); + void changed(int x, int y, int w, int h); + void changed(const QRect ®ion); + +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 + |