changeset 0:da6937383da8

initial import
author Chris Cannam
date Tue, 10 Jan 2006 16:33:16 +0000
parents
children a23739e2338a
files base/AudioLevel.cpp base/AudioLevel.h base/AudioPlaySource.h base/Layer.cpp base/Layer.h base/Model.cpp base/Model.h base/PlayParameters.cpp base/PlayParameters.h base/Profiler.cpp base/Profiler.h base/PropertyContainer.cpp base/PropertyContainer.h base/RealTime.cpp base/RealTime.h base/RingBuffer.h base/Scavenger.h base/System.cpp base/System.h base/View.cpp base/View.h base/ViewManager.cpp base/ViewManager.h base/Window.h base/ZoomConstraint.h plugin/DSSIPluginFactory.cpp plugin/DSSIPluginFactory.h plugin/DSSIPluginInstance.cpp plugin/DSSIPluginInstance.h plugin/FeatureExtractionPlugin.h plugin/FeatureExtractionPluginFactory.cpp plugin/FeatureExtractionPluginFactory.h plugin/LADSPAPluginFactory.cpp plugin/LADSPAPluginFactory.h plugin/LADSPAPluginInstance.cpp plugin/LADSPAPluginInstance.h plugin/PluginDescription.h plugin/PluginIdentifier.cpp plugin/PluginIdentifier.h plugin/RealTimePluginFactory.cpp plugin/RealTimePluginFactory.h plugin/RealTimePluginInstance.cpp plugin/RealTimePluginInstance.h plugin/api/FEAPI.h plugin/api/alsa/README plugin/api/alsa/asoundef.h plugin/api/alsa/asoundlib.h plugin/api/alsa/seq.h plugin/api/alsa/seq_event.h plugin/api/alsa/seq_midi_event.h plugin/api/alsa/sound/asequencer.h plugin/api/dssi.h plugin/api/dssi_alsa_compat.c plugin/api/ladspa.h plugin/api/new_dssi_transport_patch plugin/plugins/SamplePlayer.cpp plugin/plugins/SamplePlayer.h transform/BeatDetectTransform.cpp transform/BeatDetectTransform.h transform/BeatDetectionFunctionTransform.cpp transform/BeatDetectionFunctionTransform.h transform/FeatureExtractionPluginTransform.cpp transform/FeatureExtractionPluginTransform.h transform/Transform.cpp transform/Transform.h transform/TransformFactory.cpp transform/TransformFactory.h
diffstat 67 files changed, 13396 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/AudioLevel.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,268 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include "base/AudioLevel.h"
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <vector>
+#include <cassert>
+
+const float AudioLevel::DB_FLOOR = -1000.0;
+
+struct FaderDescription
+{
+    FaderDescription(float _minDb, float _maxDb, float _zeroPoint) :
+	minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { }
+
+    float minDb;
+    float maxDb;
+    float zeroPoint; // as fraction of total throw
+};
+
+static const FaderDescription faderTypes[] = {
+    FaderDescription(-40.0,  +6.0, 0.75), // short
+    FaderDescription(-70.0, +10.0, 0.80), // long
+    FaderDescription(-70.0,   0.0, 1.00), // IEC268
+    FaderDescription(-70.0, +10.0, 0.80), // IEC268 long
+    FaderDescription(-40.0,   0.0, 1.00), // preview
+};
+
+typedef std::vector<float> LevelList;
+static std::map<int, LevelList> previewLevelCache;
+static const LevelList &getPreviewLevelCache(int levels);
+
+float
+AudioLevel::multiplier_to_dB(float multiplier)
+{
+    if (multiplier == 0.0) return DB_FLOOR;
+    float dB = 10 * log10f(multiplier);
+    return dB;
+}
+
+float
+AudioLevel::dB_to_multiplier(float dB)
+{
+    if (dB == DB_FLOOR) return 0.0;
+    float m = powf(10.0, dB / 10.0);
+    return m;
+}
+
+/* IEC 60-268-18 fader levels.  Thanks to Steve Harris. */
+
+static float iec_dB_to_fader(float db)
+{
+    float def = 0.0f; // Meter deflection %age
+
+    if (db < -70.0f) {
+        def = 0.0f;
+    } else if (db < -60.0f) {
+        def = (db + 70.0f) * 0.25f;
+    } else if (db < -50.0f) {
+        def = (db + 60.0f) * 0.5f + 5.0f;
+    } else if (db < -40.0f) {
+        def = (db + 50.0f) * 0.75f + 7.5f;
+    } else if (db < -30.0f) {
+        def = (db + 40.0f) * 1.5f + 15.0f;
+    } else if (db < -20.0f) {
+        def = (db + 30.0f) * 2.0f + 30.0f;
+    } else {
+        def = (db + 20.0f) * 2.5f + 50.0f;
+    }
+
+    return def;
+}
+
+static float iec_fader_to_dB(float def)  // Meter deflection %age
+{
+    float db = 0.0f;
+
+    if (def >= 50.0f) {
+	db = (def - 50.0f) / 2.5f - 20.0f;
+    } else if (def >= 30.0f) {
+	db = (def - 30.0f) / 2.0f - 30.0f;
+    } else if (def >= 15.0f) {
+	db = (def - 15.0f) / 1.5f - 40.0f;
+    } else if (def >= 7.5f) {
+	db = (def - 7.5f) / 0.75f - 50.0f;
+    } else if (def >= 5.0f) {
+	db = (def - 5.0f) / 0.5f - 60.0f;
+    } else {
+	db = (def / 0.25f) - 70.0f;
+    }
+
+    return db;
+}
+
+float
+AudioLevel::fader_to_dB(int level, int maxLevel, FaderType type)
+{
+    if (level == 0) return DB_FLOOR;
+
+    if (type == IEC268Meter || type == IEC268LongMeter) {
+
+	float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
+	float percent = float(level) * maxPercent / float(maxLevel);
+	float dB = iec_fader_to_dB(percent);
+	return dB;
+
+    } else { // scale proportional to sqrt(fabs(dB))
+
+	int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
+    
+	if (level >= zeroLevel) {
+	    
+	    float value = level - zeroLevel;
+	    float scale = float(maxLevel - zeroLevel) /
+		sqrtf(faderTypes[type].maxDb);
+	    value /= scale;
+	    float dB = powf(value, 2.0);
+	    return dB;
+	    
+	} else {
+	    
+	    float value = zeroLevel - level;
+	    float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
+	    value /= scale;
+	    float dB = powf(value, 2.0);
+	    return 0.0 - dB;
+	}
+    }
+}
+
+
+int
+AudioLevel::dB_to_fader(float dB, int maxLevel, FaderType type)
+{
+    if (dB == DB_FLOOR) return 0;
+
+    if (type == IEC268Meter || type == IEC268LongMeter) {
+
+	// The IEC scale gives a "percentage travel" for a given dB
+	// level, but it reaches 100% at 0dB.  So we want to treat the
+	// result not as a percentage, but as a scale between 0 and
+	// whatever the "percentage" for our (possibly >0dB) max dB is.
+	
+	float maxPercent = iec_dB_to_fader(faderTypes[type].maxDb);
+	float percent = iec_dB_to_fader(dB);
+	int faderLevel = int((maxLevel * percent) / maxPercent + 0.01);
+	
+	if (faderLevel < 0) faderLevel = 0;
+	if (faderLevel > maxLevel) faderLevel = maxLevel;
+	return faderLevel;
+
+    } else {
+
+	int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint);
+
+	if (dB >= 0.0) {
+	    
+	    float value = sqrtf(dB);
+	    float scale = (maxLevel - zeroLevel) / sqrtf(faderTypes[type].maxDb);
+	    value *= scale;
+	    int level = int(value + 0.01) + zeroLevel;
+	    if (level > maxLevel) level = maxLevel;
+	    return level;
+	    
+	} else {
+
+	    dB = 0.0 - dB;
+	    float value = sqrtf(dB);
+	    float scale = zeroLevel / sqrtf(0.0 - faderTypes[type].minDb);
+	    value *= scale;
+	    int level = zeroLevel - int(value + 0.01);
+	    if (level < 0) level = 0;
+	    return level;
+	}
+    }
+}
+
+	
+float
+AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type)
+{
+    if (level == 0) return 0.0;
+    return dB_to_multiplier(fader_to_dB(level, maxLevel, type));
+}
+
+int
+AudioLevel::multiplier_to_fader(float multiplier, int maxLevel, FaderType type)
+{
+    if (multiplier == 0.0) return 0;
+    float dB = multiplier_to_dB(multiplier);
+    int fader = dB_to_fader(dB, maxLevel, type);
+    return fader;
+}
+
+
+const LevelList &
+getPreviewLevelCache(int levels)
+{
+    LevelList &ll = previewLevelCache[levels];
+    if (ll.empty()) {
+	for (int i = 0; i <= levels; ++i) {
+	    float m = AudioLevel::fader_to_multiplier
+		(i + levels/4, levels + levels/4, AudioLevel::PreviewLevel);
+	    if (levels == 1) m /= 100; // noise
+	    ll.push_back(m);
+	}
+    }
+    return ll;
+}
+
+int
+AudioLevel::multiplier_to_preview(float m, int levels)
+{
+    assert(levels > 0);
+    if (m < 0.0) return -multiplier_to_preview(-m, levels);
+
+    const LevelList &ll = getPreviewLevelCache(levels);
+    int result = -1;
+
+    int lo = 0, hi = levels;
+
+    // binary search
+    int level = -1;
+    while (result < 0) {
+	int newlevel = (lo + hi) / 2;
+	if (newlevel == level ||
+	    newlevel == 0 ||
+	    newlevel == levels) {
+	    result = newlevel;
+	    break;
+	}
+	level = newlevel;
+	if (ll[level] >= m) {
+	    hi = level;
+	} else if (ll[level+1] >= m) {
+	    result = level;
+	} else {
+	    lo = level;
+	}
+    }
+		   
+    return result;
+}
+
+float
+AudioLevel::preview_to_multiplier(int level, int levels)
+{
+    assert(levels > 0);
+    if (level < 0) return -preview_to_multiplier(-level, levels);
+    const LevelList &ll = getPreviewLevelCache(levels);
+    return ll[level];
+}
+	
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/AudioLevel.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _AUDIO_LEVEL_H_
+#define _AUDIO_LEVEL_H_
+
+/**
+ * AudioLevel converts audio sample levels between various scales:
+ *
+ *   - dB values (-inf -> 0dB)
+ *   - floating-point values (-1.0 -> 1.0) such as used for a
+ *     multiplier for gain or in floating-point WAV files
+ *   - integer values intended to correspond to pixels on a fader
+ *     or vu level scale.
+ */
+
+class AudioLevel
+{
+public:
+
+    static const float DB_FLOOR;
+
+    enum FaderType {
+	     ShortFader = 0, // -40 -> +6  dB
+              LongFader = 1, // -70 -> +10 dB
+            IEC268Meter = 2, // -70 ->  0  dB
+        IEC268LongMeter = 3, // -70 -> +10 dB (0dB aligns with LongFader)
+	   PreviewLevel = 4
+    };
+
+    static float multiplier_to_dB(float multiplier);
+    static float dB_to_multiplier(float dB);
+
+    static float fader_to_dB(int level, int maxLevel, FaderType type);
+    static int   dB_to_fader(float dB, int maxFaderLevel, FaderType type);
+
+    static float fader_to_multiplier(int level, int maxLevel, FaderType type);
+    static int   multiplier_to_fader(float multiplier, int maxFaderLevel,
+				     FaderType type);
+
+    // fast if "levels" doesn't change often -- for audio segment previews
+    static int   multiplier_to_preview(float multiplier, int levels);
+    static float preview_to_multiplier(int level, int levels);
+};
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/AudioPlaySource.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,53 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _AUDIO_PLAY_SOURCE_H_
+#define _AUDIO_PLAY_SOURCE_H_
+
+/**
+ * Simple interface for audio playback.  This should be all that the
+ * ViewManager needs to know about to synchronise with playback by
+ * sample frame, but it doesn't provide enough to determine what is
+ * actually being played or how.  See the audioio directory for a
+ * concrete subclass.
+ */
+
+class AudioPlaySource
+{
+public:
+    /**
+     * Start playing from the given frame.  If playback is already
+     * under way, reseek to the given frame and continue.
+     */
+    virtual void play(size_t startFrame) = 0;
+
+    /**
+     * Stop playback.
+     */
+    virtual void stop() = 0;
+
+    /**
+     * Return whether playback is currently supposed to be happening.
+     */
+    virtual bool isPlaying() const = 0;
+
+    /**
+     * Return the frame number that is currently expected to be coming
+     * out of the speakers.  (i.e. compensating for playback latency.)
+     */
+    virtual size_t getCurrentPlayingFrame() = 0;
+
+    /**
+     * Return the current (or thereabouts) output levels in the range
+     * 0.0 -> 1.0, for metering purposes.
+     */
+    virtual bool getOutputLevels(float &left, float &right) = 0;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Layer.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,39 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "Layer.h"
+#include "View.h"
+
+#include <iostream>
+
+Layer::Layer(View *w)
+{
+    m_view = w;
+
+    // Subclass must call this:
+//    w->addLayer(this);
+}
+
+Layer::~Layer()
+{
+    m_view->removeLayer(this);
+}
+
+void
+Layer::setObjectName(const QString &name)
+{
+    QObject::setObjectName(name);
+    emit layerNameChanged();
+}
+
+
+#ifdef INCLUDE_MOCFILES
+#include "Layer.moc.cpp"
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Layer.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,103 @@
+
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _VIEWER_H_
+#define _VIEWER_H_
+
+#include "PropertyContainer.h"
+
+#include <QObject>
+#include <QRect>
+
+class ZoomConstraint;
+class Model;
+class QPainter;
+class View;
+
+/**
+ * The base class for visual representations of the data found in a
+ * Model.  Layers are expected to be able to draw themselves onto a
+ * View, and may also be editable.
+ */
+
+class Layer : public QObject,
+	      public PropertyContainer
+{
+    Q_OBJECT
+
+public:
+    Layer(View *w);
+    virtual ~Layer();
+
+    virtual const Model *getModel() const = 0;
+    virtual const ZoomConstraint *getZoomConstraint() const { return 0; }
+    virtual void paint(QPainter &, QRect) const = 0;   
+
+    enum VerticalPosition {
+	PositionTop, PositionMiddle, PositionBottom
+    };
+    virtual VerticalPosition getPreferredTimeRulerPosition() const {
+	return PositionMiddle;
+    }
+    virtual VerticalPosition getPreferredFrameCountPosition() const {
+	return PositionBottom;
+    }
+
+    virtual QString getPropertyContainerName() const {
+	return objectName();
+    }
+
+    virtual int getVerticalScaleWidth(QPainter &) const { return 0; }
+    virtual void paintVerticalScale(QPainter &, QRect) const { }
+
+    virtual QRect getFeatureDescriptionRect(QPainter &, QPoint) const {
+	return QRect(0, 0, 0, 0);
+    }
+    virtual void paintLocalFeatureDescription(QPainter &, QRect, QPoint) const {
+    }
+
+    /**
+     * This should return true if the view can safely be scrolled
+     * automatically by the widget (simply copying the existing data
+     * and then refreshing the exposed area) without altering its
+     * meaning.  For the widget as a whole this is usually not
+     * possible because of invariant (non-scrolling) material
+     * displayed over the top, but the widget may be able to optimise
+     * scrolling better if it is known that individual views can be
+     * scrolled safely in this way.
+     */
+    virtual bool isLayerScrollable() const { return true; }
+
+    /**
+     * Return the proportion of background work complete in drawing
+     * this view, as a percentage -- in most cases this will be the
+     * value returned by pointer from a call to the underlying model's
+     * isReady(int *) call.  The widget may choose to show a progress
+     * meter if it finds that this returns < 100 at any given moment.
+     */
+    virtual int getCompletion() const { return 100; }
+
+    virtual void setObjectName(const QString &name);
+
+signals:
+    void modelChanged();
+    void modelCompletionChanged();
+    void modelChanged(size_t startFrame, size_t endFrame);
+    void modelReplaced();
+
+    void layerParametersChanged();
+    void layerNameChanged();
+
+protected:
+    View *m_view;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Model.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,11 @@
+
+#include "Model.h"
+
+const int Model::COMPLETION_UNKNOWN = -1;
+
+#ifdef INCLUDE_MOCFILES
+#ifdef INCLUDE_MOCFILES
+#include "Model.moc.cpp"
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Model.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,114 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _MODEL_H_
+#define _MODEL_H_
+
+#include <vector>
+#include <QObject>
+
+typedef std::vector<float> SampleBlock;
+
+/** 
+ * Model is the base class for all data models that represent any sort
+ * of data on a time scale based on an audio frame rate.
+ */
+
+class Model : virtual public QObject
+{
+    Q_OBJECT
+
+public:
+    /**
+     * Return true if the model was constructed successfully.  Classes
+     * that refer to the model should always test this before use.
+     */
+    virtual bool isOK() const = 0;
+
+    /**
+     * Return the first audio frame spanned by the model.
+     */
+    virtual size_t getStartFrame() const = 0;
+
+    /**
+     * Return the last audio frame spanned by the model.
+     */
+    virtual size_t getEndFrame() const = 0;
+
+    /**
+     * Return the frame rate in frames per second.
+     */
+    virtual size_t getSampleRate() const = 0;
+
+    /**
+     * Return a copy of this model.
+     *
+     * If the model is not editable, this may be effectively a shallow
+     * copy.  If the model is editable, however, this operation must
+     * properly copy all of the model's editable data.
+     *
+     * In general this operation is not useful for non-editable dense
+     * models such as waveforms, because there may be no efficient
+     * copy operation implemented -- for such models it is better not
+     * to copy at all.
+     *
+     * Caller owns the returned value.
+     */
+    virtual Model *clone() const = 0;
+    
+    /**
+     * Return true if the model has finished loading or calculating
+     * all its data, for a model that is capable of calculating in a
+     * background thread.  The default implementation is appropriate
+     * for a thread that does not background any work but carries out
+     * all its calculation from the constructor or accessors.
+     *
+     * If "completion" is non-NULL, this function should return
+     * through it an estimated percentage value showing how far
+     * through the background operation it thinks it is (for progress
+     * reporting).  If it has no way to calculate progress, it may
+     * return the special value COMPLETION_UNKNOWN.
+     */
+    virtual bool isReady(int *completion = 0) const {
+	bool ok = isOK();
+	if (completion) *completion = (ok ? 100 : 0);
+	return ok;
+    }
+    static const int COMPLETION_UNKNOWN;
+
+signals:
+    /**
+     * Emitted when a model has been edited (or more data retrieved
+     * from cache, in the case of a cached model that generates slowly)
+     */
+    void modelChanged();
+
+    /**
+     * Emitted when a model has been edited (or more data retrieved
+     * from cache, in the case of a cached model that generates slowly)
+     */
+    void modelChanged(size_t startFrame, size_t endFrame);
+
+    /**
+     * Emitted when some internal processing has advanced a stage, but
+     * the model has not changed externally.  Views should respond by
+     * updating any progress meters or other monitoring, but not
+     * refreshing the actual view.
+     */
+    void completionChanged();
+
+protected:
+    Model() { }
+
+    // Not provided.
+    Model(const Model &);
+    Model &operator=(const Model &); 
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/PlayParameters.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "PlayParameters.h"
+
+void
+PlayParameters::setPlayMuted(bool muted)
+{
+    m_playMuted = muted;
+    emit playParametersChanged();
+}
+
+
+void
+PlayParameters::setPlayPan(float pan)
+{
+    m_playPan = pan;
+    emit playParametersChanged();
+}
+
+
+void
+PlayParameters::setPlayGain(float gain)
+{
+    m_playGain = gain;
+    emit playParametersChanged();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/PlayParameters.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,44 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _PLAYABLE_H_
+#define _PLAYABLE_H_
+
+#include <QObject>
+
+class PlayParameters : virtual public QObject
+{
+    Q_OBJECT
+
+public:
+    PlayParameters() : m_playMuted(false), m_playPan(0.0), m_playGain(1.0) { }
+
+    virtual bool isPlayMuted() const { return m_playMuted; }
+    virtual void setPlayMuted(bool muted);
+    
+    virtual float getPlayPan() const { return m_playPan; } // -1.0 -> 1.0
+    virtual void setPlayPan(float pan);
+    
+    virtual float getPlayGain() const { return m_playGain; }
+    virtual void setPlayGain(float gain);
+
+signals:
+    void playParametersChanged();
+
+protected:
+    bool m_playMuted;
+    float m_playPan;
+    float m_playGain;
+};
+
+#endif
+
+    
+
+    
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Profiler.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,152 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam and Guillaume Laurent.
+*/
+
+#include <iostream>
+#include "Profiler.h"
+
+#include <vector>
+#include <algorithm>
+
+//#define NO_TIMING 1
+
+#ifdef NDEBUG
+#define NO_TIMING 1
+#endif
+
+using std::cerr;
+using std::endl;
+
+Profiles* Profiles::m_instance = 0;
+
+Profiles* Profiles::getInstance()
+{
+    if (!m_instance) m_instance = new Profiles();
+    
+    return m_instance;
+}
+
+Profiles::Profiles()
+{
+}
+
+Profiles::~Profiles()
+{
+    dump();
+}
+
+void Profiles::accumulate(const char* id, clock_t time, RealTime rt)
+{
+#ifndef NO_TIMING    
+    ProfilePair &pair(m_profiles[id]);
+    ++pair.first;
+    pair.second.first += time;
+    pair.second.second = pair.second.second + rt;
+
+    TimePair &timePair(m_lastCalls[id]);
+    timePair.first = time;
+    timePair.second = rt;
+#endif
+}
+
+void Profiles::dump()
+{
+#ifndef NO_TIMING    
+    cerr << "Profiles::dump() :\n";
+
+    // I'm finding these two confusing dumped out in random order,
+    // so I'm going to sort them alphabetically:
+
+    std::vector<const char *> profileNames;
+    for (ProfileMap::iterator i = m_profiles.begin();
+	 i != m_profiles.end(); ++i) {
+	profileNames.push_back((*i).first);
+    }
+
+    std::sort(profileNames.begin(), profileNames.end());
+
+    for (std::vector<const char *>::iterator i = profileNames.begin();
+	 i != profileNames.end(); ++i) {
+
+        cerr << "-> " << *i << ":  CPU: " 
+	     << m_profiles[*i].first << " calls, "
+	     << int((m_profiles[*i].second.first * 1000.0) / CLOCKS_PER_SEC) << "ms, "
+	     << (((double)m_profiles[*i].second.first * 1000000.0 /
+		  (double)m_profiles[*i].first) / CLOCKS_PER_SEC) << "us/call"
+	     << endl;
+
+        cerr << "-> " << *i << ": real: " 
+	     << m_profiles[*i].first << " calls, "
+	     << m_profiles[*i].second.second << ", "
+	     << (m_profiles[*i].second.second / m_profiles[*i].first)
+	     << "/call"
+	     << endl;
+
+	cerr << "-> " << *i << ": last:  CPU: "
+	     << int((m_lastCalls[*i].first * 1000.0) / CLOCKS_PER_SEC) << "ms, "
+	     << "   real: "
+	     << m_lastCalls[*i].second << endl;
+    }
+
+    cerr << "Profiles::dump() finished\n";
+#endif
+}
+
+Profiler::Profiler(const char* c, bool showOnDestruct)
+    : m_c(c),
+      m_showOnDestruct(showOnDestruct)
+{
+#ifndef NO_TIMING
+    m_startCPU = clock();
+
+    struct timeval tv;
+    (void)gettimeofday(&tv, 0);
+    m_startTime = RealTime(tv.tv_sec, tv.tv_usec * 1000);
+#endif
+}
+
+void
+Profiler::update()
+{
+#ifndef NO_TIMING
+    clock_t elapsedCPU = clock() - m_startCPU;
+
+    struct timeval tv;
+    (void)gettimeofday(&tv, 0);
+    RealTime elapsedTime = RealTime(tv.tv_sec, tv.tv_usec * 1000) - m_startTime;
+
+    cerr << "Profiler : id = " << m_c
+	 << " - elapsed so far = " << ((elapsedCPU * 1000) / CLOCKS_PER_SEC)
+	 << "ms CPU, " << elapsedTime << " real" << endl;
+#endif
+}    
+
+Profiler::~Profiler()
+{
+#ifndef NO_TIMING
+    clock_t elapsedCPU = clock() - m_startCPU;
+
+    struct timeval tv;
+    (void)gettimeofday(&tv, 0);
+    RealTime elapsedTime = RealTime(tv.tv_sec, tv.tv_usec * 1000) - m_startTime;
+
+    Profiles::getInstance()->accumulate(m_c, elapsedCPU, elapsedTime);
+
+    if (m_showOnDestruct)
+        cerr << "Profiler : id = " << m_c
+             << " - elapsed = " << ((elapsedCPU * 1000) / CLOCKS_PER_SEC)
+	     << "ms CPU, " << elapsedTime << " real" << endl;
+#endif
+}
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Profiler.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,76 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam and Guillaume Laurent.
+*/
+
+
+#ifndef _PROFILER_H_
+#define _PROFILER_H_
+
+#include "System.h"
+
+#include <ctime>
+#include <sys/time.h>
+#include <map>
+
+#include "RealTime.h"
+
+
+/**
+ * Profiling classes
+ */
+
+/**
+ * The class holding all profiling data
+ *
+ * This class is a singleton
+ */
+class Profiles
+{
+public:
+    static Profiles* getInstance();
+    ~Profiles();
+
+    void accumulate(const char* id, clock_t time, RealTime rt);
+    void dump();
+
+protected:
+    Profiles();
+
+    typedef std::pair<clock_t, RealTime> TimePair;
+    typedef std::pair<int, TimePair> ProfilePair;
+    typedef std::map<const char *, ProfilePair> ProfileMap;
+    typedef std::map<const char *, TimePair> LastCallMap;
+    ProfileMap m_profiles;
+    LastCallMap m_lastCalls;
+
+    static Profiles* m_instance;
+};
+
+class Profiler
+{
+public:
+    Profiler(const char*, bool showOnDestruct = false);
+    ~Profiler();
+
+    void update();
+
+protected:
+    const char* m_c;
+    clock_t m_startCPU;
+    RealTime m_startTime;
+    bool m_showOnDestruct;
+};
+ 
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/PropertyContainer.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,48 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "PropertyContainer.h"
+
+PropertyContainer::PropertyList
+PropertyContainer::getProperties() const
+{
+    return PropertyList();
+}
+
+PropertyContainer::PropertyType
+PropertyContainer::getPropertyType(const PropertyName &) const
+{
+    return InvalidProperty;
+}
+
+QString
+PropertyContainer::getPropertyGroupName(const PropertyName &) const
+{
+    return QString();
+}
+
+int
+PropertyContainer::getPropertyRangeAndValue(const PropertyName &, int *min, int *max) const
+{
+    if (min) *min = 0;
+    if (max) *max = 0;
+    return 0;
+}
+
+QString
+PropertyContainer::getPropertyValueLabel(const PropertyName &, int) const
+{
+    return QString();
+}
+
+void
+PropertyContainer::setProperty(const PropertyName &, int) 
+{
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/PropertyContainer.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,86 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _PROPERTY_CONTAINER_H_
+#define _PROPERTY_CONTAINER_H_
+
+#include <QString>
+#include <QObject>
+#include <vector>
+
+class Playable;
+
+class PropertyContainer
+{
+public:
+    typedef QString PropertyName;
+    typedef std::vector<PropertyName> PropertyList;
+    
+    enum PropertyType {
+	ToggleProperty, // on or off
+	RangeProperty, // range of integers
+	ValueProperty, // range of integers given string labels
+	ColourProperty, // colours, get/set as qRgb
+	InvalidProperty, // property not found!
+    };
+
+    /**
+     * Get a list of the names of all the supported properties on this
+     * container.  Note that these should already have been
+     * internationalized with a call to tr() or equivalent.  If the
+     * container needs to test for equality with string literals
+     * subsequently, it must be sure to call tr() again on the strings
+     * in order to ensure they match.
+     */
+    virtual PropertyList getProperties() const;
+
+    /**
+     * Return the type of the given property, or InvalidProperty if
+     * the property is not supported on this container.
+     */
+    virtual PropertyType getPropertyType(const PropertyName &) const;
+
+    /**
+     * If this property has something in common with other properties
+     * on this container, return a name that can be used to group them
+     * (in order to save screen space, for example).  e.g. "Window
+     * Type" and "Window Size" might both have a group name of "Window".
+     * If this property is not groupable, return the empty string.
+     */
+    virtual QString getPropertyGroupName(const PropertyName &) const;
+
+    /**
+     * Return the minimum and maximum values for the given property
+     * and its current value in this container.  Min and/or max may be
+     * passed as NULL if their values are not required.
+     */
+    virtual int getPropertyRangeAndValue(const PropertyName &,
+					 int *min, int *max) const;
+
+    /**
+     * If the given property is a ValueProperty, return the display
+     * label to be used for the given value for that property.
+     */
+    virtual QString getPropertyValueLabel(const PropertyName &,
+					  int value) const;
+
+    /**
+     * Set a property.  This is used for all property types.  For
+     * boolean properties, zero is false and non-zero true; for
+     * colours, the integer value should be treated as a qRgb.
+     */
+    virtual void setProperty(const PropertyName &, int value);
+
+    virtual QString getPropertyContainerName() const = 0;
+    virtual QString getPropertyContainerIconName() const = 0;
+
+    Playable *getPlayable() const { return 0; }
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/RealTime.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,200 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include <iostream>
+
+#if (__GNUC__ < 3)
+#include <strstream>
+#define stringstream strstream
+#else
+#include <sstream>
+#endif
+
+using std::cerr;
+using std::endl;
+
+#include "base/RealTime.h"
+
+// A RealTime consists of two ints that must be at least 32 bits each.
+// A signed 32-bit int can store values exceeding +/- 2 billion.  This
+// means we can safely use our lower int for nanoseconds, as there are
+// 1 billion nanoseconds in a second and we need to handle double that
+// because of the implementations of addition etc that we use.
+//
+// The maximum valid RealTime on a 32-bit system is somewhere around
+// 68 years: 999999999 nanoseconds longer than the classic Unix epoch.
+
+#define ONE_BILLION 1000000000
+
+RealTime::RealTime(int s, int n) :
+    sec(s), nsec(n)
+{
+    if (sec == 0) {
+	while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; }
+	while (nsec >=  ONE_BILLION) { nsec -= ONE_BILLION; ++sec; }
+    } else if (sec < 0) {
+	while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; }
+	while (nsec > 0)             { nsec -= ONE_BILLION; ++sec; }
+    } else { 
+	while (nsec >=  ONE_BILLION) { nsec -= ONE_BILLION; ++sec; }
+	while (nsec < 0)             { nsec += ONE_BILLION; --sec; }
+    }
+}
+
+
+std::ostream &operator<<(std::ostream &out, const RealTime &rt)
+{
+    if (rt < RealTime::zeroTime) {
+	out << "-";
+    } else {
+	out << " ";
+    }
+
+    int s = (rt.sec < 0 ? -rt.sec : rt.sec);
+    int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec);
+
+    out << s << ".";
+
+    int nn(n);
+    if (nn == 0) out << "00000000";
+    else while (nn < (ONE_BILLION / 10)) {
+	out << "0";
+	nn *= 10;
+    }
+    
+    out << n << "R";
+    return out;
+}
+
+std::string
+RealTime::toString() const
+{
+    std::stringstream out;
+    out << *this;
+    
+#if (__GNUC__ < 3)
+    out << std::ends;
+#endif
+
+    std::string s = out.str();
+
+    // remove trailing R
+    return s.substr(0, s.length() - 1);
+}
+
+std::string
+RealTime::toText(bool fixedDp) const
+{
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toText();
+
+    std::stringstream out;
+
+    if (sec >= 3600) {
+	out << (sec / 3600) << ":";
+    }
+
+    if (sec >= 60) {
+	out << (sec % 3600) / 60 << ":";
+    }
+
+    if (sec >= 10) {
+	out << ((sec % 60) / 10);
+    }
+
+    out << (sec % 10);
+    
+    int ms = msec();
+
+    if (ms != 0) {
+	out << ".";
+	out << (ms / 100);
+	ms = ms % 100;
+	if (ms != 0) {
+	    out << (ms / 10);
+	    ms = ms % 10;
+	} else if (fixedDp) {
+	    out << "0";
+	}
+	if (ms != 0) {
+	    out << ms;
+	} else if (fixedDp) {
+	    out << "0";
+	}
+    } else if (fixedDp) {
+	out << ".000";
+    }
+	
+#if (__GNUC__ < 3)
+    out << std::ends;
+#endif
+
+    std::string s = out.str();
+
+    return s;
+}
+
+
+RealTime
+RealTime::operator/(int d) const
+{
+    int secdiv = sec / d;
+    int secrem = sec % d;
+
+    double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d;
+    
+    return RealTime(secdiv, int(nsecdiv + 0.5));
+}
+
+double 
+RealTime::operator/(const RealTime &r) const
+{
+    double lTotal = double(sec) * ONE_BILLION + double(nsec);
+    double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec);
+    
+    if (rTotal == 0) return 0.0;
+    else return lTotal/rTotal;
+}
+
+long
+RealTime::realTime2Frame(const RealTime &time, unsigned int sampleRate)
+{
+    if (time < zeroTime) return -realTime2Frame(-time, sampleRate);
+
+    // We like integers.  The last term is always zero unless the
+    // sample rate is greater than 1MHz, but hell, you never know...
+
+    long frame =
+	time.sec * sampleRate +
+	(time.msec() * sampleRate) / 1000 +
+	((time.usec() - 1000 * time.msec()) * sampleRate) / 1000000 +
+	((time.nsec - 1000 * time.usec()) * sampleRate) / 1000000000;
+
+    return frame;
+}
+
+RealTime
+RealTime::frame2RealTime(long frame, unsigned int sampleRate)
+{
+    if (frame < 0) return -frame2RealTime(-frame, sampleRate);
+
+    RealTime rt;
+    rt.sec = frame / long(sampleRate);
+    frame -= rt.sec * long(sampleRate);
+    rt.nsec = (int)(((float(frame) * 1000000) / long(sampleRate)) * 1000);
+    return rt;
+}
+
+const RealTime RealTime::zeroTime(0,0);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/RealTime.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,109 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _REAL_TIME_H_
+#define _REAL_TIME_H_
+
+#include <iostream>
+#include <string>
+
+/**
+ * RealTime represents time values to nanosecond precision
+ * with accurate arithmetic and frame-rate conversion functions.
+ */
+
+struct RealTime
+{
+    int sec;
+    int nsec;
+
+    int usec() const { return nsec / 1000; }
+    int msec() const { return nsec / 1000000; }
+
+    RealTime(): sec(0), nsec(0) {}
+    RealTime(int s, int n);
+
+    RealTime(const RealTime &r) :
+	sec(r.sec), nsec(r.nsec) { }
+
+    RealTime &operator=(const RealTime &r) {
+	sec = r.sec; nsec = r.nsec; return *this;
+    }
+
+    RealTime operator+(const RealTime &r) const {
+	return RealTime(sec + r.sec, nsec + r.nsec);
+    }
+    RealTime operator-(const RealTime &r) const {
+	return RealTime(sec - r.sec, nsec - r.nsec);
+    }
+    RealTime operator-() const {
+	return RealTime(-sec, -nsec);
+    }
+
+    bool operator <(const RealTime &r) const {
+	if (sec == r.sec) return nsec < r.nsec;
+	else return sec < r.sec;
+    }
+
+    bool operator >(const RealTime &r) const {
+	if (sec == r.sec) return nsec > r.nsec;
+	else return sec > r.sec;
+    }
+
+    bool operator==(const RealTime &r) const {
+        return (sec == r.sec && nsec == r.nsec);
+    }
+ 
+    bool operator!=(const RealTime &r) const {
+        return !(r == *this);
+    }
+ 
+    bool operator>=(const RealTime &r) const {
+        if (sec == r.sec) return nsec >= r.nsec;
+        else return sec >= r.sec;
+    }
+
+    bool operator<=(const RealTime &r) const {
+        if (sec == r.sec) return nsec <= r.nsec;
+        else return sec <= r.sec;
+    }
+
+    RealTime operator/(int d) const;
+
+    // Find the fractional difference between times
+    //
+    double operator/(const RealTime &r) const;
+
+    // Return a human-readable debug-type string to full precision
+    // (probably not a format to show to a user directly)
+    // 
+    std::string toString() const;
+
+    // Return a user-readable string to the nearest millisecond
+    // in a form like HH:MM:SS.mmm
+    //
+    std::string toText(bool fixedDp = false) const;
+
+    // Convenience functions for handling sample frames
+    //
+    static long realTime2Frame(const RealTime &r, unsigned int sampleRate);
+    static RealTime frame2RealTime(long frame, unsigned int sampleRate);
+
+    static const RealTime zeroTime;
+};
+
+std::ostream &operator<<(std::ostream &out, const RealTime &rt);
+    
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/RingBuffer.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,541 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _RINGBUFFER_H_
+#define _RINGBUFFER_H_
+
+#include <sys/types.h>
+
+#include "System.h"
+#include "Scavenger.h"
+
+//#define DEBUG_RINGBUFFER 1
+
+#ifdef DEBUG_RINGBUFFER
+#include <iostream>
+#endif
+
+/**
+ * RingBuffer implements a lock-free ring buffer for one writer and N
+ * readers, that is to be used to store a sample type T.
+ *
+ * For efficiency, RingBuffer frequently initialises samples by
+ * writing zeroes into their memory space, so T should normally be a
+ * simple type that can safely be set to zero using memset.
+ */
+
+template <typename T, int N = 1>
+class RingBuffer
+{
+public:
+    /**
+     * Create a ring buffer with room to write n samples.
+     *
+     * Note that the internal storage size will actually be n+1
+     * samples, as one element is unavailable for administrative
+     * reasons.  Since the ring buffer performs best if its size is a
+     * power of two, this means n should ideally be some power of two
+     * minus one.
+     */
+    RingBuffer(size_t n);
+
+    virtual ~RingBuffer();
+
+    /**
+     * Return the total capacity of the ring buffer in samples.
+     * (This is the argument n passed to the constructor.)
+     */
+    size_t getSize() const;
+
+    /**
+     * Resize the ring buffer.  This also empties it.  Actually swaps
+     * in a new, larger buffer; the old buffer is scavenged after a
+     * seemly delay.  Should be called from the write thread.
+     */
+    void resize(size_t newSize);
+
+    /**
+     * Lock the ring buffer into physical memory.  Returns true
+     * for success.
+     */
+    bool mlock();
+
+    /**
+     * Reset read and write pointers, thus emptying the buffer.
+     * Should be called from the write thread.
+     */
+    void reset();
+
+    /**
+     * Return the amount of data available for reading by reader R, in
+     * samples.
+     */
+    size_t getReadSpace(int R = 0) const;
+
+    /**
+     * Return the amount of space available for writing, in samples.
+     */
+    size_t getWriteSpace() const;
+
+    /**
+     * Read n samples from the buffer, for reader R.  If fewer than n
+     * are available, the remainder will be zeroed out.  Returns the
+     * number of samples actually read.
+     */
+    size_t read(T *destination, size_t n, int R = 0);
+
+    /**
+     * Read n samples from the buffer, for reader R, adding them to
+     * the destination.  If fewer than n are available, the remainder
+     * will be left alone.  Returns the number of samples actually
+     * read.
+     */
+    size_t readAdding(T *destination, size_t n, int R = 0);
+
+    /**
+     * Read one sample from the buffer, for reader R.  If no sample is
+     * available, this will silently return zero.  Calling this
+     * repeatedly is obviously slower than calling read once, but it
+     * may be good enough if you don't want to allocate a buffer to
+     * read into.
+     */
+    T readOne(int R = 0);
+
+    /**
+     * Read n samples from the buffer, if available, for reader R,
+     * without advancing the read pointer -- i.e. a subsequent read()
+     * or skip() will be necessary to empty the buffer.  If fewer than
+     * n are available, the remainder will be zeroed out.  Returns the
+     * number of samples actually read.
+     */
+    size_t peek(T *destination, size_t n, int R = 0) const;
+
+    /**
+     * Read one sample from the buffer, if available, without
+     * advancing the read pointer -- i.e. a subsequent read() or
+     * skip() will be necessary to empty the buffer.  Returns zero if
+     * no sample was available.
+     */
+    T peekOne(int R = 0) const;
+
+    /**
+     * Pretend to read n samples from the buffer, for reader R,
+     * without actually returning them (i.e. discard the next n
+     * samples).  Returns the number of samples actually available for
+     * discarding.
+     */
+    size_t skip(size_t n, int R = 0);
+
+    /**
+     * Write n samples to the buffer.  If insufficient space is
+     * available, not all samples may actually be written.  Returns
+     * the number of samples actually written.
+     */
+    size_t write(const T *source, size_t n);
+
+    /**
+     * Write n zero-value samples to the buffer.  If insufficient
+     * space is available, not all zeros may actually be written.
+     * Returns the number of zeroes actually written.
+     */
+    size_t zero(size_t n);
+
+protected:
+    T               *m_buffer;
+    volatile size_t  m_writer;
+    volatile size_t  m_readers[N];
+    size_t           m_size;
+    bool             m_mlocked;
+
+    static Scavenger<ScavengerArrayWrapper<T> > m_scavenger;
+
+private:
+    RingBuffer(const RingBuffer &); // not provided
+    RingBuffer &operator=(const RingBuffer &); // not provided
+};
+
+template <typename T, int N>
+Scavenger<ScavengerArrayWrapper<T> > RingBuffer<T, N>::m_scavenger;
+
+template <typename T, int N>
+RingBuffer<T, N>::RingBuffer(size_t n) :
+    m_buffer(new T[n + 1]),
+    m_writer(0),
+    m_size(n + 1),
+    m_mlocked(false)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::RingBuffer(" << n << ")" << std::endl;
+#endif
+
+    for (int i = 0; i < N; ++i) m_readers[i] = 0;
+
+    m_scavenger.scavenge();
+}
+
+template <typename T, int N>
+RingBuffer<T, N>::~RingBuffer()
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::~RingBuffer" << std::endl;
+#endif
+
+    if (m_mlocked) {
+	MUNLOCK((void *)m_buffer, m_size * sizeof(T));
+    }
+    delete[] m_buffer;
+
+    m_scavenger.scavenge();
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::getSize() const
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getSize(): " << m_size-1 << std::endl;
+#endif
+
+    return m_size - 1;
+}
+
+template <typename T, int N>
+void
+RingBuffer<T, N>::resize(size_t newSize)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::resize(" << newSize << ")" << std::endl;
+#endif
+
+    m_scavenger.scavenge();
+
+    if (m_mlocked) {
+	MUNLOCK((void *)m_buffer, m_size * sizeof(T));
+    }
+
+    m_scavenger.claim(new ScavengerArrayWrapper<T>(m_buffer));
+
+    reset();
+    m_buffer = new T[newSize + 1];
+    m_size = newSize + 1;
+
+    if (m_mlocked) {
+	if (MLOCK((void *)m_buffer, m_size * sizeof(T))) {
+	    m_mlocked = false;
+	}
+    }
+}
+
+template <typename T, int N>
+bool
+RingBuffer<T, N>::mlock()
+{
+    if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false;
+    m_mlocked = true;
+    return true;
+}
+
+template <typename T, int N>
+void
+RingBuffer<T, N>::reset()
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::reset" << std::endl;
+#endif
+
+    m_writer = 0;
+    for (int i = 0; i < N; ++i) m_readers[i] = 0;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::getReadSpace(int R) const
+{
+    size_t writer = m_writer;
+    size_t reader = m_readers[R];
+    size_t space = 0;
+
+    if (writer > reader) space = writer - reader;
+    else space = ((writer + m_size) - reader) % m_size;
+
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getReadSpace(" << R << "): " << space << std::endl;
+#endif
+
+    return space;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::getWriteSpace() const
+{
+    size_t space = 0;
+    for (int i = 0; i < N; ++i) {
+	size_t here = (m_readers[i] + m_size - m_writer - 1) % m_size;
+	if (i == 0 || here < space) space = here;
+    }
+
+#ifdef DEBUG_RINGBUFFER
+    size_t rs(getReadSpace()), rp(m_readers[0]);
+
+    std::cerr << "RingBuffer: write space " << space << ", read space "
+	      << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl;
+    std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl;
+#endif
+
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::getWriteSpace(): " << space << std::endl;
+#endif
+
+    return space;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::read(T *destination, size_t n, int R)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read(dest, " << n << ", " << R << ")" << std::endl;
+#endif
+
+    size_t available = getReadSpace(R);
+    if (n > available) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: Only " << available << " samples available"
+		  << std::endl;
+#endif
+	memset(destination + available, 0, (n - available) * sizeof(T));
+	n = available;
+    }
+    if (n == 0) return n;
+
+    size_t here = m_size - m_readers[R];
+    if (here >= n) {
+	memcpy(destination, m_buffer + m_readers[R], n * sizeof(T));
+    } else {
+	memcpy(destination, m_buffer + m_readers[R], here * sizeof(T));
+	memcpy(destination + here, m_buffer, (n - here) * sizeof(T));
+    }
+
+    m_readers[R] = (m_readers[R] + n) % m_size;
+
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl;
+#endif
+
+    return n;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::readAdding(T *destination, size_t n, int R)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readAdding(dest, " << n << ", " << R << ")" << std::endl;
+#endif
+
+    size_t available = getReadSpace(R);
+    if (n > available) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: Only " << available << " samples available"
+		  << std::endl;
+#endif
+	n = available;
+    }
+    if (n == 0) return n;
+
+    size_t here = m_size - m_readers[R];
+
+    if (here >= n) {
+	for (size_t i = 0; i < n; ++i) {
+	    destination[i] += (m_buffer + m_readers[R])[i];
+	}
+    } else {
+	for (size_t i = 0; i < here; ++i) {
+	    destination[i] += (m_buffer + m_readers[R])[i];
+	}
+	for (size_t i = 0; i < (n - here); ++i) {
+	    destination[i + here] += m_buffer[i];
+	}
+    }
+
+    m_readers[R] = (m_readers[R] + n) % m_size;
+    return n;
+}
+
+template <typename T, int N>
+T
+RingBuffer<T, N>::readOne(int R)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::readOne(" << R << ")" << std::endl;
+#endif
+
+    if (m_writer == m_readers[R]) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: No sample available"
+		  << std::endl;
+#endif
+	T t;
+	memset(&t, 0, sizeof(T));
+	return t;
+    }
+    T value = m_buffer[m_readers[R]];
+    if (++m_readers[R] == m_size) m_readers[R] = 0;
+    return value;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::peek(T *destination, size_t n, int R) const
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(dest, " << n << ", " << R << ")" << std::endl;
+#endif
+
+    size_t available = getReadSpace(R);
+    if (n > available) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: Only " << available << " samples available"
+		  << std::endl;
+#endif
+	memset(destination + available, 0, (n - available) * sizeof(T));
+	n = available;
+    }
+    if (n == 0) return n;
+
+    size_t here = m_size - m_readers[R];
+    if (here >= n) {
+	memcpy(destination, m_buffer + m_readers[R], n * sizeof(T));
+    } else {
+	memcpy(destination, m_buffer + m_readers[R], here * sizeof(T));
+	memcpy(destination + here, m_buffer, (n - here) * sizeof(T));
+    }
+
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek: read " << n << std::endl;
+#endif
+
+    return n;
+}
+
+template <typename T, int N>
+T
+RingBuffer<T, N>::peekOne(int R) const
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::peek(" << R << ")" << std::endl;
+#endif
+
+    if (m_writer == m_readers[R]) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: No sample available"
+		  << std::endl;
+#endif
+	T t;
+	memset(&t, 0, sizeof(T));
+	return t;
+    }
+    T value = m_buffer[m_readers[R]];
+    return value;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::skip(size_t n, int R)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::skip(" << n << ", " << R << ")" << std::endl;
+#endif
+
+    size_t available = getReadSpace(R);
+    if (n > available) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: Only " << available << " samples available"
+		  << std::endl;
+#endif
+	n = available;
+    }
+    if (n == 0) return n;
+    m_readers[R] = (m_readers[R] + n) % m_size;
+    return n;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::write(const T *source, size_t n)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write(" << n << ")" << std::endl;
+#endif
+
+    size_t available = getWriteSpace();
+    if (n > available) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: Only room for " << available << " samples"
+		  << std::endl;
+#endif
+	n = available;
+    }
+    if (n == 0) return n;
+
+    size_t here = m_size - m_writer;
+    if (here >= n) {
+	memcpy(m_buffer + m_writer, source, n * sizeof(T));
+    } else {
+	memcpy(m_buffer + m_writer, source, here * sizeof(T));
+	memcpy(m_buffer, source + here, (n - here) * sizeof(T));
+    }
+
+    m_writer = (m_writer + n) % m_size;
+
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl;
+#endif
+
+    return n;
+}
+
+template <typename T, int N>
+size_t
+RingBuffer<T, N>::zero(size_t n)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T," << N << ">[" << this << "]::zero(" << n << ")" << std::endl;
+#endif
+
+    size_t available = getWriteSpace();
+    if (n > available) {
+#ifdef DEBUG_RINGBUFFER
+	std::cerr << "WARNING: Only room for " << available << " samples"
+		  << std::endl;
+#endif
+	n = available;
+    }
+    if (n == 0) return n;
+
+    size_t here = m_size - m_writer;
+    if (here >= n) {
+	memset(m_buffer + m_writer, 0, n * sizeof(T));
+    } else {
+	memset(m_buffer + m_writer, 0, here * sizeof(T));
+	memset(m_buffer, 0, (n - here) * sizeof(T));
+    }
+
+    m_writer = (m_writer + n) % m_size;
+    return n;
+}
+
+#endif // _RINGBUFFER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Scavenger.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,145 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _SCAVENGER_H_
+#define _SCAVENGER_H_
+
+#include "System.h"
+
+#include <vector>
+#include <sys/time.h>
+
+/**
+ * A very simple class that facilitates running things like plugins
+ * without locking, by collecting unwanted objects and deleting them
+ * after a delay so as to be sure nobody's in the middle of using
+ * them.  Requires scavenge() to be called regularly from a non-RT
+ * thread.
+ *
+ * This is currently not at all suitable for large numbers of objects
+ * -- it's just a quick hack for use with things like plugins.
+ */
+
+template <typename T>
+class Scavenger
+{
+public:
+    Scavenger(int sec = 2, int defaultObjectListSize = 200);
+
+    /**
+     * Call from an RT thread etc., to pass ownership of t to us.
+     * Only one thread should be calling this on any given scavenger.
+     */
+    void claim(T *t);
+
+    /**
+     * Call from a non-RT thread.
+     * Only one thread should be calling this on any given scavenger.
+     */
+    void scavenge();
+
+protected:
+    typedef std::pair<T *, int> ObjectTimePair;
+    typedef std::vector<ObjectTimePair> ObjectTimeList;
+    ObjectTimeList m_objects;
+    int m_sec;
+
+    unsigned int m_claimed;
+    unsigned int m_scavenged;
+};
+
+/**
+ * A wrapper to permit arrays to be scavenged.
+ */
+
+template <typename T>
+class ScavengerArrayWrapper
+{
+public:
+    ScavengerArrayWrapper(T *array) : m_array(array) { }
+    ~ScavengerArrayWrapper() { delete[] m_array; }
+
+private:
+    T *m_array;
+};
+
+
+template <typename T>
+Scavenger<T>::Scavenger(int sec, int defaultObjectListSize) :
+    m_objects(ObjectTimeList(defaultObjectListSize)),
+    m_sec(sec),
+    m_claimed(0),
+    m_scavenged(0)
+{
+}
+
+template <typename T>
+void
+Scavenger<T>::claim(T *t)
+{
+//    std::cerr << "Scavenger::claim(" << t << ")" << std::endl;
+
+    struct timeval tv;
+    (void)gettimeofday(&tv, 0);
+    int sec = tv.tv_sec;
+
+    for (size_t i = 0; i < m_objects.size(); ++i) {
+	ObjectTimePair &pair = m_objects[i];
+	if (pair.first == 0) {
+	    pair.second = sec;
+	    pair.first = t;
+	    ++m_claimed;
+	    return;
+	}
+    }
+
+    // Oh no -- run out of slots!  Warn and discard something at
+    // random (without deleting it -- it's probably safer to leak).
+
+    for (size_t i = 0; i < m_objects.size(); ++i) {
+	ObjectTimePair &pair = m_objects[i];
+	if (pair.first != 0) {
+	    pair.second = sec;
+	    pair.first = t;
+	    ++m_claimed;
+	    ++m_scavenged;
+	}
+    }
+}
+
+template <typename T>
+void
+Scavenger<T>::scavenge()
+{
+//    std::cerr << "Scavenger::scavenge: scavenged " << m_scavenged << ", claimed " << m_claimed << std::endl;
+
+    if (m_scavenged >= m_claimed) return;
+    
+    struct timeval tv;
+    (void)gettimeofday(&tv, 0);
+    int sec = tv.tv_sec;
+
+    for (size_t i = 0; i < m_objects.size(); ++i) {
+	ObjectTimePair &pair = m_objects[i];
+	if (pair.first != 0 && pair.second + m_sec < sec) {
+	    T *ot = pair.first;
+	    pair.first = 0;
+	    delete ot;
+	    ++m_scavenged;
+	}
+    }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/System.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,30 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "System.h"
+
+#ifdef _WIN32
+
+extern "C" {
+
+void gettimeofday(struct timeval *tv, void *tz)
+{
+    union { 
+	long long ns100;  
+	FILETIME ft; 
+    } now; 
+    
+    GetSystemTimeAsFileTime(&now.ft); 
+    tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL); 
+    tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); 
+}
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/System.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,50 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _SYSTEM_H_
+#define _SYSTEM_H_
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include <malloc.h>
+
+#define MLOCK(a,b)   1
+#define MUNLOCK(a,b) 1
+
+#define DLOPEN(a,b)  LoadLibrary((a).toStdWString().c_str())
+#define DLSYM(a,b)   GetProcAddress((HINSTANCE)(a),(b))
+#define DLCLOSE(a)   FreeLibrary((HINSTANCE)(a))
+#define DLERROR()    ""
+
+#define PLUGIN_GLOB  "*.dll"
+
+extern "C" {
+void gettimeofday(struct timeval *p, void *tz);
+}
+
+#else
+
+#include <sys/mman.h>
+#include <dlfcn.h>
+
+#define MLOCK(a,b)   ::mlock((a),(b))
+#define MUNLOCK(a,b) ::munlock((a),(b))
+
+#define DLOPEN(a,b)  dlopen((a).toStdString().c_str(),(b))
+#define DLSYM(a,b)   dlsym((a),(b))
+#define DLCLOSE(a)   dlclose((a))
+#define DLERROR()    dlerror()
+
+#define PLUGIN_GLOB  "*.so"
+
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/View.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,956 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "base/View.h"
+#include "base/ViewManager.h"
+#include "base/Layer.h"
+#include "base/Model.h"
+#include "base/ZoomConstraint.h"
+#include "base/Profiler.h"
+
+#include "layer/TimeRulerLayer.h" //!!! damn, shouldn't be including that here
+
+#include <QPainter>
+#include <QPaintEvent>
+#include <QRect>
+#include <QApplication>
+
+#include <iostream>
+
+//#define DEBUG_VIEW_WIDGET_PAINT 1
+
+using std::cerr;
+using std::endl;
+
+View::View(QWidget *w, bool showProgress) :
+    QFrame(w),
+    m_centreFrame(0),
+    m_zoomLevel(1024),
+    m_newModel(true),
+    m_followPan(true),
+    m_followZoom(true),
+    m_followPlay(PlaybackScrollPage),
+    m_lightBackground(true),
+    m_showProgress(showProgress),
+    m_cache(0),
+    m_cacheCentreFrame(0),
+    m_cacheZoomLevel(1024),
+    m_deleting(false),
+    m_manager(0)
+{
+//    QWidget::setAttribute(Qt::WA_PaintOnScreen);
+}
+
+View::~View()
+{
+    m_deleting = true;
+
+    for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	delete *i;
+    }
+}
+
+PropertyContainer::PropertyList
+View::getProperties() const
+{
+    PropertyList list;
+    list.push_back(tr("Global Scroll"));
+    list.push_back(tr("Global Zoom"));
+    list.push_back(tr("Follow Playback"));
+    return list;
+}
+
+PropertyContainer::PropertyType
+View::getPropertyType(const PropertyName &name) const
+{
+    if (name == tr("Global Scroll")) return ToggleProperty;
+    if (name == tr("Global Zoom")) return ToggleProperty;
+    if (name == tr("Follow Playback")) return ValueProperty;
+    return InvalidProperty;
+}
+
+int
+View::getPropertyRangeAndValue(const PropertyName &name,
+				       int *min, int *max) const
+{
+    if (name == tr("Global Scroll")) return m_followPan;
+    if (name == tr("Global Zoom")) return m_followZoom;
+    if (name == tr("Follow Playback")) { *min = 0; *max = 2; return int(m_followPlay); }
+    return PropertyContainer::getPropertyRangeAndValue(name, min, max);
+}
+
+QString
+View::getPropertyValueLabel(const PropertyName &name,
+				  int value) const
+{
+    if (name == tr("Follow Playback")) {
+	switch (value) {
+	default:
+	case 0: return tr("Scroll");
+	case 1: return tr("Page");
+	case 2: return tr("Off");
+	}
+    }
+    return tr("<unknown>");
+}
+
+void
+View::setProperty(const PropertyName &name, int value)
+{
+    if (name == tr("Global Scroll")) {
+	setFollowGlobalPan(value != 0);
+    } else if (name == tr("Global Zoom")) {
+	setFollowGlobalZoom(value != 0);
+    } else if (name == tr("Follow Playback")) {
+	switch (value) {
+	default:
+	case 0: setPlaybackFollow(PlaybackScrollContinuous); break;
+	case 1: setPlaybackFollow(PlaybackScrollPage); break;
+	case 2: setPlaybackFollow(PlaybackIgnore); break;
+	}
+    }
+}
+
+size_t
+View::getPropertyContainerCount() const
+{
+    return m_layers.size() + 1; // the 1 is for me
+}
+
+const PropertyContainer *
+View::getPropertyContainer(size_t i) const
+{
+    return (const PropertyContainer *)(((View *)this)->
+				       getPropertyContainer(i));
+}
+
+PropertyContainer *
+View::getPropertyContainer(size_t i)
+{
+    if (i == 0) return this;
+    return m_layers[i-1];
+}
+
+void
+View::propertyContainerSelected(PropertyContainer *pc)
+{
+    if (pc == this) return;
+
+    delete m_cache;
+    m_cache = 0;
+
+    Layer *selectedLayer = 0;
+
+    for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	if (*i == pc) {
+	    selectedLayer = *i;
+	    m_layers.erase(i);
+	    break;
+	}
+    }
+
+    if (selectedLayer) {
+	m_layers.push_back(selectedLayer);
+	update();
+    }
+}
+
+long
+View::getStartFrame() const
+{
+    size_t w2 = (width() / 2) * m_zoomLevel;
+    size_t frame = m_centreFrame;
+    if (frame >= w2) {
+	frame -= w2;
+	return (frame / m_zoomLevel * m_zoomLevel);
+    } else {
+	frame = w2 - frame;
+	frame = frame / m_zoomLevel * m_zoomLevel;
+	return -(long)frame - m_zoomLevel;
+    }
+}
+
+size_t
+View::getEndFrame() const
+{
+    return getStartFrame() + (width() * m_zoomLevel) - 1;
+}
+
+void
+View::setStartFrame(long f)
+{
+    setCentreFrame(f + m_zoomLevel * (width() / 2));
+}
+
+void
+View::setCentreFrame(size_t f, bool e)
+{
+    if (m_centreFrame != f) {
+
+	int formerPixel = m_centreFrame / m_zoomLevel;
+
+	m_centreFrame = f;
+
+	int newPixel = m_centreFrame / m_zoomLevel;
+	
+	if (newPixel != formerPixel) {
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	    std::cout << "View(" << this << ")::setCentreFrame: newPixel " << newPixel << ", formerPixel " << formerPixel << std::endl;
+#endif
+	    update();
+	}
+
+	if (e) emit centreFrameChanged(this, f, m_followPan);
+    }
+}
+
+void
+View::setZoomLevel(size_t z)
+{
+    if (m_zoomLevel != int(z)) {
+	m_zoomLevel = z;
+	emit zoomLevelChanged(this, z, m_followZoom);
+	update();
+    }
+}
+
+void
+View::addLayer(Layer *layer)
+{
+    delete m_cache;
+    m_cache = 0;
+
+    m_layers.push_back(layer);
+
+    m_progressBars[layer] = new LayerProgressBar(this);
+    m_progressBars[layer]->setMinimum(0);
+    m_progressBars[layer]->setMaximum(100);
+    m_progressBars[layer]->setMinimumWidth(80);
+    m_progressBars[layer]->hide();
+
+    connect(layer, SIGNAL(layerParametersChanged()),
+	    this,    SLOT(layerParametersChanged()));
+    connect(layer, SIGNAL(layerNameChanged()),
+	    this,    SLOT(layerNameChanged()));
+    connect(layer, SIGNAL(modelChanged()),
+	    this,    SLOT(modelChanged()));
+    connect(layer, SIGNAL(modelCompletionChanged()),
+	    this,    SLOT(modelCompletionChanged()));
+    connect(layer, SIGNAL(modelChanged(size_t, size_t)),
+	    this,    SLOT(modelChanged(size_t, size_t)));
+    connect(layer, SIGNAL(modelReplaced()),
+	    this,    SLOT(modelReplaced()));
+
+    m_newModel = true;
+    update();
+
+    emit propertyContainerAdded(layer);
+}
+
+void
+View::removeLayer(Layer *layer)
+{
+    if (m_deleting) {
+	return;
+    }
+
+    delete m_cache;
+    m_cache = 0;
+
+    for (LayerList::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	if (*i == layer) {
+	    m_layers.erase(i);
+	    if (m_progressBars.find(layer) != m_progressBars.end()) {
+		delete m_progressBars[layer];
+		m_progressBars.erase(layer);
+	    }
+	    break;
+	}
+    }
+    
+    update();
+
+    emit propertyContainerRemoved(layer);
+}
+
+void
+View::setViewManager(ViewManager *manager)
+{
+    if (m_manager) {
+	m_manager->disconnect(this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool)));
+	m_manager->disconnect(this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool)));
+	disconnect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
+	disconnect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
+    }
+
+    m_manager = manager;
+    if (m_followPan) setCentreFrame(m_manager->getGlobalCentreFrame(), false);
+    if (m_followZoom) setZoomLevel(m_manager->getGlobalZoom());
+
+    connect(m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
+	    this, SLOT(viewManagerCentreFrameChanged(void *, unsigned long, bool)));
+    connect(m_manager, SIGNAL(playbackFrameChanged(unsigned long)),
+	    this, SLOT(viewManagerPlaybackFrameChanged(unsigned long)));
+    connect(m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
+	    this, SLOT(viewManagerZoomLevelChanged(void *, unsigned long, bool)));
+
+    connect(this, SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
+	    m_manager, SIGNAL(centreFrameChanged(void *, unsigned long, bool)));
+    connect(this, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
+	    m_manager, SIGNAL(zoomLevelChanged(void *, unsigned long, bool)));
+}
+
+void
+View::setFollowGlobalPan(bool f)
+{
+    m_followPan = f;
+    emit propertyContainerPropertyChanged(this);
+}
+
+void
+View::setFollowGlobalZoom(bool f)
+{
+    m_followZoom = f;
+    emit propertyContainerPropertyChanged(this);
+}
+
+void
+View::setPlaybackFollow(PlaybackFollowMode m)
+{
+    m_followPlay = m;
+    emit propertyContainerPropertyChanged(this);
+}
+
+void
+View::modelChanged()
+{
+    QObject *obj = sender();
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+    std::cerr << "View::modelChanged()" << std::endl;
+#endif
+    delete m_cache;
+    m_cache = 0;
+
+    checkProgress(obj);
+
+    update();
+}
+
+void
+View::modelChanged(size_t startFrame, size_t endFrame)
+{
+    QObject *obj = sender();
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+    std::cerr << "View::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl;
+#endif
+
+    long myStartFrame = getStartFrame();
+    size_t myEndFrame = getEndFrame();
+
+    if (myStartFrame > 0 && endFrame < size_t(myStartFrame)) {
+	checkProgress(obj);
+	return;
+    }
+    if (startFrame > myEndFrame) {
+	checkProgress(obj);
+	return;
+    }
+
+    delete m_cache;
+    m_cache = 0;
+
+    if (long(startFrame) < myStartFrame) startFrame = myStartFrame;
+    if (endFrame > myEndFrame) endFrame = myEndFrame;
+
+    int x0 = (startFrame - myStartFrame) / m_zoomLevel;
+    int x1 = (endFrame - myStartFrame) / m_zoomLevel;
+    if (x1 < x0) return;
+
+    checkProgress(obj);
+
+    update(x0, 0, x1 - x0 + 1, height());
+}    
+
+void
+View::modelCompletionChanged()
+{
+    QObject *obj = sender();
+    checkProgress(obj);
+}
+
+void
+View::modelReplaced()
+{
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+    std::cerr << "View::modelReplaced()" << std::endl;
+#endif
+    m_newModel = true;
+    update();
+}
+
+void
+View::layerParametersChanged()
+{
+    Layer *layer = dynamic_cast<Layer *>(sender());
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+    std::cerr << "View::layerParametersChanged()" << std::endl;
+#endif
+
+    delete m_cache;
+    m_cache = 0;
+    update();
+
+    if (layer) {
+	emit propertyContainerPropertyChanged(layer);
+    }
+}
+
+void
+View::layerNameChanged()
+{
+    Layer *layer = dynamic_cast<Layer *>(sender());
+    if (layer) emit propertyContainerNameChanged(layer);
+}
+
+void
+View::viewManagerCentreFrameChanged(void *p, unsigned long f, bool locked)
+{
+    if (m_followPan && p != this && locked) {
+	if (m_manager && (sender() == m_manager)) {
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	    std::cerr << this << ": manager frame changed " << f << " from " << p << std::endl;
+#endif
+	    setCentreFrame(f);
+	    if (p == this) repaint();
+	}
+    }
+}
+
+void
+View::viewManagerPlaybackFrameChanged(unsigned long f)
+{
+    if (m_manager) {
+	if (sender() != m_manager) return;
+    }
+
+    if (m_playPointerFrame == f) return;
+    bool visible = (m_playPointerFrame / m_zoomLevel != f / m_zoomLevel);
+    size_t oldPlayPointerFrame = m_playPointerFrame;
+    m_playPointerFrame = f;
+    if (!visible) return;
+
+    switch (m_followPlay) {
+
+    case PlaybackScrollContinuous:
+	if (QApplication::mouseButtons() == Qt::NoButton) {
+	    setCentreFrame(f, false);
+	}
+	break;
+
+    case PlaybackScrollPage:
+    { 
+	long w = width() * getZoomLevel();
+	w -= w/5;
+	long sf = (f / w) * w - w/8;
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	std::cerr << "PlaybackScrollPage: f = " << f << ", sf = " << sf << ", start frame "
+		  << getStartFrame() << std::endl;
+#endif
+	setCentreFrame(sf + width() * getZoomLevel() / 2, false);
+	int xold = (long(oldPlayPointerFrame) - getStartFrame()) / m_zoomLevel;
+	int xnew = (long(m_playPointerFrame) - getStartFrame()) / m_zoomLevel;
+	update(xold - 1, 0, 3, height());
+	update(xnew - 1, 0, 3, height());
+	break;
+    }
+
+    case PlaybackIgnore:
+	if (long(f) >= getStartFrame() && f < getEndFrame()) {
+	    update();
+	}
+	break;
+    }
+}
+
+void
+View::viewManagerZoomLevelChanged(void *p, unsigned long z, bool locked)
+{
+    if (m_followZoom && p != this && locked) {
+	if (m_manager && (sender() == m_manager)) {
+	    setZoomLevel(z);
+	    if (p == this) repaint();
+	}
+    }
+}
+
+size_t
+View::getModelsStartFrame() const
+{
+    bool first = true;
+    size_t startFrame = 0;
+
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+
+	if ((*i)->getModel() && (*i)->getModel()->isOK()) {
+
+	    size_t thisStartFrame = (*i)->getModel()->getStartFrame();
+
+	    if (first || thisStartFrame < startFrame) {
+		startFrame = thisStartFrame;
+	    }
+	    first = false;
+	}
+    }
+    return startFrame;
+}
+
+size_t
+View::getModelsEndFrame() const
+{
+    bool first = true;
+    size_t endFrame = 0;
+
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+
+	if ((*i)->getModel() && (*i)->getModel()->isOK()) {
+
+	    size_t thisEndFrame = (*i)->getModel()->getEndFrame();
+
+	    if (first || thisEndFrame > endFrame) {
+		endFrame = thisEndFrame;
+	    }
+	    first = false;
+	}
+    }
+
+    if (first) return getModelsStartFrame();
+    return endFrame;
+}
+
+int
+View::getModelsSampleRate() const
+{
+    //!!! Just go for the first, for now.  If we were supporting
+    // multiple samplerates, we'd probably want to do frame/time
+    // conversion in the model
+
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	if ((*i)->getModel() && (*i)->getModel()->isOK()) {
+	    return (*i)->getModel()->getSampleRate();
+	}
+    }
+    return 0;
+}
+
+bool
+View::areLayersScrollable() const
+{
+    // True iff all views are scrollable
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	if (!(*i)->isLayerScrollable()) return false;
+    }
+    return true;
+}
+
+View::LayerList
+View::getScrollableBackLayers(bool &changed) const
+{
+    changed = false;
+
+    LayerList scrollables;
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	if ((*i)->isLayerScrollable()) scrollables.push_back(*i);
+	else {
+	    if (scrollables != m_lastScrollableBackLayers) {
+		m_lastScrollableBackLayers = scrollables;
+		changed = true;
+	    }
+	    return scrollables;
+	}
+    }
+
+    if (scrollables.size() == 1 &&
+	dynamic_cast<TimeRulerLayer *>(*scrollables.begin())) {
+
+	// If only the ruler is scrollable, it's not worth the bother
+	// -- it probably redraws as quickly as it refreshes from
+	// cache
+	scrollables.clear();
+    }
+
+    if (scrollables != m_lastScrollableBackLayers) {
+	m_lastScrollableBackLayers = scrollables;
+	changed = true;
+    }
+    return scrollables;
+}
+
+View::LayerList
+View::getNonScrollableFrontLayers(bool &changed) const
+{
+    changed = false;
+    LayerList scrollables = getScrollableBackLayers(changed);
+    LayerList nonScrollables;
+
+    // Everything in front of the first non-scrollable from the back
+    // should also be considered non-scrollable
+
+    size_t count = 0;
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+	if (count < scrollables.size()) {
+	    ++count;
+	    continue;
+	}
+	nonScrollables.push_back(*i);
+    }
+
+    if (nonScrollables != m_lastNonScrollableBackLayers) {
+	m_lastNonScrollableBackLayers = nonScrollables;
+	changed = true;
+    }
+
+    return nonScrollables;
+}
+
+size_t
+View::getZoomConstraintBlockSize(size_t blockSize,
+				 ZoomConstraint::RoundingDirection dir)
+    const
+{
+    size_t candidate = blockSize;
+    bool haveCandidate = false;
+
+    for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
+
+	if ((*i)->getZoomConstraint()) {
+
+	    size_t thisBlockSize = 
+		(*i)->getZoomConstraint()->getNearestBlockSize
+		(blockSize, dir);
+
+	    // Go for the block size that's furthest from the one
+	    // passed in.  Most of the time, that's what we want.
+	    if (!haveCandidate ||
+		(thisBlockSize > blockSize && thisBlockSize > candidate) ||
+		(thisBlockSize < blockSize && thisBlockSize < candidate)) {
+		candidate = thisBlockSize;
+		haveCandidate = true;
+	    }
+	}
+    }
+
+    return candidate;
+}
+
+void
+View::zoom(bool in)
+{
+    int newZoomLevel = m_zoomLevel;
+
+    if (in) {
+	newZoomLevel = getZoomConstraintBlockSize(newZoomLevel - 1, 
+						  ZoomConstraint::RoundDown);
+    } else {
+	newZoomLevel = getZoomConstraintBlockSize(newZoomLevel + 1,
+						  ZoomConstraint::RoundUp);
+    }
+
+    if (newZoomLevel != m_zoomLevel) {
+	setZoomLevel(newZoomLevel);
+    }
+}
+
+void
+View::checkProgress(void *object)
+{
+//    std::cerr << "View::checkProgress(" << object << ")" << std::endl;
+
+    if (!m_showProgress) return;
+
+    int ph = height();
+
+    for (ProgressMap::const_iterator i = m_progressBars.begin();
+	 i != m_progressBars.end(); ++i) {
+
+	if (i->first == object) {
+
+	    int completion = i->first->getCompletion();
+
+	    if (completion >= 100) {
+
+		i->second->hide();
+
+	    } else {
+
+		i->second->setText(i->first->getPropertyContainerName());
+		i->second->setValue(completion);
+		i->second->move(0, ph - i->second->height());
+
+		i->second->show();
+		i->second->update();
+
+		ph -= i->second->height();
+	    }
+	} else {
+	    if (i->second->isVisible()) {
+		ph -= i->second->height();
+	    }
+	}
+    }
+}
+/*!!!
+void
+View::identifyLocalFeatures(bool on, int x, int y)
+{
+    for (LayerList::const_iterator i = m_layers.end(); i != m_layers.begin(); ) {
+	--i;
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	std::cerr << "View::identifyLocalFeatures: calling on " << *i << std::endl;
+#endif
+	if ((*i)->identifyLocalFeatures(on, x, y)) break;
+    }
+}
+*/
+
+void
+View::paintEvent(QPaintEvent *e)
+{
+//    Profiler prof("View::paintEvent", true);
+//    std::cerr << "View::paintEvent" << std::endl;
+
+    if (m_layers.empty()) {
+	QFrame::paintEvent(e);
+	return;
+    }
+
+    if (m_newModel) {
+	m_newModel = false;
+    }
+
+    // ensure our constraints are met
+    m_zoomLevel = getZoomConstraintBlockSize(m_zoomLevel,
+					     ZoomConstraint::RoundUp);
+
+    QPainter paint;
+    bool repaintCache = false;
+    bool paintedCacheRect = false;
+
+    QRect cacheRect(rect());
+
+    if (e) {
+	cacheRect &= e->rect();
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	std::cerr << "paint rect " << cacheRect.width() << "x" << cacheRect.height()
+		  << ", my rect " << width() << "x" << height() << std::endl;
+#endif
+    }
+
+    QRect nonCacheRect(cacheRect);
+
+    // If not all layers are scrollable, but some of the back layers
+    // are, we should store only those in the cache
+
+    bool layersChanged = false;
+    LayerList scrollables = getScrollableBackLayers(layersChanged);
+    LayerList nonScrollables = getNonScrollableFrontLayers(layersChanged);
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+    std::cerr << "View(" << this << ")::paintEvent: have " << scrollables.size()
+	      << " scrollable back layers and " << nonScrollables.size()
+	      << " non-scrollable front layers" << std::endl;
+#endif
+
+    if (layersChanged || scrollables.empty()) {
+	delete m_cache;
+	m_cache = 0;
+    }
+
+    if (!scrollables.empty()) {
+	if (!m_cache ||
+	    m_cacheZoomLevel != m_zoomLevel ||
+	    width() != m_cache->width() ||
+	    height() != m_cache->height()) {
+
+	    // cache is not valid
+
+	    if (cacheRect.width() < width()/10) {
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+		std::cerr << "View(" << this << ")::paintEvent: small repaint, not bothering to recreate cache" << std::endl;
+#endif
+	    } else {
+		delete m_cache;
+		m_cache = new QPixmap(width(), height());
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+		std::cerr << "View(" << this << ")::paintEvent: recreated cache" << std::endl;
+#endif
+		cacheRect = rect();
+		repaintCache = true;
+	    }
+
+	} else if (m_cacheCentreFrame != m_centreFrame) {
+
+	    long dx = long(m_cacheCentreFrame / m_zoomLevel) -
+		long(m_centreFrame / m_zoomLevel);
+
+	    if (dx > -width() && dx < width()) {
+#if defined(Q_WS_WIN32) || defined(Q_WS_MAC)
+		// Copying a pixmap to itself doesn't work properly on Windows
+		// or Mac (it only works when moving in one direction)
+		static QPixmap *tmpPixmap = 0;
+		if (!tmpPixmap ||
+		    tmpPixmap->width() != width() ||
+		    tmpPixmap->height() != height()) {
+		    delete tmpPixmap;
+		    tmpPixmap = new QPixmap(width(), height());
+		}
+		paint.begin(tmpPixmap);
+		paint.drawPixmap(0, 0, *m_cache);
+		paint.end();
+		paint.begin(m_cache);
+		paint.drawPixmap(dx, 0, *tmpPixmap);
+		paint.end();
+#else
+		// But it seems to be fine on X11
+		paint.begin(m_cache);
+		paint.drawPixmap(dx, 0, *m_cache);
+		paint.end();
+#endif
+
+		if (dx < 0) {
+		    cacheRect = QRect(width() + dx, 0, -dx, height());
+		} else {
+		    cacheRect = QRect(0, 0, dx, height());
+		}
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+		std::cerr << "View(" << this << ")::paintEvent: scrolled cache by " << dx << std::endl;
+#endif
+	    } else {
+		cacheRect = rect();
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+		std::cerr << "View(" << this << ")::paintEvent: scrolling too far" << std::endl;
+#endif
+	    }
+	    repaintCache = true;
+
+	} else {
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+	    std::cerr << "View(" << this << ")::paintEvent: cache is good" << std::endl;
+#endif
+	    paint.begin(this);
+	    paint.drawPixmap(cacheRect, *m_cache, cacheRect);
+	    paint.end();
+	    QFrame::paintEvent(e);
+	    paintedCacheRect = true;
+	}
+
+	m_cacheCentreFrame = m_centreFrame;
+	m_cacheZoomLevel = m_zoomLevel;
+    }
+
+#ifdef DEBUG_VIEW_WIDGET_PAINT
+//    std::cerr << "View(" << this << ")::paintEvent: cacheRect " << cacheRect << ", nonCacheRect " << (nonCacheRect | cacheRect) << ", repaintCache " << repaintCache << ", paintedCacheRect " << paintedCacheRect << std::endl;
+#endif
+
+    // Scrollable (cacheable) items first
+
+    if (!paintedCacheRect) {
+
+	if (repaintCache) paint.begin(m_cache);
+	else paint.begin(this);
+
+	paint.setClipRect(cacheRect);
+	
+	if (hasLightBackground()) {
+	    paint.setPen(Qt::white);
+	    paint.setBrush(Qt::white);
+	} else {
+	    paint.setPen(Qt::black);
+	    paint.setBrush(Qt::black);
+	}
+	paint.drawRect(cacheRect);
+
+	paint.setPen(Qt::black);
+	paint.setBrush(Qt::NoBrush);
+	
+	for (LayerList::iterator i = scrollables.begin(); i != scrollables.end(); ++i) {
+	    (*i)->paint(paint, cacheRect);
+	}
+	
+	paint.end();
+
+	if (repaintCache) {
+	    cacheRect |= (e ? e->rect() : rect());
+	    paint.begin(this);
+	    paint.drawPixmap(cacheRect, *m_cache, cacheRect);
+	    paint.end();
+	}
+    }
+
+    // Now non-cacheable items.  We always need to redraw the
+    // non-cacheable items across at least the area we drew of the
+    // cacheable items.
+
+    nonCacheRect |= cacheRect;
+
+    paint.begin(this);
+    paint.setClipRect(nonCacheRect);
+
+    if (scrollables.empty()) {
+	if (hasLightBackground()) {
+	    paint.setPen(Qt::white);
+	    paint.setBrush(Qt::white);
+	} else {
+	    paint.setPen(Qt::black);
+	    paint.setBrush(Qt::black);
+	}
+	paint.drawRect(nonCacheRect);
+    }
+	
+    paint.setPen(Qt::black);
+    paint.setBrush(Qt::NoBrush);
+	
+    for (LayerList::iterator i = nonScrollables.begin(); i != nonScrollables.end(); ++i) {
+	(*i)->paint(paint, nonCacheRect);
+    }
+	
+    paint.end();
+
+    if (m_followPlay != PlaybackScrollContinuous) {
+
+	paint.begin(this);
+
+	if (long(m_playPointerFrame) > getStartFrame() &&
+	    m_playPointerFrame < getEndFrame()) {
+
+	    int playx = (long(m_playPointerFrame) - getStartFrame()) /
+		m_zoomLevel;
+
+	    paint.setPen(Qt::black);
+	    paint.drawLine(playx - 1, 0, playx - 1, height() - 1);
+	    paint.drawLine(playx + 1, 0, playx + 1, height() - 1);
+	    paint.drawPoint(playx, 0);
+	    paint.drawPoint(playx, height() - 1);
+	    paint.setPen(Qt::white);
+	    paint.drawLine(playx, 1, playx, height() - 2);
+	}
+
+	paint.end();
+    }
+
+    QFrame::paintEvent(e);
+}
+
+#ifdef INCLUDE_MOCFILES
+#include "View.moc.cpp"
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/View.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,232 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _CANVAS_H_
+#define _CANVAS_H_
+
+#include <QFrame>
+#include <QProgressBar>
+
+#include "base/ZoomConstraint.h"
+#include "base/PropertyContainer.h"
+
+class Layer;
+class ViewManager;
+
+#include <map>
+
+/**
+ * View is the base class of widgets that display one or more
+ * overlaid views of data against a horizontal time scale. 
+ *
+ * A View may have any number of attached Layers, each of which
+ * is expected to have one data Model (although multiple views may
+ * share the same model).
+ *
+ * A View may be panned in time and zoomed, although the
+ * mechanisms for doing so (as well as any other operations and
+ * properties available) depend on the subclass.
+ */
+
+class View : public QFrame,
+	     public PropertyContainer
+{
+    Q_OBJECT
+
+public:
+    /**
+     * Deleting a View deletes all its views.  However, it is
+     * also acceptable for the views to be deleted by other code (in
+     * which case they will remove themselves from this View
+     * automatically), or to be removed explicitly without deleting
+     * using removeLayer.
+     */
+    virtual ~View();
+
+    /**
+     * Retrieve the first visible sample frame on the widget.
+     * This is a calculated value based on the centre-frame, widget
+     * width and zoom level.  The result may be negative.
+     */
+    virtual long getStartFrame() const;
+
+    /**
+     * Set the widget pan based on the given first visible frame.  The
+     * frame value may be negative.
+     */
+    virtual void setStartFrame(long);
+
+    /**
+     * Return the centre frame of the visible widget.  This is an
+     * exact value that does not depend on the zoom block size.  Other
+     * frame values (start, end) are calculated from this based on the
+     * zoom and other factors.
+     */
+    virtual size_t getCentreFrame() const { return m_centreFrame; }
+
+    /**
+     * Set the centre frame of the visible widget.
+     */
+    virtual void setCentreFrame(size_t f) { setCentreFrame(f, true); }
+
+    /**
+     * Retrieve the last visible sample frame on the widget.
+     * This is a calculated value based on the centre-frame, widget
+     * width and zoom level.
+     */
+    virtual size_t getEndFrame() const;
+
+    /**
+     * Return the zoom level, i.e. the number of frames per pixel.
+     */
+    virtual int getZoomLevel() const { return m_zoomLevel; }
+
+    /**
+     * Set the zoom level, i.e. the number of frames per pixel.  The
+     * centre frame will be unchanged; the start and end frames will
+     * change.
+     */
+    virtual void setZoomLevel(size_t z);
+
+    /**
+     * Zoom in or out.
+     */
+    virtual void zoom(bool in);
+
+    virtual void addLayer(Layer *v);
+    virtual void removeLayer(Layer *v); // does not delete the layer
+    virtual int getLayerCount() const { return m_layers.size(); }
+
+    /**
+     * Return a layer, counted in stacking order.  That is, layer 0 is
+     * the bottom layer and layer "getLayerCount()-1" is the top one.
+     */
+    virtual Layer *getLayer(int n) { return m_layers[n]; }
+
+    virtual void setViewManager(ViewManager *m);
+
+    virtual void setFollowGlobalPan(bool f);
+    virtual bool getFollowGlobalPan() const { return m_followPan; }
+
+    virtual void setFollowGlobalZoom(bool f);
+    virtual bool getFollowGlobalZoom() const { return m_followZoom; }
+
+    virtual void setLightBackground(bool lb) { m_lightBackground = lb; }
+    virtual bool hasLightBackground() const { return m_lightBackground; }
+
+    virtual bool shouldIlluminateLocalFeatures(const Layer *, QPoint &) {
+	return false;
+    }
+
+    enum PlaybackFollowMode {
+	PlaybackScrollContinuous,
+	PlaybackScrollPage,
+	PlaybackIgnore
+    };
+    virtual void setPlaybackFollow(PlaybackFollowMode m);
+    virtual PlaybackFollowMode getPlaybackFollow() const { return m_followPlay; }
+
+    virtual PropertyList getProperties() const;
+    virtual PropertyType getPropertyType(const PropertyName &) const;
+    virtual int getPropertyRangeAndValue(const PropertyName &,
+					   int *min, int *max) const;
+    virtual QString getPropertyValueLabel(const PropertyName &,
+					  int value) const;
+    virtual void setProperty(const PropertyName &, int value);
+
+    virtual size_t getPropertyContainerCount() const;
+    virtual const PropertyContainer *getPropertyContainer(size_t i) const;
+    virtual PropertyContainer *getPropertyContainer(size_t i);
+
+    virtual QString getPropertyContainerName() const {
+	return objectName();
+    }
+
+signals:
+    void propertyContainerAdded(PropertyContainer *pc);
+    void propertyContainerRemoved(PropertyContainer *pc);
+    void propertyContainerPropertyChanged(PropertyContainer *pc);
+    void propertyContainerNameChanged(PropertyContainer *pc);
+
+    void centreFrameChanged(void *, unsigned long, bool);
+    void zoomLevelChanged(void *, unsigned long, bool);
+
+public slots:
+    virtual void modelChanged();
+    virtual void modelChanged(size_t startFrame, size_t endFrame);
+    virtual void modelCompletionChanged();
+    virtual void modelReplaced();
+    virtual void layerParametersChanged();
+    virtual void layerNameChanged();
+
+    virtual void viewManagerCentreFrameChanged(void *, unsigned long, bool);
+    virtual void viewManagerPlaybackFrameChanged(unsigned long);
+    virtual void viewManagerZoomLevelChanged(void *, unsigned long, bool);
+
+    virtual void propertyContainerSelected(PropertyContainer *pc);
+
+protected:
+    View(QWidget *, bool showProgress);
+    virtual void paintEvent(QPaintEvent *e);
+
+    typedef std::vector<Layer *> LayerList;
+
+    size_t getModelsStartFrame() const;
+    size_t getModelsEndFrame() const;
+    int getModelsSampleRate() const;
+    bool areLayersScrollable() const;
+    LayerList getScrollableBackLayers(bool &changed) const;
+    LayerList getNonScrollableFrontLayers(bool &changed) const;
+    size_t getZoomConstraintBlockSize(size_t blockSize,
+				      ZoomConstraint::RoundingDirection dir =
+				      ZoomConstraint::RoundNearest) const;
+
+    void setCentreFrame(size_t f, bool e);
+
+    void checkProgress(void *object);
+
+    size_t m_centreFrame;
+    int m_zoomLevel;
+    bool m_newModel;
+    bool m_followPan;
+    bool m_followZoom;
+    PlaybackFollowMode m_followPlay;
+    size_t m_playPointerFrame;
+    bool m_lightBackground;
+    bool m_showProgress;
+
+    QPixmap *m_cache;
+    size_t m_cacheCentreFrame;
+    int m_cacheZoomLevel;
+
+    bool m_deleting;
+
+    LayerList m_layers; // I don't own these, but see note in dtor comment above
+
+    // caches for use in getScrollableBackLayers, getNonScrollableFrontLayers
+    mutable LayerList m_lastScrollableBackLayers;
+    mutable LayerList m_lastNonScrollableBackLayers;
+
+    class LayerProgressBar : public QProgressBar {
+    public:
+	LayerProgressBar(QWidget *parent) : QProgressBar(parent) { }
+	virtual QString text() const { return m_text; }
+	virtual void setText(QString text) { m_text = text; }
+    protected:
+	QString m_text;
+    };
+
+    typedef std::map<Layer *, LayerProgressBar *> ProgressMap;
+    ProgressMap m_progressBars; // I own the ProgressBars
+
+    ViewManager *m_manager; // I don't own this
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ViewManager.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,162 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "ViewManager.h"
+#include "AudioPlaySource.h"
+#include "PlayParameters.h"
+#include "Model.h"
+
+#include <iostream>
+
+//#define DEBUG_VIEW_MANAGER 1
+
+ViewManager::ViewManager() :
+    m_playSource(0),
+    m_globalCentreFrame(0),
+    m_globalZoom(1024),
+    m_lastLeft(0), 
+    m_lastRight(0)
+{
+    connect(this, 
+	    SIGNAL(centreFrameChanged(void *, unsigned long, bool)),
+	    SLOT(considerSeek(void *, unsigned long, bool)));
+
+    connect(this, 
+	    SIGNAL(zoomLevelChanged(void *, unsigned long, bool)),
+	    SLOT(considerZoomChange(void *, unsigned long, bool)));
+}
+
+unsigned long
+ViewManager::getGlobalCentreFrame() const
+{
+#ifdef DEBUG_VIEW_MANAGER
+    std::cout << "ViewManager::getGlobalCentreFrame: returning " << m_globalCentreFrame << std::endl;
+#endif
+    return m_globalCentreFrame;
+}
+
+unsigned long
+ViewManager::getGlobalZoom() const
+{
+#ifdef DEBUG_VIEW_MANAGER
+    std::cout << "ViewManager::getGlobalZoom: returning " << m_globalZoom << std::endl;
+#endif
+    return m_globalZoom;
+}
+
+void
+ViewManager::setAudioPlaySource(AudioPlaySource *source)
+{
+    if (!m_playSource) {
+	QTimer::singleShot(100, this, SLOT(checkPlayStatus()));
+    }
+    m_playSource = source;
+}
+
+PlayParameters *
+ViewManager::getPlayParameters(const Model *model)
+{
+    if (m_playParameters.find(model) == m_playParameters.end()) {
+	// Give all models the same type of play parameters for the moment
+	m_playParameters[model] = new PlayParameters;
+    }
+
+    return m_playParameters[model];
+}
+
+void
+ViewManager::clearPlayParameters()
+{
+    while (!m_playParameters.empty()) {
+	delete m_playParameters.begin()->second;
+	m_playParameters.erase(m_playParameters.begin());
+    }
+}
+
+void
+ViewManager::checkPlayStatus()
+{
+    if (m_playSource && m_playSource->isPlaying()) {
+
+	float left = 0, right = 0;
+	if (m_playSource->getOutputLevels(left, right)) {
+	    if (left != m_lastLeft || right != m_lastRight) {
+		emit outputLevelsChanged(left, right);
+		m_lastLeft = left;
+		m_lastRight = right;
+	    }
+	}
+
+	m_globalCentreFrame = m_playSource->getCurrentPlayingFrame();
+
+#ifdef DEBUG_VIEW_MANAGER
+	std::cout << "ViewManager::checkPlayStatus: Playing, frame " << m_globalCentreFrame << ", levels " << m_lastLeft << "," << m_lastRight << std::endl;
+#endif
+
+	emit playbackFrameChanged(m_globalCentreFrame);
+
+	QTimer::singleShot(20, this, SLOT(checkPlayStatus()));
+
+    } else {
+
+	QTimer::singleShot(100, this, SLOT(checkPlayStatus()));
+	
+	if (m_lastLeft != 0.0 || m_lastRight != 0.0) {
+	    emit outputLevelsChanged(0.0, 0.0);
+	    m_lastLeft = 0.0;
+	    m_lastRight = 0.0;
+	}
+
+#ifdef DEBUG_VIEW_MANAGER
+//	std::cout << "ViewManager::checkPlayStatus: Not playing" << std::endl;
+#endif
+    }
+}
+
+void
+ViewManager::considerSeek(void *p, unsigned long f, bool locked)
+{
+    if (locked) {
+	m_globalCentreFrame = f;
+    }
+
+#ifdef DEBUG_VIEW_MANAGER 
+    std::cout << "ViewManager::considerSeek(" << p << ", " << f << ", " << locked << ")" << std::endl;
+#endif
+
+    if (p == this || !locked) return;
+
+    if (m_playSource && m_playSource->isPlaying()) {
+	unsigned long playFrame = m_playSource->getCurrentPlayingFrame();
+	unsigned long diff = std::max(f, playFrame) - std::min(f, playFrame);
+	if (diff > 20000) {
+	    m_playSource->play(f);
+#ifdef DEBUG_VIEW_MANAGER 
+	    std::cout << "ViewManager::considerSeek: reseeking from " << playFrame << " to " << f << std::endl;
+#endif
+	}
+    }
+}
+
+void
+ViewManager::considerZoomChange(void *p, unsigned long z, bool locked)
+{
+    if (locked) {
+	m_globalZoom = z;
+    }
+
+#ifdef DEBUG_VIEW_MANAGER 
+    std::cout << "ViewManager::considerZoomChange(" << p << ", " << z << ", " << locked << ")" << std::endl;
+#endif
+}
+
+#ifdef INCLUDE_MOCFILES
+#include "ViewManager.moc.cpp"
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ViewManager.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,78 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _VIEW_MANAGER_H_
+#define _VIEW_MANAGER_H_
+
+#include <QObject>
+#include <QTimer>
+
+#include <map>
+
+class AudioPlaySource;
+class PlayParameters;
+class Model;
+
+/**
+ * The ViewManager manages properties that may need to be synchronised
+ * between separate Views.  For example, it handles signals associated
+ * with changes to the global pan and zoom.  It also handles playback
+ * properties and play synchronisation.
+ *
+ * Views should be implemented in such a way as to work
+ * correctly whether they are supplied with a ViewManager or not.
+ */
+
+class ViewManager : public QObject
+{
+    Q_OBJECT
+
+public:
+    ViewManager();
+
+    void setAudioPlaySource(AudioPlaySource *source);
+
+//!!! No way to remove a model!
+    PlayParameters *getPlayParameters(const Model *model);
+    void clearPlayParameters();
+
+    unsigned long getGlobalCentreFrame() const;
+    unsigned long getGlobalZoom() const;
+
+signals:
+    /** Emitted when a widget pans.  The originator identifies the widget. */
+    void centreFrameChanged(void *originator, unsigned long frame, bool locked);
+
+    /** Emitted when a widget zooms.  The originator identifies the widget. */
+    void zoomLevelChanged(void *originator, unsigned long zoom, bool locked);
+
+    /** Emitted when the playback frame changes. */
+    void playbackFrameChanged(unsigned long frame);
+
+    /** Emitted when the output levels change. Values in range 0.0 -> 1.0. */
+    void outputLevelsChanged(float left, float right);
+
+protected slots:
+    void checkPlayStatus();
+    void considerSeek(void *, unsigned long, bool);
+    void considerZoomChange(void *, unsigned long, bool);
+
+protected:
+    AudioPlaySource *m_playSource;
+    unsigned long m_globalCentreFrame;
+    unsigned long m_globalZoom;
+
+    float m_lastLeft;
+    float m_lastRight;
+
+    std::map<const Model *, PlayParameters *> m_playParameters;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/Window.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,120 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _WINDOW_H_
+#define _WINDOW_H_
+
+#include <cmath>
+#include <iostream>
+#include <map>
+
+enum WindowType {
+    RectangularWindow,
+    BartlettWindow,
+    HammingWindow,
+    HanningWindow,
+    BlackmanWindow,
+    GaussianWindow,
+    ParzenWindow
+};
+
+template <typename T>
+class Window
+{
+public:
+    /**
+     * Construct a windower of the given type.
+     */
+    Window(WindowType type, size_t size) : m_type(type), m_size(size) { encache(); }
+    Window(const Window &w) : m_type(w.m_type), m_size(w.m_size) { encache(); }
+    Window &operator=(const Window &w) {
+	if (&w == this) return;
+	m_type = w.m_type;
+	m_size = w.m_size;
+	encache();
+	return *this;
+    }
+    virtual ~Window() { delete m_cache; }
+    
+    void cut(T *src) const { cut(src, src); }
+    void cut(T *src, T *dst) const {
+	for (size_t i = 0; i < m_size; ++i) dst[i] = src[i] * m_cache[i];
+    }
+
+    WindowType getType() const { return m_type; }
+    size_t getSize() const { return m_size; }
+
+protected:
+    WindowType m_type;
+    size_t m_size;
+    T *m_cache;
+    
+    void encache();
+};
+
+template <typename T>
+void Window<T>::encache()
+{
+    size_t n = m_size;
+    T *mult = new T[n];
+    size_t i;
+    for (i = 0; i < n; ++i) mult[i] = 1.0;
+
+    switch (m_type) {
+		
+    case RectangularWindow:
+	for (i = 0; i < n; ++i) {
+	    mult[i] = mult[i] * 0.5;
+	}
+	break;
+	    
+    case BartlettWindow:
+	for (i = 0; i < n/2; ++i) {
+	    mult[i] = mult[i] * (i / T(n/2));
+	    mult[i + n/2] = mult[i + n/2] * (1.0 - (i / T(n/2)));
+	}
+	break;
+	    
+    case HammingWindow:
+	for (i = 0; i < n; ++i) {
+	    mult[i] = mult[i] * (0.54 - 0.46 * cos(2 * M_PI * i / n));
+	}
+	break;
+	    
+    case HanningWindow:
+	for (i = 0; i < n; ++i) {
+	    mult[i] = mult[i] * (0.50 - 0.50 * cos(2 * M_PI * i / n));
+	}
+	break;
+	    
+    case BlackmanWindow:
+	for (i = 0; i < n; ++i) {
+	    mult[i] = mult[i] * (0.42 - 0.50 * cos(2 * M_PI * i / n)
+				 + 0.08 * cos(4 * M_PI * i / n));
+	}
+	break;
+	    
+    case GaussianWindow:
+	for (i = 0; i < n; ++i) {
+	    mult[i] = mult[i] * exp((-1.0 / (n*n)) * ((T(2*i) - n) *
+						      (T(2*i) - n)));
+	}
+	break;
+	    
+    case ParzenWindow:
+	for (i = 0; i < n; ++i) {
+	    mult[i] = mult[i] * (1.0 - fabs((T(2*i) - n) / T(n + 1)));
+	}
+	break;
+    }
+	
+    m_cache = mult;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ZoomConstraint.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _ZOOM_CONSTRAINT_H_
+#define _ZOOM_CONSTRAINT_H_
+
+#include <stdlib.h>
+
+/**
+ * ZoomConstraint is a simple interface that describes a limitation on
+ * the available zoom sizes for a view, for example based on cache
+ * strategy or a (processing) window-size limitation.
+ *
+ * The default ZoomConstraint imposes no actual constraint.
+ */
+
+class ZoomConstraint
+{
+public:
+    enum RoundingDirection {
+	RoundDown,
+	RoundUp,
+	RoundNearest
+    };
+
+    /**
+     * Given the "ideal" block size (frames per pixel) for a given
+     * zoom level, return the nearest viable block size for this
+     * constraint.
+     *
+     * For example, if a block size of 1523 frames per pixel is
+     * requested but the underlying model only supports value
+     * summaries at powers-of-two block sizes, return 1024 or 2048
+     * depending on the rounding direction supplied.
+     */
+    virtual size_t getNearestBlockSize(size_t requestedBlockSize,
+				       RoundingDirection = RoundNearest)
+	const
+    {
+	return requestedBlockSize;
+    }
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/DSSIPluginFactory.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,349 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include "DSSIPluginFactory.h"
+#include <iostream>
+
+#include <QString>
+
+#include "DSSIPluginInstance.h"
+#include "PluginIdentifier.h"
+
+//!!!
+#include "plugins/SamplePlayer.h"
+
+#include "base/System.h"
+
+#ifdef HAVE_LIBLRDF
+#include "lrdf.h"
+#endif // HAVE_LIBLRDF
+
+
+DSSIPluginFactory::DSSIPluginFactory() :
+    LADSPAPluginFactory()
+{
+    m_hostDescriptor.DSSI_API_Version = 2;
+    m_hostDescriptor.request_transport_information = NULL;
+    m_hostDescriptor.request_midi_send = DSSIPluginInstance::requestMidiSend;
+    m_hostDescriptor.request_non_rt_thread = DSSIPluginInstance::requestNonRTThread;
+    m_hostDescriptor.midi_send = DSSIPluginInstance::midiSend;
+}
+
+DSSIPluginFactory::~DSSIPluginFactory()
+{
+    // nothing else to do here either
+}
+
+void
+DSSIPluginFactory::enumeratePlugins(std::vector<QString> &list)
+{
+    for (std::vector<QString>::iterator i = m_identifiers.begin();
+	 i != m_identifiers.end(); ++i) {
+
+	const DSSI_Descriptor *ddesc = getDSSIDescriptor(*i);
+	if (!ddesc) continue;
+
+	const LADSPA_Descriptor *descriptor = ddesc->LADSPA_Plugin;
+	if (!descriptor) continue;
+	
+//	std::cerr << "DSSIPluginFactory::enumeratePlugins: Name " << (descriptor->Name ? descriptor->Name : "NONE" ) << std::endl;
+
+	list.push_back(*i);
+	list.push_back(descriptor->Name);
+	list.push_back(QString("%1").arg(descriptor->UniqueID));
+	list.push_back(descriptor->Label);
+	list.push_back(descriptor->Maker);
+	list.push_back(descriptor->Copyright);
+	list.push_back((ddesc->run_synth || ddesc->run_multiple_synths) ? "true" : "false");
+	list.push_back(ddesc->run_multiple_synths ? "true" : "false");
+	list.push_back(m_taxonomy[descriptor->UniqueID]);
+	list.push_back(QString("%1").arg(descriptor->PortCount));
+
+	for (unsigned long p = 0; p < descriptor->PortCount; ++p) {
+
+	    int type = 0;
+	    if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) {
+		type |= PortType::Control;
+	    } else {
+		type |= PortType::Audio;
+	    }
+	    if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) {
+		type |= PortType::Input;
+	    } else {
+		type |= PortType::Output;
+	    }
+
+	    list.push_back(QString("%1").arg(p));
+	    list.push_back(descriptor->PortNames[p]);
+	    list.push_back(QString("%1").arg(type));
+	    list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p)));
+	    list.push_back(QString("%1").arg(getPortMinimum(descriptor, p)));
+	    list.push_back(QString("%1").arg(getPortMaximum(descriptor, p)));
+	    list.push_back(QString("%1").arg(getPortDefault(descriptor, p)));
+	}
+    }
+
+    unloadUnusedLibraries();
+}
+	
+RealTimePluginInstance *
+DSSIPluginFactory::instantiatePlugin(QString identifier,
+				     int instrument,
+				     int position,
+				     unsigned int sampleRate,
+				     unsigned int blockSize,
+				     unsigned int channels)
+{
+    const DSSI_Descriptor *descriptor = getDSSIDescriptor(identifier);
+
+    if (descriptor) {
+
+	DSSIPluginInstance *instance =
+	    new DSSIPluginInstance
+	    (this, instrument, identifier, position, sampleRate, blockSize, channels,
+	     descriptor);
+
+	m_instances.insert(instance);
+
+	return instance;
+    }
+
+    return 0;
+}
+
+const DSSI_Descriptor *
+DSSIPluginFactory::getDSSIDescriptor(QString identifier)
+{
+    QString type, soname, label;
+    PluginIdentifier::parseIdentifier(identifier, type, soname, label);
+
+    if (soname == PluginIdentifier::BUILTIN_PLUGIN_SONAME) {
+	if (label == "sample_player") {
+	    const DSSI_Descriptor *descriptor = SamplePlayer::getDescriptor(0);
+	    if (descriptor) {
+		descriptor->receive_host_descriptor(&m_hostDescriptor);
+	    }
+	    return descriptor;
+	} else {
+	    return 0;
+	}
+    }
+    
+    bool firstInLibrary = false;
+
+    if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
+	loadLibrary(soname);
+	if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
+	    std::cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: loadLibrary failed for " << soname.toStdString() << std::endl;
+	    return 0;
+	}
+	firstInLibrary = true;
+    }
+
+    void *libraryHandle = m_libraryHandles[soname];
+
+    DSSI_Descriptor_Function fn = (DSSI_Descriptor_Function)
+	DLSYM(libraryHandle, "dssi_descriptor");
+
+    if (!fn) {
+	std::cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No descriptor function in library " << soname.toStdString() << std::endl;
+	return 0;
+    }
+
+    const DSSI_Descriptor *descriptor = 0;
+    
+    int index = 0;
+    while ((descriptor = fn(index))) {
+	if (descriptor->LADSPA_Plugin->Label == label) {
+	    if (firstInLibrary && (descriptor->DSSI_API_Version >= 2)) {
+		descriptor->receive_host_descriptor(&m_hostDescriptor);
+	    }
+	    return descriptor;
+	}
+	++index;
+    }
+
+    std::cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No such plugin as " << label.toStdString() << " in library " << soname.toStdString() << std::endl;
+
+    return 0;
+}
+
+const LADSPA_Descriptor *
+DSSIPluginFactory::getLADSPADescriptor(QString identifier)
+{
+    const DSSI_Descriptor *dssiDescriptor = getDSSIDescriptor(identifier);
+    if (dssiDescriptor) return dssiDescriptor->LADSPA_Plugin;
+    else return 0;
+}
+
+
+std::vector<QString>
+DSSIPluginFactory::getPluginPath()
+{
+    std::vector<QString> pathList;
+    std::string path;
+
+    char *cpath = getenv("DSSI_PATH");
+    if (cpath) path = cpath;
+
+    if (path == "") {
+	path = "/usr/local/lib/dssi:/usr/lib/dssi";
+	char *home = getenv("HOME");
+	if (home) path = std::string(home) + "/.dssi:" + path;
+    }
+
+    std::string::size_type index = 0, newindex = 0;
+
+    while ((newindex = path.find(':', index)) < path.size()) {
+	pathList.push_back(path.substr(index, newindex - index).c_str());
+	index = newindex + 1;
+    }
+    
+    pathList.push_back(path.substr(index).c_str());
+
+    return pathList;
+}
+
+
+#ifdef HAVE_LIBLRDF
+std::vector<QString>
+DSSIPluginFactory::getLRDFPath(QString &baseUri)
+{
+    std::vector<QString> pathList = getPluginPath();
+    std::vector<QString> lrdfPaths;
+
+    lrdfPaths.push_back("/usr/local/share/dssi/rdf");
+    lrdfPaths.push_back("/usr/share/dssi/rdf");
+
+    lrdfPaths.push_back("/usr/local/share/ladspa/rdf");
+    lrdfPaths.push_back("/usr/share/ladspa/rdf");
+
+    for (std::vector<QString>::iterator i = pathList.begin();
+	 i != pathList.end(); ++i) {
+	lrdfPaths.push_back(*i + "/rdf");
+    }
+
+#ifdef DSSI_BASE
+    baseUri = DSSI_BASE;
+#else
+    baseUri = "http://dssi.sourceforge.net/ontology#";
+#endif
+
+    return lrdfPaths;
+}    
+#endif
+
+
+void
+DSSIPluginFactory::discoverPlugins(QString soname)
+{
+    // Note that soname is expected to be a full path at this point,
+    // of a file that is known to exist
+
+    void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
+
+    if (!libraryHandle) {
+        std::cerr << "WARNING: DSSIPluginFactory::discoverPlugins: couldn't load plugin library "
+                  << soname.toStdString() << " - " << DLERROR() << std::endl;
+        return;
+    }
+
+    DSSI_Descriptor_Function fn = (DSSI_Descriptor_Function)
+	DLSYM(libraryHandle, "dssi_descriptor");
+
+    if (!fn) {
+	std::cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No descriptor function in " << soname.toStdString() << std::endl;
+	return;
+    }
+
+    const DSSI_Descriptor *descriptor = 0;
+    
+    int index = 0;
+    while ((descriptor = fn(index))) {
+
+	const LADSPA_Descriptor *ladspaDescriptor = descriptor->LADSPA_Plugin;
+	if (!ladspaDescriptor) {
+	    std::cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No LADSPA descriptor for plugin " << index << " in " << soname.toStdString() << std::endl;
+	    ++index;
+	    continue;
+	}
+
+#ifdef HAVE_LIBLRDF
+	char *def_uri = 0;
+	lrdf_defaults *defs = 0;
+		
+	QString category = m_taxonomy[ladspaDescriptor->UniqueID];
+
+	if (category == "" && ladspaDescriptor->Name != 0) {
+	    std::string name = ladspaDescriptor->Name;
+	    if (name.length() > 4 &&
+		name.substr(name.length() - 4) == " VST") {
+		if (descriptor->run_synth || descriptor->run_multiple_synths) {
+		    category = "VST instruments";
+		} else {
+		    category = "VST effects";
+		}
+		m_taxonomy[ladspaDescriptor->UniqueID] = category;
+	    }
+	}
+	
+//	std::cerr << "Plugin id is " << ladspaDescriptor->UniqueID
+//		  << ", category is \"" << (category ? category : QString("(none)"))
+//		  << "\", name is " << ladspaDescriptor->Name
+//		  << ", label is " << ladspaDescriptor->Label
+//		  << std::endl;
+	
+	def_uri = lrdf_get_default_uri(ladspaDescriptor->UniqueID);
+	if (def_uri) {
+	    defs = lrdf_get_setting_values(def_uri);
+	}
+	
+	int controlPortNumber = 1;
+	
+	for (unsigned long i = 0; i < ladspaDescriptor->PortCount; i++) {
+	    
+	    if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) {
+		
+		if (def_uri && defs) {
+		    
+		    for (int j = 0; j < defs->count; j++) {
+			if (defs->items[j].pid == controlPortNumber) {
+//			    std::cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << ladspaDescriptor->PortNames[i] << std::endl;
+			    m_portDefaults[ladspaDescriptor->UniqueID][i] =
+				defs->items[j].value;
+			}
+		    }
+		}
+		
+		++controlPortNumber;
+	    }
+	}
+#endif // HAVE_LIBLRDF
+
+	QString identifier = PluginIdentifier::createIdentifier
+	    ("dssi", soname, ladspaDescriptor->Label);
+	m_identifiers.push_back(identifier);
+
+	++index;
+    }
+
+    if (DLCLOSE(libraryHandle) != 0) {
+        std::cerr << "WARNING: DSSIPluginFactory::discoverPlugins - can't unload " << libraryHandle << std::endl;
+        return;
+    }
+}
+
+    
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/DSSIPluginFactory.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,61 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _DSSI_PLUGIN_FACTORY_H_
+#define _DSSI_PLUGIN_FACTORY_H_
+
+#define DSSI_API_LEVEL 2
+
+#include "LADSPAPluginFactory.h"
+#include "api/dssi.h"
+
+#include <QMutex>
+
+class DSSIPluginInstance;
+
+class DSSIPluginFactory : public LADSPAPluginFactory
+{
+public:
+    virtual ~DSSIPluginFactory();
+
+    virtual void enumeratePlugins(std::vector<QString> &list);
+
+    virtual RealTimePluginInstance *instantiatePlugin(QString identifier,
+						      int clientId,
+						      int position,
+						      unsigned int sampleRate,
+						      unsigned int blockSize,
+						      unsigned int channels);
+
+protected:
+    DSSIPluginFactory();
+    friend class RealTimePluginFactory;
+
+    virtual std::vector<QString> getPluginPath();
+
+#ifdef HAVE_LIBLRDF
+    virtual std::vector<QString> getLRDFPath(QString &baseUri);
+#endif
+
+    virtual void discoverPlugins(QString soName);
+
+    virtual const LADSPA_Descriptor *getLADSPADescriptor(QString identifier);
+    virtual const DSSI_Descriptor *getDSSIDescriptor(QString identifier);
+
+    DSSI_Host_Descriptor m_hostDescriptor;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/DSSIPluginInstance.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,1182 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include <iostream>
+#include <cassert>
+
+#include "DSSIPluginInstance.h"
+#include "PluginIdentifier.h"
+#include "LADSPAPluginFactory.h"
+
+#define DEBUG_DSSI 1
+//#define DEBUG_DSSI_PROCESS 1
+
+#define EVENT_BUFFER_SIZE 1023
+
+#ifdef DEBUG_DSSI
+static std::ostream &operator<<(std::ostream& o, const QString &s)
+{
+    o << s.toLocal8Bit().data();
+    return o;
+}
+#endif
+
+DSSIPluginInstance::GroupMap DSSIPluginInstance::m_groupMap;
+snd_seq_event_t **DSSIPluginInstance::m_groupLocalEventBuffers = 0;
+size_t DSSIPluginInstance::m_groupLocalEventBufferCount = 0;
+Scavenger<ScavengerArrayWrapper<snd_seq_event_t *> > DSSIPluginInstance::m_bufferScavenger(2, 10);
+std::map<LADSPA_Handle, std::set<DSSIPluginInstance::NonRTPluginThread *> > DSSIPluginInstance::m_threads;
+
+
+DSSIPluginInstance::DSSIPluginInstance(RealTimePluginFactory *factory,
+				       int clientId,
+				       QString identifier,
+				       int position,
+				       unsigned long sampleRate,
+				       size_t blockSize,
+				       int idealChannelCount,
+				       const DSSI_Descriptor* descriptor) :
+    RealTimePluginInstance(factory, identifier),
+    m_client(clientId),
+    m_position(position),
+    m_descriptor(descriptor),
+    m_programCacheValid(false),
+    m_eventBuffer(EVENT_BUFFER_SIZE),
+    m_blockSize(blockSize),
+    m_idealChannelCount(idealChannelCount),
+    m_sampleRate(sampleRate),
+    m_latencyPort(0),
+    m_run(false),
+    m_bypassed(false),
+    m_grouped(false)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::DSSIPluginInstance(" << identifier << ")"
+	      << std::endl;
+#endif
+
+    init();
+
+    m_inputBuffers  = new sample_t*[m_audioPortsIn.size()];
+    m_outputBuffers = new sample_t*[m_outputBufferCount];
+
+    for (size_t i = 0; i < m_audioPortsIn.size(); ++i) {
+	m_inputBuffers[i] = new sample_t[blockSize];
+    }
+    for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	m_outputBuffers[i] = new sample_t[blockSize];
+    }
+
+    m_ownBuffers = true;
+
+    m_pending.lsb = m_pending.msb = m_pending.program = -1;
+
+    instantiate(sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+	initialiseGroupMembership();
+    }
+}
+
+DSSIPluginInstance::DSSIPluginInstance(RealTimePluginFactory *factory,
+				       int clientId,
+				       QString identifier,
+				       int position,
+				       unsigned long sampleRate,
+				       size_t blockSize,
+				       sample_t **inputBuffers,
+				       sample_t **outputBuffers,
+				       const DSSI_Descriptor* descriptor) :
+    RealTimePluginInstance(factory, identifier),
+    m_client(clientId),
+    m_position(position),
+    m_descriptor(descriptor),
+    m_eventBuffer(EVENT_BUFFER_SIZE),
+    m_blockSize(blockSize),
+    m_inputBuffers(inputBuffers),
+    m_outputBuffers(outputBuffers),
+    m_ownBuffers(false),
+    m_idealChannelCount(0),
+    m_sampleRate(sampleRate),
+    m_latencyPort(0),
+    m_run(false),
+    m_bypassed(false),
+    m_grouped(false)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::DSSIPluginInstance[buffers supplied](" << identifier << ")"
+	      << std::endl;
+#endif
+
+    init();
+
+    m_pending.lsb = m_pending.msb = m_pending.program = -1;
+
+    instantiate(sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+	if (m_descriptor->run_multiple_synths) {
+	    m_grouped = true;
+	    initialiseGroupMembership();
+	}
+    }
+}
+
+
+void
+DSSIPluginInstance::init()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::init" << std::endl;
+#endif
+
+    // Discover ports numbers and identities
+    //
+    const LADSPA_Descriptor *descriptor = m_descriptor->LADSPA_Plugin;
+
+    for (unsigned long i = 0; i < descriptor->PortCount; ++i)
+    {
+        if (LADSPA_IS_PORT_AUDIO(descriptor->PortDescriptors[i]))
+        {
+            if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+                m_audioPortsIn.push_back(i);
+	    } else {
+                m_audioPortsOut.push_back(i);
+	    }
+        }
+        else
+        if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i]))
+        {
+	    if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+
+		LADSPA_Data *data = new LADSPA_Data(0.0);
+
+		m_controlPortsIn.push_back(std::pair<unsigned long, LADSPA_Data*>
+					   (i, data));
+
+		m_backupControlPortsIn.push_back(0.0);
+
+	    } else {
+		LADSPA_Data *data = new LADSPA_Data(0.0);
+		m_controlPortsOut.push_back(
+                    std::pair<unsigned long, LADSPA_Data*>(i, data));
+		if (!strcmp(descriptor->PortNames[i], "latency") ||
+		    !strcmp(descriptor->PortNames[i], "_latency")) {
+#ifdef DEBUG_DSSI
+		    std::cerr << "Wooo! We have a latency port!" << std::endl;
+#endif
+		    m_latencyPort = data;
+		}
+	    }
+        }
+#ifdef DEBUG_DSSI
+        else
+            std::cerr << "DSSIPluginInstance::DSSIPluginInstance - "
+                      << "unrecognised port type" << std::endl;
+#endif
+    }
+
+    m_outputBufferCount = std::max(m_idealChannelCount, m_audioPortsOut.size());
+}
+
+size_t
+DSSIPluginInstance::getLatency()
+{
+    size_t latency = 0;
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::getLatency(): m_latencyPort " << m_latencyPort << ", m_run " << m_run << std::endl;
+#endif
+
+    if (m_latencyPort) {
+	if (!m_run) run(RealTime::zeroTime);
+	latency = (size_t)(*m_latencyPort + 0.1);
+    }
+    
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::getLatency(): latency is " << latency << std::endl;
+#endif
+
+    return latency;
+}
+
+void
+DSSIPluginInstance::silence()
+{
+    if (m_instanceHandle != 0) {
+	deactivate();
+	activate();
+    }
+}
+
+void
+DSSIPluginInstance::discardEvents()
+{
+    m_eventBuffer.reset();
+}
+
+void
+DSSIPluginInstance::setIdealChannelCount(size_t channels)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::setIdealChannelCount: channel count "
+	      << channels << " (was " << m_idealChannelCount << ")" << std::endl;
+#endif
+
+    if (channels == m_idealChannelCount) {
+	silence();
+	return;
+    }
+
+    if (m_instanceHandle != 0) {
+	deactivate();
+    }
+
+    m_idealChannelCount = channels;
+
+    if (channels > m_outputBufferCount) {
+
+	for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	    delete[] m_outputBuffers[i];
+	}
+
+	delete[] m_outputBuffers;
+
+	m_outputBufferCount = channels;
+
+	m_outputBuffers = new sample_t*[m_outputBufferCount];
+
+	for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	    m_outputBuffers[i] = new sample_t[m_blockSize];
+	}
+
+	connectPorts();
+    }
+
+    if (m_instanceHandle != 0) {
+	activate();
+    }
+}
+
+void
+DSSIPluginInstance::detachFromGroup()
+{
+    if (!m_grouped) return;
+    m_groupMap[m_identifier].erase(this);
+    m_grouped = false;
+}
+
+void
+DSSIPluginInstance::initialiseGroupMembership()
+{
+    if (!m_descriptor->run_multiple_synths) {
+	m_grouped = false;
+	return;
+    }
+
+    //!!! GroupMap is not actually thread-safe.
+
+    size_t pluginsInGroup = m_groupMap[m_identifier].size();
+
+    if (++pluginsInGroup > m_groupLocalEventBufferCount) {
+
+	size_t nextBufferCount = pluginsInGroup * 2;
+
+	snd_seq_event_t **eventLocalBuffers = new snd_seq_event_t *[nextBufferCount];
+
+	for (size_t i = 0; i < m_groupLocalEventBufferCount; ++i) {
+	    eventLocalBuffers[i] = m_groupLocalEventBuffers[i];
+	}
+	for (size_t i = m_groupLocalEventBufferCount; i < nextBufferCount; ++i) {
+	    eventLocalBuffers[i] = new snd_seq_event_t[EVENT_BUFFER_SIZE];
+	}
+
+	if (m_groupLocalEventBuffers) {
+	    m_bufferScavenger.claim(new ScavengerArrayWrapper<snd_seq_event_t *>
+				    (m_groupLocalEventBuffers));
+	}
+
+	m_groupLocalEventBuffers = eventLocalBuffers;
+	m_groupLocalEventBufferCount = nextBufferCount;
+    }
+
+    m_grouped = true;
+    m_groupMap[m_identifier].insert(this);
+}
+
+DSSIPluginInstance::~DSSIPluginInstance()
+{
+    std::cerr << "DSSIPluginInstance::~DSSIPluginInstance" << std::endl;
+
+    if (m_threads.find(m_instanceHandle) != m_threads.end()) {
+
+	for (std::set<NonRTPluginThread *>::iterator i =
+		 m_threads[m_instanceHandle].begin();
+	     i != m_threads[m_instanceHandle].end(); ++i) {
+
+	    (*i)->setExiting();
+	    (*i)->wait();
+	    delete *i;
+	}
+
+	m_threads.erase(m_instanceHandle);
+    }
+
+    detachFromGroup();
+
+    if (m_instanceHandle != 0) {
+	deactivate();
+    }
+
+    cleanup();
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i)
+        delete m_controlPortsIn[i].second;
+
+    for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i)
+        delete m_controlPortsOut[i].second;
+
+    m_controlPortsIn.clear();
+    m_controlPortsOut.clear();
+
+    if (m_ownBuffers) {
+	for (size_t i = 0; i < m_audioPortsIn.size(); ++i) {
+	    delete[] m_inputBuffers[i];
+	}
+	for (size_t i = 0; i < m_outputBufferCount; ++i) {
+	    delete[] m_outputBuffers[i];
+	}
+
+	delete[] m_inputBuffers;
+	delete[] m_outputBuffers;
+    }
+
+    m_audioPortsIn.clear();
+    m_audioPortsOut.clear();
+}
+
+
+void
+DSSIPluginInstance::instantiate(unsigned long sampleRate)
+{
+#ifdef DEBUG_DSSI
+    std::cout << "DSSIPluginInstance::instantiate - plugin \"unique\" id = "
+              << m_descriptor->LADSPA_Plugin->UniqueID << std::endl;
+#endif
+    if (!m_descriptor) return;
+
+    const LADSPA_Descriptor *descriptor = m_descriptor->LADSPA_Plugin;
+
+    if (!descriptor->instantiate) {
+	std::cerr << "Bad plugin: plugin id " << descriptor->UniqueID
+		  << ":" << descriptor->Label
+		  << " has no instantiate method!" << std::endl;
+	return;
+    }
+
+    m_instanceHandle = descriptor->instantiate(descriptor, sampleRate);
+
+    if (m_instanceHandle) {
+
+	if (m_descriptor->get_midi_controller_for_port) {
+
+	    for (unsigned long i = 0; i < descriptor->PortCount; ++i) {
+
+		if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i]) &&
+		    LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) {
+
+		    int controller = m_descriptor->get_midi_controller_for_port
+			(m_instanceHandle, i);
+
+		    if (controller != 0 && controller != 32 &&
+			DSSI_IS_CC(controller)) {
+
+			m_controllerMap[DSSI_CC_NUMBER(controller)] = i;
+		    }
+		}
+	    }
+	}
+    }
+}
+
+void
+DSSIPluginInstance::checkProgramCache() const
+{
+    if (m_programCacheValid) return;
+    m_cachedPrograms.clear();
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::checkProgramCache" << std::endl;
+#endif
+
+    if (!m_descriptor || !m_descriptor->get_program) {
+	m_programCacheValid = true;
+	return;
+    }
+
+    unsigned long index = 0;
+    const DSSI_Program_Descriptor *programDescriptor;
+    while ((programDescriptor = m_descriptor->get_program(m_instanceHandle, index))) {
+	++index;
+	ProgramDescriptor d;
+	d.bank = programDescriptor->Bank;
+	d.program = programDescriptor->Program;
+//!!!	d.name = QString("%1. %2").arg(index).arg(programDescriptor->Name);
+	d.name = programDescriptor->Name;
+	m_cachedPrograms.push_back(d);
+    }
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::checkProgramCache: have " << m_cachedPrograms.size() << " programs" << std::endl;
+#endif
+
+    m_programCacheValid = true;
+}
+
+QStringList
+DSSIPluginInstance::getPrograms() const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getPrograms" << std::endl;
+#endif
+
+    if (!m_descriptor) return QStringList();
+
+    checkProgramCache();
+
+    QStringList programs;
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+	programs.push_back(i->name);
+    }
+
+    return programs;
+}
+
+QString
+DSSIPluginInstance::getProgram(int bank, int program) const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getProgram(" << bank << "," << program << ")" << std::endl;
+#endif
+
+    if (!m_descriptor) return QString();
+
+    checkProgramCache();
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+	if (i->bank == bank && i->program == program) return i->name;
+    }
+
+    return QString();
+}
+
+unsigned long
+DSSIPluginInstance::getProgram(QString name) const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getProgram(" << name << ")" << std::endl;
+#endif
+
+    if (!m_descriptor) return 0;
+
+    checkProgramCache();
+
+    unsigned long rv;
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+	if (i->name == name) {
+	    rv = i->bank;
+	    rv = (rv << 16) + i->program;
+	    return rv;
+	}
+    }
+
+    return 0;
+}
+
+QString
+DSSIPluginInstance::getCurrentProgram() const
+{
+    return m_program;
+}
+
+void
+DSSIPluginInstance::selectProgram(QString program)
+{
+    selectProgramAux(program, true);
+}
+
+void
+DSSIPluginInstance::selectProgramAux(QString program, bool backupPortValues)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::selectProgram(" << program << ")" << std::endl;
+#endif
+
+    if (!m_descriptor) return;
+
+    checkProgramCache();
+
+    if (!m_descriptor->select_program) return;
+
+    bool found = false;
+    unsigned long bankNo = 0, programNo = 0;
+
+    for (std::vector<ProgramDescriptor>::iterator i = m_cachedPrograms.begin();
+	 i != m_cachedPrograms.end(); ++i) {
+
+	if (i->name == program) {
+
+	    bankNo = i->bank;
+	    programNo = i->program;
+	    found = true;
+
+#ifdef DEBUG_DSSI
+	    std::cerr << "DSSIPluginInstance::selectProgram(" << program << "): found at bank " << bankNo << ", program " << programNo << std::endl;
+#endif
+
+	    break;
+	}
+    }
+
+    if (!found) return;
+    m_program = program;
+
+    // DSSI select_program is an audio context call
+    m_processLock.lock();
+    m_descriptor->select_program(m_instanceHandle, bankNo, programNo);
+    m_processLock.unlock();
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::selectProgram(" << program << "): made select_program(" << bankNo << "," << programNo << ") call" << std::endl;
+#endif
+
+    if (backupPortValues) {
+	for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
+	    m_backupControlPortsIn[i] = *m_controlPortsIn[i].second;
+	}
+    }
+}
+
+void
+DSSIPluginInstance::activate()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::activate" << std::endl;
+#endif
+
+    if (!m_descriptor || !m_descriptor->LADSPA_Plugin->activate) return;
+    m_descriptor->LADSPA_Plugin->activate(m_instanceHandle);
+
+    if (!m_program.isEmpty()) {
+#ifdef DEBUG_DSSI
+	std::cerr << "DSSIPluginInstance::activate: restoring program " << m_program << std::endl;
+#endif
+	selectProgramAux(m_program, false);
+    }
+
+    for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
+#ifdef DEBUG_DSSI
+	std::cerr << "DSSIPluginInstance::activate: setting port " << m_controlPortsIn[i].first << " to " << m_backupControlPortsIn[i] << std::endl;
+#endif
+	*m_controlPortsIn[i].second = m_backupControlPortsIn[i];
+    }
+}
+
+void
+DSSIPluginInstance::connectPorts()
+{
+    if (!m_descriptor || !m_descriptor->LADSPA_Plugin->connect_port) return;
+    std::cerr << "DSSIPluginInstance::connectPorts: " << m_audioPortsIn.size() 
+	      << " audio ports in, " << m_audioPortsOut.size() << " out, "
+	      << m_outputBufferCount << " output buffers" << std::endl;
+
+    assert(sizeof(LADSPA_Data) == sizeof(float));
+    assert(sizeof(sample_t) == sizeof(float));
+
+    int inbuf = 0, outbuf = 0;
+
+    for (unsigned int i = 0; i < m_audioPortsIn.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_audioPortsIn[i],
+	     (LADSPA_Data *)m_inputBuffers[inbuf]);
+	++inbuf;
+    }
+
+    for (unsigned int i = 0; i < m_audioPortsOut.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_audioPortsOut[i],
+	     (LADSPA_Data *)m_outputBuffers[outbuf]);
+	++outbuf;
+    }
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_controlPortsIn[i].first,
+	     m_controlPortsIn[i].second);
+    }
+
+    for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i) {
+	m_descriptor->LADSPA_Plugin->connect_port
+	    (m_instanceHandle,
+	     m_controlPortsOut[i].first,
+	     m_controlPortsOut[i].second);
+    }
+}
+
+unsigned int
+DSSIPluginInstance::getParameterCount() const
+{
+    return m_controlPortsIn.size();
+}
+
+void
+DSSIPluginInstance::setParameterValue(unsigned int parameter, float value)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::setParameterValue(" << parameter << ") to " << value << std::endl;
+#endif
+    if (parameter >= m_controlPortsIn.size()) return;
+
+    unsigned int portNumber = m_controlPortsIn[parameter].first;
+
+    LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory);
+    if (f) {
+	if (value < f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber)) {
+	    value = f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber);
+	}
+	if (value > f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber)) {
+	    value = f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber);
+	}
+    }
+
+    (*m_controlPortsIn[parameter].second) = value;
+    m_backupControlPortsIn[parameter] = value;
+}
+
+void
+DSSIPluginInstance::setPortValueFromController(unsigned int port, int cv)
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::setPortValueFromController(" << port << ") to " << cv << std::endl;
+#endif
+
+    const LADSPA_Descriptor *p = m_descriptor->LADSPA_Plugin;
+    LADSPA_PortRangeHintDescriptor d = p->PortRangeHints[port].HintDescriptor;
+    LADSPA_Data lb = p->PortRangeHints[port].LowerBound;
+    LADSPA_Data ub = p->PortRangeHints[port].UpperBound;
+
+    float value = (float)cv;
+
+    if (!LADSPA_IS_HINT_BOUNDED_BELOW(d)) {
+	if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	    /* unbounded: might as well leave the value alone. */
+	} else {
+	    /* bounded above only. just shift the range. */
+	    value = ub - 127.0f + value;
+	}
+    } else {
+	if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	    /* bounded below only. just shift the range. */
+	    value = lb + value;
+	} else {
+	    /* bounded both ends.  more interesting. */
+	    /* XXX !!! todo: fill in logarithmic, sample rate &c */
+	    value = lb + ((ub - lb) * value / 127.0f);
+	}
+    }
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
+	if (m_controlPortsIn[i].first == port) {
+	    setParameterValue(i, value);
+	}
+    }
+}
+
+float
+DSSIPluginInstance::getParameterValue(unsigned int parameter) const
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::getParameterValue(" << parameter << ")" << std::endl;
+#endif
+    if (parameter >= m_controlPortsIn.size()) return 0.0;
+    return (*m_controlPortsIn[parameter].second);
+}
+
+float
+DSSIPluginInstance::getParameterDefault(unsigned int parameter) const
+{
+    if (parameter >= m_controlPortsIn.size()) return 0.0;
+
+    LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory);
+    if (f) {
+	return f->getPortDefault(m_descriptor->LADSPA_Plugin,
+				 m_controlPortsIn[parameter].first);
+    } else {
+	return 0.0f;
+    }
+}
+
+QString
+DSSIPluginInstance::configure(QString key,
+			      QString value)
+{
+    if (!m_descriptor || !m_descriptor->configure) return QString();
+
+    if (key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY) {
+#ifdef DSSI_PROJECT_DIRECTORY_KEY
+	key = DSSI_PROJECT_DIRECTORY_KEY;
+#else
+	return QString();
+#endif
+    }
+	
+    
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::configure(" << key << "," << value << ")" << std::endl;
+#endif
+
+    char *message = m_descriptor->configure(m_instanceHandle,
+					    key.toStdString().c_str(),
+					    value.toStdString().c_str());
+
+    m_programCacheValid = false;
+
+    QString qm;
+
+    // Ignore return values from reserved key configuration calls such
+    // as project directory
+#ifdef DSSI_RESERVED_CONFIGURE_PREFIX
+    if (key.startsWith(DSSI_RESERVED_CONFIGURE_PREFIX)) {
+	return qm;
+    }
+#endif
+
+    if (message) {
+	if (m_descriptor->LADSPA_Plugin && m_descriptor->LADSPA_Plugin->Label) {
+	    qm = QString(m_descriptor->LADSPA_Plugin->Label) + ": ";
+	}
+	qm = qm + message;
+	free(message);
+    }
+
+    return qm;
+}
+
+void
+DSSIPluginInstance::sendEvent(const RealTime &eventTime,
+			      const void *e)
+{
+    snd_seq_event_t *event = (snd_seq_event_t *)e;
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::sendEvent at " << eventTime << std::endl;
+#endif
+    snd_seq_event_t ev(*event);
+
+    ev.time.time.tv_sec = eventTime.sec;
+    ev.time.time.tv_nsec = eventTime.nsec;
+
+    // DSSI doesn't use MIDI channels, it uses run_multiple_synths instead.
+    ev.data.note.channel = 0;
+
+    m_eventBuffer.write(&ev, 1);
+}
+
+bool
+DSSIPluginInstance::handleController(snd_seq_event_t *ev)
+{
+    int controller = ev->data.control.param;
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::handleController " << controller << std::endl;
+#endif
+
+    if (controller == 0) { // bank select MSB
+	
+	m_pending.msb = ev->data.control.value;
+
+    } else if (controller == 32) { // bank select LSB
+
+	m_pending.lsb = ev->data.control.value;
+
+    } else if (controller > 0 && controller < 128) {
+	
+	if (m_controllerMap.find(controller) != m_controllerMap.end()) {
+	    int port = m_controllerMap[controller];
+	    setPortValueFromController(port, ev->data.control.value);
+	} else {
+	    return true; // pass through to plugin
+	}
+    }
+
+    return false;
+}
+
+void
+DSSIPluginInstance::run(const RealTime &blockTime)
+{
+    static snd_seq_event_t localEventBuffer[EVENT_BUFFER_SIZE];
+    int evCount = 0;
+
+    bool needLock = false;
+    if (m_descriptor->select_program) needLock = true;
+
+    if (needLock) {
+	if (!m_processLock.tryLock()) {
+	    for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) {
+		memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t));
+	    }
+	    return;
+	}
+    }
+
+    if (m_grouped) {
+	runGrouped(blockTime);
+	goto done;
+    }
+
+    if (!m_descriptor || !m_descriptor->run_synth) {
+	m_eventBuffer.skip(m_eventBuffer.getReadSpace());
+	if (m_descriptor->LADSPA_Plugin->run) {
+	    m_descriptor->LADSPA_Plugin->run(m_instanceHandle, m_blockSize);
+	} else {
+	    for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) {
+		memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t));
+	    }
+	}
+	m_run = true;
+	if (needLock) m_processLock.unlock();
+	return;
+    }
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::run(" << blockTime << ")" << std::endl;
+#endif
+
+#ifdef DEBUG_DSSI_PROCESS
+    if (m_eventBuffer.getReadSpace() > 0) {
+	std::cerr << "DSSIPluginInstance::run: event buffer has "
+		  << m_eventBuffer.getReadSpace() << " event(s) in it" << std::endl;
+    }
+#endif
+
+    while (m_eventBuffer.getReadSpace() > 0) {
+
+	snd_seq_event_t *ev = localEventBuffer + evCount;
+	*ev = m_eventBuffer.peekOne();
+	bool accept = true;
+
+	RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec);
+
+	int frameOffset = 0;
+	if (evTime > blockTime) {
+	    frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate);
+	}
+
+#ifdef DEBUG_DSSI_PROCESS
+	std::cerr << "DSSIPluginInstance::run: evTime " << evTime << ", blockTime " << blockTime << ", frameOffset " << frameOffset
+		  << ", blockSize " << m_blockSize << std::endl;
+	std::cerr << "Type: " << int(ev->type) << ", pitch: " << int(ev->data.note.note) << ", velocity: " << int(ev->data.note.velocity) << std::endl;
+#endif
+
+	if (frameOffset >= int(m_blockSize)) break;
+	if (frameOffset < 0) frameOffset = 0;
+
+	ev->time.tick = frameOffset;
+	m_eventBuffer.skip(1);
+
+	if (ev->type == SND_SEQ_EVENT_CONTROLLER) {
+	    accept = handleController(ev);
+	} else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) {
+	    m_pending.program = ev->data.control.value;
+	    accept = false;
+	}
+
+	if (accept) {
+	    if (++evCount >= EVENT_BUFFER_SIZE) break;
+	}
+    }
+
+    if (m_pending.program >= 0 && m_descriptor->select_program) {
+
+	int program = m_pending.program;
+	int bank = m_pending.lsb + 128 * m_pending.msb;
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::run: making select_program(" << bank << "," << program << ") call" << std::endl;
+#endif
+
+	m_pending.lsb = m_pending.msb = m_pending.program = -1;
+	m_descriptor->select_program(m_instanceHandle, bank, program);
+
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::run: made select_program(" << bank << "," << program << ") call" << std::endl;
+#endif
+    }
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::run: running with " << evCount << " events"
+	      << std::endl;
+#endif
+
+    m_descriptor->run_synth(m_instanceHandle, m_blockSize,
+			    localEventBuffer, evCount);
+
+#ifdef DEBUG_DSSI_PROCESS
+//    for (int i = 0; i < m_blockSize; ++i) {
+//	std::cout << m_outputBuffers[0][i] << " ";
+//	if (i % 8 == 0) std::cout << std::endl;
+//    }
+#endif
+
+ done:
+    if (needLock) m_processLock.unlock();
+
+    if (m_audioPortsOut.size() == 0) {
+	// copy inputs to outputs
+	for (size_t ch = 0; ch < m_idealChannelCount; ++ch) {
+	    size_t sch = ch % m_audioPortsIn.size();
+	    for (size_t i = 0; i < m_blockSize; ++i) {
+		m_outputBuffers[ch][i] = m_inputBuffers[sch][i];
+	    }
+	}
+    } else if (m_idealChannelCount < m_audioPortsOut.size()) {
+	if (m_idealChannelCount == 1) {
+	    // mix down to mono
+	    for (size_t ch = 1; ch < m_audioPortsOut.size(); ++ch) {
+		for (size_t i = 0; i < m_blockSize; ++i) {
+		    m_outputBuffers[0][i] += m_outputBuffers[ch][i];
+		}
+	    }
+	}
+    } else if (m_idealChannelCount > m_audioPortsOut.size()) {
+	// duplicate
+	for (size_t ch = m_audioPortsOut.size(); ch < m_idealChannelCount; ++ch) {
+	    size_t sch = (ch - m_audioPortsOut.size()) % m_audioPortsOut.size();
+	    for (size_t i = 0; i < m_blockSize; ++i) {
+		m_outputBuffers[ch][i] = m_outputBuffers[sch][i];
+	    }
+	}
+    }	
+
+    m_lastRunTime = blockTime;
+    m_run = true;
+}
+
+void
+DSSIPluginInstance::runGrouped(const RealTime &blockTime)
+{
+    // If something else in our group has just been called for this
+    // block time (but we haven't) then we should just write out the
+    // results and return; if we have just been called for this block
+    // time or nothing else in the group has been, we should run the
+    // whole group.
+
+    bool needRun = true;
+
+    PluginSet &s = m_groupMap[m_identifier];
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): this is " << this << "; " << s.size() << " elements in m_groupMap[" << m_identifier << "]" << std::endl;
+#endif
+
+    if (m_lastRunTime != blockTime) {
+	for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) {
+	    DSSIPluginInstance *instance = *i;
+	    if (instance != this && instance->m_lastRunTime == blockTime) {
+#ifdef DEBUG_DSSI_PROCESS
+		std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): plugin " << instance << " has already been run" << std::endl;
+#endif
+		needRun = false;
+	    }
+	}
+    }
+
+    if (!needRun) {
+#ifdef DEBUG_DSSI_PROCESS
+	std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): already run, returning" << std::endl;
+#endif
+	return;
+    }
+
+#ifdef DEBUG_DSSI_PROCESS
+    std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): I'm the first, running" << std::endl;
+#endif
+
+    size_t index = 0;
+    unsigned long *counts = (unsigned long *)
+	alloca(m_groupLocalEventBufferCount * sizeof(unsigned long));
+    LADSPA_Handle *instances = (LADSPA_Handle *)
+	alloca(m_groupLocalEventBufferCount * sizeof(LADSPA_Handle));
+
+    for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) {
+
+	if (index >= m_groupLocalEventBufferCount) break;
+
+	DSSIPluginInstance *instance = *i;
+	counts[index] = 0;
+	instances[index] = instance->m_instanceHandle;
+
+#ifdef DEBUG_DSSI_PROCESS
+	std::cerr << "DSSIPluginInstance::runGrouped(" << blockTime << "): running " << instance << std::endl;
+#endif
+
+	if (instance->m_pending.program >= 0 &&
+	    instance->m_descriptor->select_program) {
+	    int program = instance->m_pending.program;
+	    int bank = instance->m_pending.lsb + 128 * instance->m_pending.msb;
+	    instance->m_pending.lsb = instance->m_pending.msb = instance->m_pending.program = -1;
+	    instance->m_descriptor->select_program
+		(instance->m_instanceHandle, bank, program);
+	}
+
+	while (instance->m_eventBuffer.getReadSpace() > 0) {
+
+	    snd_seq_event_t *ev = m_groupLocalEventBuffers[index] + counts[index];
+	    *ev = instance->m_eventBuffer.peekOne();
+	    bool accept = true;
+
+	    RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec);
+
+	    int frameOffset = 0;
+	    if (evTime > blockTime) {
+		frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate);
+	    }
+
+#ifdef DEBUG_DSSI_PROCESS
+	    std::cerr << "DSSIPluginInstance::runGrouped: evTime " << evTime << ", frameOffset " << frameOffset
+		      << ", block size " << m_blockSize << std::endl;
+#endif
+
+	    if (frameOffset >= int(m_blockSize)) break;
+	    if (frameOffset < 0) frameOffset = 0;
+
+	    ev->time.tick = frameOffset;
+	    instance->m_eventBuffer.skip(1);
+
+	    if (ev->type == SND_SEQ_EVENT_CONTROLLER) {
+		accept = instance->handleController(ev);
+	    } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) {
+		instance->m_pending.program = ev->data.control.value;
+		accept = false;
+	    }
+
+	    if (accept) {
+		if (++counts[index] >= EVENT_BUFFER_SIZE) break;
+	    }
+	}
+
+	++index;
+    }
+
+    m_descriptor->run_multiple_synths(index,
+				      instances,
+				      m_blockSize,
+				      m_groupLocalEventBuffers,
+				      counts);
+}
+
+int
+DSSIPluginInstance::requestMidiSend(LADSPA_Handle instance,
+				    unsigned char ports,
+				    unsigned char channels)
+{
+    // This is called from a non-RT context (during instantiate)
+
+    std::cerr << "DSSIPluginInstance::requestMidiSend" << std::endl;
+    return 1;
+}
+
+void
+DSSIPluginInstance::midiSend(LADSPA_Handle instance,
+			     snd_seq_event_t *events,
+			     unsigned long eventCount)
+{
+    // This is likely to be called from an RT context
+
+    std::cerr << "DSSIPluginInstance::midiSend" << std::endl;
+}
+
+void
+DSSIPluginInstance::NonRTPluginThread::run()
+{
+    while (!m_exiting) {
+	m_runFunction(m_handle);
+	usleep(100000);
+    }
+}
+
+int
+DSSIPluginInstance::requestNonRTThread(LADSPA_Handle instance,
+				       void (*runFunction)(LADSPA_Handle))
+{
+    NonRTPluginThread *thread = new NonRTPluginThread(instance, runFunction);
+    m_threads[instance].insert(thread);
+    thread->start();
+    return 0;
+}
+
+void
+DSSIPluginInstance::deactivate()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::deactivate " << m_identifier << std::endl;
+#endif
+    if (!m_descriptor || !m_descriptor->LADSPA_Plugin->deactivate) return;
+
+    for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) {
+	m_backupControlPortsIn[i] = *m_controlPortsIn[i].second;
+    }
+
+    m_descriptor->LADSPA_Plugin->deactivate(m_instanceHandle);
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::deactivate " << m_identifier << " done" << std::endl;
+#endif
+
+    m_bufferScavenger.scavenge();
+}
+
+void
+DSSIPluginInstance::cleanup()
+{
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::cleanup " << m_identifier << std::endl;
+#endif
+    if (!m_descriptor) return;
+
+    if (!m_descriptor->LADSPA_Plugin->cleanup) {
+	std::cerr << "Bad plugin: plugin id "
+		  << m_descriptor->LADSPA_Plugin->UniqueID
+		  << ":" << m_descriptor->LADSPA_Plugin->Label
+		  << " has no cleanup method!" << std::endl;
+	return;
+    }
+
+    m_descriptor->LADSPA_Plugin->cleanup(m_instanceHandle);
+    m_instanceHandle = 0;
+#ifdef DEBUG_DSSI
+    std::cerr << "DSSIPluginInstance::cleanup " << m_identifier << " done" << std::endl;
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/DSSIPluginInstance.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,209 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _DSSIPLUGININSTANCE_H_
+#define _DSSIPLUGININSTANCE_H_
+
+#define DSSI_API_LEVEL 2
+
+#include <vector>
+#include <set>
+#include <map>
+#include <QString>
+#include <QMutex>
+#include <QThread>
+
+#include "api/dssi.h"
+
+#include "base/RingBuffer.h"
+#include "RealTimePluginInstance.h"
+#include "base/Scavenger.h"
+
+class DSSIPluginInstance : public RealTimePluginInstance
+{
+public:
+    virtual ~DSSIPluginInstance();
+
+    virtual bool isOK() const { return m_instanceHandle != 0; }
+
+    int getClientId() const { return m_client; }
+    virtual QString getIdentifier() const { return m_identifier; }
+    int getPosition() const { return m_position; }
+
+    virtual void run(const RealTime &);
+
+    virtual unsigned int getParameterCount() const;
+    virtual void setParameterValue(unsigned int parameter, float value);
+    virtual float getParameterValue(unsigned int parameter) const;
+    virtual float getParameterDefault(unsigned int parameter) const;
+    virtual QString configure(QString key, QString value);
+    virtual void sendEvent(const RealTime &eventTime,
+			   const void *event);
+
+    virtual size_t getBufferSize() const { return m_blockSize; }
+    virtual size_t getAudioInputCount() const { return m_audioPortsIn.size(); }
+    virtual size_t getAudioOutputCount() const { return m_idealChannelCount; }
+    virtual sample_t **getAudioInputBuffers() { return m_inputBuffers; }
+    virtual sample_t **getAudioOutputBuffers() { return m_outputBuffers; }
+
+    virtual QStringList getPrograms() const;
+    virtual QString getCurrentProgram() const;
+    virtual QString getProgram(int bank, int program) const;
+    virtual unsigned long getProgram(QString name) const;
+    virtual void selectProgram(QString program);
+
+    virtual bool isBypassed() const { return m_bypassed; }
+    virtual void setBypassed(bool bypassed) { m_bypassed = bypassed; }
+
+    virtual size_t getLatency();
+
+    virtual void silence();
+    virtual void discardEvents();
+    virtual void setIdealChannelCount(size_t channels); // may re-instantiate
+
+    virtual bool isInGroup() const { return m_grouped; }
+    virtual void detachFromGroup();
+
+protected:
+    // To be constructed only by DSSIPluginFactory
+    friend class DSSIPluginFactory;
+
+    // Constructor that creates the buffers internally
+    // 
+    DSSIPluginInstance(RealTimePluginFactory *factory,
+		       int client,
+		       QString identifier,
+		       int position,
+		       unsigned long sampleRate,
+		       size_t blockSize,
+		       int idealChannelCount,
+		       const DSSI_Descriptor* descriptor);
+    
+    // Constructor that uses shared buffers
+    // 
+    DSSIPluginInstance(RealTimePluginFactory *factory,
+		       int client,
+		       QString identifier,
+		       int position,
+		       unsigned long sampleRate,
+		       size_t blockSize,
+		       sample_t **inputBuffers,
+		       sample_t **outputBuffers,
+		       const DSSI_Descriptor* descriptor);
+
+    void init();
+    void instantiate(unsigned long sampleRate);
+    void cleanup();
+    void activate();
+    void deactivate();
+    void connectPorts();
+
+    bool handleController(snd_seq_event_t *ev);
+    void setPortValueFromController(unsigned int portNumber, int controlValue);
+    void selectProgramAux(QString program, bool backupPortValues);
+    void checkProgramCache() const;
+
+    void initialiseGroupMembership();
+    void runGrouped(const RealTime &);
+
+    // For use in DSSIPluginFactory (set in the DSSI_Host_Descriptor):
+    static int requestMidiSend(LADSPA_Handle instance,
+			       unsigned char ports,
+			       unsigned char channels);
+    static void midiSend(LADSPA_Handle instance,
+			 snd_seq_event_t *events,
+			 unsigned long eventCount);
+    static int requestNonRTThread(LADSPA_Handle instance,
+				  void (*runFunction)(LADSPA_Handle));
+
+    int                        m_client;
+    int                        m_position;
+    LADSPA_Handle              m_instanceHandle;
+    const DSSI_Descriptor     *m_descriptor;
+
+    std::vector<std::pair<unsigned long, LADSPA_Data*> > m_controlPortsIn;
+    std::vector<std::pair<unsigned long, LADSPA_Data*> > m_controlPortsOut;
+
+    std::vector<LADSPA_Data>  m_backupControlPortsIn;
+
+    std::map<int, int>        m_controllerMap;
+
+    std::vector<int>          m_audioPortsIn;
+    std::vector<int>          m_audioPortsOut;
+
+    struct ProgramControl {
+	int msb;
+	int lsb;
+	int program;
+    };
+    ProgramControl m_pending;
+
+    struct ProgramDescriptor {
+	int bank;
+	int program;
+	QString name;
+    };
+    mutable std::vector<ProgramDescriptor> m_cachedPrograms;
+    mutable bool m_programCacheValid;
+
+    RingBuffer<snd_seq_event_t> m_eventBuffer;
+
+    size_t                    m_blockSize;
+    sample_t                **m_inputBuffers;
+    sample_t                **m_outputBuffers;
+    bool                      m_ownBuffers;
+    size_t                    m_idealChannelCount;
+    size_t                    m_outputBufferCount;
+    size_t                    m_sampleRate;
+    float                    *m_latencyPort;
+    bool                      m_run;
+    
+    bool                      m_bypassed;
+    QString                   m_program;
+    bool                      m_grouped;
+    RealTime                  m_lastRunTime;
+
+    QMutex                    m_processLock;
+
+    typedef std::set<DSSIPluginInstance *> PluginSet;
+    typedef std::map<QString, PluginSet> GroupMap;
+    static GroupMap m_groupMap;
+    static snd_seq_event_t **m_groupLocalEventBuffers;
+    static size_t m_groupLocalEventBufferCount;
+
+    static Scavenger<ScavengerArrayWrapper<snd_seq_event_t *> > m_bufferScavenger;
+
+    class NonRTPluginThread : public QThread
+    {
+    public:
+	NonRTPluginThread(LADSPA_Handle handle,
+			  void (*runFunction)(LADSPA_Handle)) :
+	    m_handle(handle),
+	    m_runFunction(runFunction),
+	    m_exiting(false) { }
+
+	virtual void run();
+	void setExiting() { m_exiting = true; }
+
+    protected:
+	LADSPA_Handle m_handle;
+	void (*m_runFunction)(LADSPA_Handle);
+	bool m_exiting;
+    };
+    static std::map<LADSPA_Handle, std::set<NonRTPluginThread *> > m_threads;
+};
+
+#endif // _DSSIPLUGININSTANCE_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/FeatureExtractionPlugin.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,353 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _FEATURE_EXTRACTION_PLUGIN_H_
+#define _FEATURE_EXTRACTION_PLUGIN_H_
+
+/**
+ * A base class for feature extraction plugins.
+ */
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include "base/RealTime.h"
+
+/**
+ * FeatureExtractionPlugin is a base class for plugin instance classes
+ * that provide feature extraction from audio or related data.
+ *
+ * In most cases, the input will be audio and the output will be a
+ * stream of derived data at a lower sampling resolution than the
+ * input.
+ */
+
+class FeatureExtractionPlugin
+{
+public:
+    /**
+     * Initialise a plugin to prepare it for use with the given number
+     * of input channels, step size (window increment, in sample
+     * frames) and block size (window size, in sample frames).
+     *
+     * The input sample rate should have been already specified at
+     * construction time.
+     * 
+     * Return true for successful initialisation, false if the number
+     * of input channels, step size and/or block size cannot be
+     * supported.
+     */
+    virtual bool initialise(size_t inputChannels,
+			    size_t stepSize,
+			    size_t blockSize) = 0;
+
+    /**
+     * Reset the plugin after use, to prepare it for another clean
+     * run.  Not called for the first initialisation (i.e. initialise
+     * must also do a reset).
+     */
+    virtual void reset() = 0;
+
+    /**
+     * Get the computer-usable name of the plugin.  This should be
+     * reasonably short and contain no whitespace or punctuation
+     * characters.
+     */
+    virtual std::string getName() const = 0;
+
+    /**
+     * Get a human-readable description of the plugin.  This should be
+     * self-contained, as it may be shown to the user in isolation
+     * without also showing the plugin's "name".
+     */
+    virtual std::string getDescription() const = 0;
+
+    /**
+     * Get the name of the author or vendor of the plugin in
+     * human-readable form.
+     */
+    virtual std::string getMaker() const = 0;
+
+    /**
+     * Get the version number of the plugin.
+     */
+    virtual int getPluginVersion() const = 0;
+
+    /**
+     * Get the copyright statement or licensing summary of the plugin.
+     */
+    virtual std::string getCopyright() const = 0;
+
+    /**
+     * Get the preferred step size (window increment -- the distance
+     * in sample frames between the start frames of consecutive blocks
+     * passed to the process() function) for the plugin.  This should
+     * be called before initialise().
+     */
+    virtual size_t getPreferredStepSize() const = 0;
+
+    /**
+     * Get the preferred block size (window size -- the number of
+     * sample frames passed in each block to the process() function).
+     * This should be called before initialise().
+     */
+    virtual size_t getPreferredBlockSize() const { return getPreferredStepSize(); }
+
+    /**
+     * Get the minimum supported number of input channels.
+     */
+    virtual size_t getMinChannelCount() const { return 1; }
+
+    /**
+     * Get the maximum supported number of input channels.
+     */
+    virtual size_t getMaxChannelCount() const { return 1; }
+
+
+    struct OutputDescriptor
+    {
+	/**
+	 * The name of the output, in computer-usable form.  Should be
+	 * reasonably short and without whitespace or punctuation.
+	 */
+	std::string name;
+
+	/**
+	 * The human-readable name of the output.
+	 */
+	std::string description;
+
+	/**
+	 * The unit of the output, in human-readable form.
+	 */
+	std::string unit;
+
+	/**
+	 * True if the output has the same number of values per result
+	 * for every output result.  Outputs for which this is false
+	 * are unlikely to be very useful in a general-purpose host.
+	 */
+	bool hasFixedValueCount;
+
+	/**
+	 * The number of values per result of the output.  Undefined
+	 * if hasFixedValueCount is false.  If this is zero, the output
+	 * is point data (i.e. only the time of each output is of
+	 * interest, the value list will be empty).
+	 *
+	 * Note that this gives the number of values of a single
+	 * output result, not of the output stream (which has one more
+	 * dimension: time).
+	 */
+	size_t valueCount;
+
+	/**
+	 * True if the results in the output have a fixed numeric
+	 * range (minimum and maximum values).  Undefined if
+	 * valueCount is zero.
+	 */
+	bool hasKnownExtents;
+
+	/**
+	 * Minimum value of the results in the output.  Undefined if
+	 * hasKnownExtents is false or valueCount is zero.
+	 */
+	float minValue;
+
+	/**
+	 * Maximum value of the results in the output.  Undefined if
+	 * hasKnownExtents is false or valueCount is zero.
+	 */
+	float maxValue;
+
+	/**
+	 * True if the output values are quantized to a particular
+	 * resolution.  Undefined if valueCount is zero.
+	 */
+	bool isQuantized;
+
+	/**
+	 * Quantization resolution of the output values (e.g. 1.0 if
+	 * they are all integers).  Undefined if isQuantized is false
+	 * or valueCount is zero.
+	 */
+	float quantizeStep;
+
+	enum SampleType {
+
+	    /// Results from each process() align with that call's block start
+	    OneSamplePerStep,
+
+	    /// Results are evenly spaced in time (sampleRate specified below)
+	    FixedSampleRate,
+
+	    /// Results are unevenly spaced and have individual timestamps
+	    VariableSampleRate
+	};
+
+	/**
+	 * Positioning in time of the output results.
+	 */
+	SampleType sampleType;
+
+	/**
+	 * Sample rate of the output results.  Undefined if sampleType
+	 * is OneSamplePerStep.
+	 *
+	 * If sampleType is VariableSampleRate and this value is
+	 * non-zero, then it may be used to calculate a resolution for
+	 * the output (i.e. the "duration" of each value, in time).
+	 * It's recommended to set this to zero if that behaviour is
+	 * not desired.
+	 */
+	float sampleRate;
+    };
+
+    typedef std::vector<OutputDescriptor> OutputList;
+
+    /**
+     * Get the outputs of this plugin.  An output's index in this list
+     * is used as its numeric index when looking it up in the
+     * FeatureSet returned from the process() call.
+     */
+    virtual OutputList getOutputDescriptors() const = 0;
+
+
+    struct ParameterDescriptor
+    {
+	/**
+	 * The name of the parameter, in computer-usable form.  Should
+	 * be reasonably short and without whitespace or punctuation.
+	 */
+	std::string name;
+
+	/**
+	 * The human-readable name of the parameter.
+	 */
+	std::string description;
+
+	/**
+	 * The unit of the parameter, in human-readable form.
+	 */
+	std::string unit;
+
+	/**
+	 * The minimum value of the parameter.
+	 */
+	float minValue;
+
+	/**
+	 * The maximum value of the parameter.
+	 */
+	float maxValue;
+
+	/**
+	 * The default value of the parameter.
+	 */
+	float defaultValue;
+	
+	/**
+	 * True if the parameter values are quantized to a particular
+	 * resolution.
+	 */
+	bool isQuantized;
+
+	/**
+	 * Quantization resolution of the parameter values (e.g. 1.0
+	 * if they are all integers).  Undefined if isQuantized is
+	 * false.
+	 */
+	float quantizeStep;
+    };
+
+    typedef std::vector<ParameterDescriptor> ParameterList;
+
+    /**
+     * Get the controllable parameters of this plugin.
+     */
+    virtual ParameterList getParameterDescriptors() const {
+	return ParameterList();
+    }
+
+    /**
+     * Get the value of a named parameter.  The argument is the name
+     * field from that parameter's descriptor.
+     */
+    virtual float getParameter(std::string) const { return 0.0; }
+
+    /**
+     * Set a named parameter.  The first argument is the name field
+     * from that parameter's descriptor.
+     */
+    virtual void setParameter(std::string, float) { } 
+
+    struct Feature
+    {
+	/**
+	 * True if an output feature has its own timestamp.  This is
+	 * mandatory if the output has VariableSampleRate, and is
+	 * likely to be disregarded otherwise.
+	 */
+	bool hasTimestamp;
+
+	/**
+	 * Timestamp of the output feature.  This is mandatory if the
+	 * output has VariableSampleRate, and is likely to be
+	 * disregarded otherwise.  Undefined if hasTimestamp is false.
+	 */
+	RealTime timestamp;
+	
+	/**
+	 * Results for a single sample of this feature.  If the output
+	 * hasFixedValueCount, there must be the same number of values
+	 * as the output's valueCount count.
+	 */
+	std::vector<float> values;
+
+	/**
+	 * Label for the sample of this feature.
+	 */
+	std::string label;
+    };
+
+    typedef std::vector<Feature> FeatureList;
+    typedef std::map<int, FeatureList> FeatureSet; // key is output no
+
+    /**
+     * Process a single block of input data.  inputBuffers points to
+     * one array of floats per input channel, and each of those arrays
+     * contains the blockSize number of samples (the host will
+     * zero-pad as necessary).  The timestamp is the real time in
+     * seconds of the start of the supplied block of samples.
+     *
+     * Return any features that have become available after this
+     * process call.  (These do not necessarily have to fall within
+     * the process block, except for OneSamplePerStep outputs.)
+     */
+    virtual FeatureSet process(float **inputBuffers,
+			       RealTime timestamp) = 0;
+
+    /**
+     * After all blocks have been processed, calculate and return any
+     * remaining features derived from the complete input.
+     */
+    virtual FeatureSet getRemainingFeatures() = 0;
+
+protected:
+    FeatureExtractionPlugin(float inputSampleRate) :
+	m_inputSampleRate(inputSampleRate) { }
+
+    float m_inputSampleRate;
+};
+
+#endif
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/FeatureExtractionPluginFactory.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,100 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "FeatureExtractionPluginFactory.h"
+#include "PluginIdentifier.h"
+
+#include "plugins/BeatDetect.h" //!!!
+#include "plugins/ZeroCrossing.h" //!!!
+
+#include <iostream>
+
+static FeatureExtractionPluginFactory *_nativeInstance = 0;
+
+FeatureExtractionPluginFactory *
+FeatureExtractionPluginFactory::instance(QString pluginType)
+{
+    if (pluginType == "sv") {
+	if (!_nativeInstance) {
+	    std::cerr << "FeatureExtractionPluginFactory::instance(" << pluginType.toStdString()
+		      << "): creating new FeatureExtractionPluginFactory" << std::endl;
+	    _nativeInstance = new FeatureExtractionPluginFactory();
+	}
+	return _nativeInstance;
+    }
+
+    else return 0;
+}
+
+FeatureExtractionPluginFactory *
+FeatureExtractionPluginFactory::instanceFor(QString identifier)
+{
+    QString type, soName, label;
+    PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+    return instance(type);
+}
+
+std::vector<QString>
+FeatureExtractionPluginFactory::getAllPluginIdentifiers()
+{
+    FeatureExtractionPluginFactory *factory;
+    std::vector<QString> rv;
+    
+    factory = instance("sv");
+    if (factory) {
+	std::vector<QString> tmp = factory->getPluginIdentifiers();
+	for (size_t i = 0; i < tmp.size(); ++i) {
+	    rv.push_back(tmp[i]);
+	}
+    }
+
+    // Plugins can change the locale, revert it to default.
+    setlocale(LC_ALL, "C");
+    return rv;
+}
+
+std::vector<QString>
+FeatureExtractionPluginFactory::getPluginIdentifiers()
+{
+    std::vector<QString> rv;
+    rv.push_back("sv:_builtin:beats"); //!!!
+    rv.push_back("sv:_builtin:zerocrossing"); //!!!
+    return rv;
+}
+
+FeatureExtractionPlugin *
+FeatureExtractionPluginFactory::instantiatePlugin(QString identifier,
+						  float inputSampleRate)
+{
+    QString type, soName, label;
+    PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+    if (type != "sv") {
+	std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type.toStdString() << std::endl;
+	return 0;
+    }
+
+    //!!!
+    if (soName != PluginIdentifier::BUILTIN_PLUGIN_SONAME) {
+	std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Non-built-in plugins not yet supported (paradoxically enough)" << std::endl;
+	return 0;
+    }
+
+    if (label == "beats") {
+	return new BeatDetector(inputSampleRate); //!!!
+    }
+
+    if (label == "zerocrossing") {
+	return new ZeroCrossing(inputSampleRate); //!!!
+    }
+
+    std::cerr << "FeatureExtractionPluginFactory::instantiatePlugin: Unknown plugin \"" << identifier.toStdString() << "\"" << std::endl;
+    
+    return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/FeatureExtractionPluginFactory.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,35 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _FEATURE_EXTRACTION_PLUGIN_FACTORY_H_
+#define _FEATURE_EXTRACTION_PLUGIN_FACTORY_H_
+
+#include <QString>
+#include <vector>
+
+class FeatureExtractionPlugin;
+
+class FeatureExtractionPluginFactory
+{
+public:
+    static FeatureExtractionPluginFactory *instance(QString pluginType);
+    static FeatureExtractionPluginFactory *instanceFor(QString identifier);
+    static std::vector<QString> getAllPluginIdentifiers();
+
+    std::vector<QString> getPluginIdentifiers();
+
+    // We don't set blockSize or channels on this -- they're
+    // negotiated and handled via initialize() on the plugin
+    virtual FeatureExtractionPlugin *instantiatePlugin(QString identifier,
+						       float inputSampleRate);
+
+protected:
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/LADSPAPluginFactory.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,690 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam and Richard Bown.
+*/
+
+#include "LADSPAPluginFactory.h"
+#include <iostream>
+
+#include <QDir>
+#include <QFile>
+#include <QTextStream>
+
+#include <cmath>
+
+#include "LADSPAPluginInstance.h"
+#include "PluginIdentifier.h"
+
+#include "base/System.h"
+
+#ifdef HAVE_LIBLRDF
+#include "lrdf.h"
+#endif // HAVE_LIBLRDF
+
+
+LADSPAPluginFactory::LADSPAPluginFactory()
+{
+}
+ 
+LADSPAPluginFactory::~LADSPAPluginFactory()
+{
+    for (std::set<RealTimePluginInstance *>::iterator i = m_instances.begin();
+	 i != m_instances.end(); ++i) {
+	(*i)->setFactory(0);
+	delete *i;
+    }
+    m_instances.clear();
+    unloadUnusedLibraries();
+}
+
+const std::vector<QString> &
+LADSPAPluginFactory::getPluginIdentifiers() const
+{
+    return m_identifiers;
+}
+
+void
+LADSPAPluginFactory::enumeratePlugins(std::vector<QString> &list)
+{
+    for (std::vector<QString>::iterator i = m_identifiers.begin();
+	 i != m_identifiers.end(); ++i) {
+
+	const LADSPA_Descriptor *descriptor = getLADSPADescriptor(*i);
+
+	if (!descriptor) {
+	    std::cerr << "WARNING: LADSPAPluginFactory::enumeratePlugins: couldn't get descriptor for identifier " << i->toStdString() << std::endl;
+	    continue;
+	}
+	
+	list.push_back(*i);
+	list.push_back(descriptor->Name);
+	list.push_back(QString("%1").arg(descriptor->UniqueID));
+	list.push_back(descriptor->Label);
+	list.push_back(descriptor->Maker);
+	list.push_back(descriptor->Copyright);
+	list.push_back("false"); // is synth
+	list.push_back("false"); // is grouped
+	
+	if (m_taxonomy.find(descriptor->UniqueID) != m_taxonomy.end() &&
+	    m_taxonomy[descriptor->UniqueID] != "") {
+//		std::cerr << "LADSPAPluginFactory: cat for " << i->toStdString()<< " found in taxonomy as " << m_taxonomy[descriptor->UniqueID] << std::endl;
+	    list.push_back(m_taxonomy[descriptor->UniqueID]);
+
+	} else if (m_fallbackCategories.find(*i) !=
+		   m_fallbackCategories.end()) {
+	    list.push_back(m_fallbackCategories[*i]);
+//		std::cerr << "LADSPAPluginFactory: cat for " << i->toStdString()  <<" found in fallbacks as " << m_fallbackCategories[*i] << std::endl;
+
+	} else {
+	    list.push_back("");
+//		std::cerr << "LADSPAPluginFactory: cat for " << i->toStdString() << " not found (despite having " << m_fallbackCategories.size() << " fallbacks)" << std::endl;
+	    
+	}
+
+	list.push_back(QString("%1").arg(descriptor->PortCount));
+
+	for (unsigned long p = 0; p < descriptor->PortCount; ++p) {
+
+	    int type = 0;
+	    if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) {
+		type |= PortType::Control;
+	    } else {
+		type |= PortType::Audio;
+	    }
+	    if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) {
+		type |= PortType::Input;
+	    } else {
+		type |= PortType::Output;
+	    }
+
+	    list.push_back(QString("%1").arg(p));
+	    list.push_back(descriptor->PortNames[p]);
+	    list.push_back(QString("%1").arg(type));
+	    list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p)));
+	    list.push_back(QString("%1").arg(getPortMinimum(descriptor, p)));
+	    list.push_back(QString("%1").arg(getPortMaximum(descriptor, p)));
+	    list.push_back(QString("%1").arg(getPortDefault(descriptor, p)));
+	}
+    }
+
+    unloadUnusedLibraries();
+}
+	
+float
+LADSPAPluginFactory::getPortMinimum(const LADSPA_Descriptor *descriptor, int port)
+{
+    LADSPA_PortRangeHintDescriptor d =
+	descriptor->PortRangeHints[port].HintDescriptor;
+
+    float minimum = 0.0;
+		
+    if (LADSPA_IS_HINT_BOUNDED_BELOW(d)) {
+	float lb = descriptor->PortRangeHints[port].LowerBound;
+	minimum = lb;
+    } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	float ub = descriptor->PortRangeHints[port].UpperBound;
+	minimum = std::min(0.0, ub - 1.0);
+    }
+    
+    if (LADSPA_IS_HINT_SAMPLE_RATE(d)) {
+	minimum *= m_sampleRate;
+    }
+
+    return minimum;
+}
+
+float
+LADSPAPluginFactory::getPortMaximum(const LADSPA_Descriptor *descriptor, int port)
+{
+    LADSPA_PortRangeHintDescriptor d =
+	descriptor->PortRangeHints[port].HintDescriptor;
+
+    float maximum = 1.0;
+    
+    if (LADSPA_IS_HINT_BOUNDED_ABOVE(d)) {
+	float ub = descriptor->PortRangeHints[port].UpperBound;
+	maximum = ub;
+    } else {
+	float lb = descriptor->PortRangeHints[port].LowerBound;
+	maximum = lb + 1.0;
+    }
+    
+    if (LADSPA_IS_HINT_SAMPLE_RATE(d)) {
+	maximum *= m_sampleRate;
+    }
+
+    return maximum;
+}
+
+float
+LADSPAPluginFactory::getPortDefault(const LADSPA_Descriptor *descriptor, int port)
+{
+    float minimum = getPortMinimum(descriptor, port);
+    float maximum = getPortMaximum(descriptor, port);
+    float deft;
+
+    if (m_portDefaults.find(descriptor->UniqueID) != 
+	m_portDefaults.end()) {
+	if (m_portDefaults[descriptor->UniqueID].find(port) !=
+	    m_portDefaults[descriptor->UniqueID].end()) {
+
+	    deft = m_portDefaults[descriptor->UniqueID][port];
+	    if (deft < minimum) deft = minimum;
+	    if (deft > maximum) deft = maximum;
+	    return deft;
+	}
+    }
+
+    LADSPA_PortRangeHintDescriptor d =
+	descriptor->PortRangeHints[port].HintDescriptor;
+
+    bool logarithmic = LADSPA_IS_HINT_LOGARITHMIC(d);
+    
+    if (!LADSPA_IS_HINT_HAS_DEFAULT(d)) {
+	
+	deft = minimum;
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_MINIMUM(d)) {
+	
+	deft = minimum;
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_LOW(d)) {
+	
+	if (logarithmic) {
+	    deft = powf(10, log10(minimum) * 0.75 +
+			    log10(maximum) * 0.25);
+	} else {
+	    deft = minimum * 0.75 + maximum * 0.25;
+	}
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(d)) {
+	
+	if (logarithmic) {
+	    deft = powf(10, log10(minimum) * 0.5 +
+		   	    log10(maximum) * 0.5);
+	} else {
+	    deft = minimum * 0.5 + maximum * 0.5;
+	}
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_HIGH(d)) {
+	
+	if (logarithmic) {
+	    deft = powf(10, log10(minimum) * 0.25 +
+			    log10(maximum) * 0.75);
+	} else {
+	    deft = minimum * 0.25 + maximum * 0.75;
+	}
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(d)) {
+	
+	deft = maximum;
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_0(d)) {
+	
+	deft = 0.0;
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_1(d)) {
+	
+	deft = 1.0;
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_100(d)) {
+	
+	deft = 100.0;
+	
+    } else if (LADSPA_IS_HINT_DEFAULT_440(d)) {
+	
+	deft = 440.0;
+	
+    } else {
+	
+	deft = minimum;
+    }
+    
+    if (LADSPA_IS_HINT_SAMPLE_RATE(d)) {
+	deft *= m_sampleRate;
+    }
+
+    return deft;
+}
+
+int
+LADSPAPluginFactory::getPortDisplayHint(const LADSPA_Descriptor *descriptor, int port)
+{
+    LADSPA_PortRangeHintDescriptor d =
+	descriptor->PortRangeHints[port].HintDescriptor;
+    int hint = PortHint::NoHint;
+
+    if (LADSPA_IS_HINT_TOGGLED(d)) hint |= PortHint::Toggled;
+    if (LADSPA_IS_HINT_INTEGER(d)) hint |= PortHint::Integer;
+    if (LADSPA_IS_HINT_LOGARITHMIC(d)) hint |= PortHint::Logarithmic;
+
+    return hint;
+}
+
+
+RealTimePluginInstance *
+LADSPAPluginFactory::instantiatePlugin(QString identifier,
+				       int instrument,
+				       int position,
+				       unsigned int sampleRate,
+				       unsigned int blockSize,
+				       unsigned int channels)
+{
+    const LADSPA_Descriptor *descriptor = getLADSPADescriptor(identifier);
+
+    if (descriptor) {
+
+	LADSPAPluginInstance *instance =
+	    new LADSPAPluginInstance
+	    (this, instrument, identifier, position, sampleRate, blockSize, channels,
+	     descriptor);
+
+	m_instances.insert(instance);
+
+	return instance;
+    }
+
+    return 0;
+}
+
+void
+LADSPAPluginFactory::releasePlugin(RealTimePluginInstance *instance,
+				   QString identifier)
+{
+    if (m_instances.find(instance) == m_instances.end()) {
+	std::cerr << "WARNING: LADSPAPluginFactory::releasePlugin: Not one of mine!"
+		  << std::endl;
+	return;
+    }
+
+    QString type, soname, label;
+    PluginIdentifier::parseIdentifier(identifier, type, soname, label);
+
+    m_instances.erase(instance);
+
+    bool stillInUse = false;
+
+    for (std::set<RealTimePluginInstance *>::iterator ii = m_instances.begin();
+	 ii != m_instances.end(); ++ii) {
+	QString itype, isoname, ilabel;
+	PluginIdentifier::parseIdentifier((*ii)->getIdentifier(), itype, isoname, ilabel);
+	if (isoname == soname) {
+//	    std::cerr << "LADSPAPluginFactory::releasePlugin: dll " << soname.toStdString() << " is still in use for plugin " << ilabel << std::endl;
+	    stillInUse = true;
+	    break;
+	}
+    }
+    
+    if (!stillInUse) {
+//	std::cerr << "LADSPAPluginFactory::releasePlugin: dll " << soname.toStdString() << " no longer in use, unloading" << std::endl;
+	unloadLibrary(soname);
+    }
+}
+
+const LADSPA_Descriptor *
+LADSPAPluginFactory::getLADSPADescriptor(QString identifier)
+{
+    QString type, soname, label;
+    PluginIdentifier::parseIdentifier(identifier, type, soname, label);
+
+    if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
+	loadLibrary(soname);
+	if (m_libraryHandles.find(soname) == m_libraryHandles.end()) {
+	    std::cerr << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: loadLibrary failed for " << soname.toStdString() << std::endl;
+	    return 0;
+	}
+    }
+
+    void *libraryHandle = m_libraryHandles[soname];
+
+    LADSPA_Descriptor_Function fn = (LADSPA_Descriptor_Function)
+	DLSYM(libraryHandle, "ladspa_descriptor");
+
+    if (!fn) {
+	std::cerr << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No descriptor function in library " << soname.toStdString() << std::endl;
+	return 0;
+    }
+
+    const LADSPA_Descriptor *descriptor = 0;
+    
+    int index = 0;
+    while ((descriptor = fn(index))) {
+	if (descriptor->Label == label) return descriptor;
+	++index;
+    }
+
+    std::cerr << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No such plugin as " << label.toStdString() << " in library " << soname.toStdString() << std::endl;
+
+    return 0;
+}
+
+void
+LADSPAPluginFactory::loadLibrary(QString soName)
+{
+    void *libraryHandle = DLOPEN(soName, RTLD_NOW);
+    if (libraryHandle) m_libraryHandles[soName] = libraryHandle;
+}
+
+void
+LADSPAPluginFactory::unloadLibrary(QString soName)
+{
+    LibraryHandleMap::iterator li = m_libraryHandles.find(soName);
+    if (li != m_libraryHandles.end()) {
+//	std::cerr << "unloading " << soname.toStdString() << std::endl;
+	DLCLOSE(m_libraryHandles[soName]);
+	m_libraryHandles.erase(li);
+    }
+}
+
+void
+LADSPAPluginFactory::unloadUnusedLibraries()
+{
+    std::vector<QString> toUnload;
+
+    for (LibraryHandleMap::iterator i = m_libraryHandles.begin();
+	 i != m_libraryHandles.end(); ++i) {
+
+	bool stillInUse = false;
+
+	for (std::set<RealTimePluginInstance *>::iterator ii = m_instances.begin();
+	     ii != m_instances.end(); ++ii) {
+
+	    QString itype, isoname, ilabel;
+	    PluginIdentifier::parseIdentifier((*ii)->getIdentifier(), itype, isoname, ilabel);
+	    if (isoname == i->first) {
+		stillInUse = true;
+		break;
+	    }
+	}
+
+	if (!stillInUse) toUnload.push_back(i->first);
+    }
+
+    for (std::vector<QString>::iterator i = toUnload.begin();
+	 i != toUnload.end(); ++i) {
+	unloadLibrary(*i);
+    }
+}
+
+
+// It is only later, after they've gone,
+// I realize they have delivered a letter.
+// It's a letter from my wife.  "What are you doing
+// there?" my wife asks.  "Are you drinking?"
+// I study the postmark for hours.  Then it, too, begins to fade.
+// I hope someday to forget all this.
+
+
+std::vector<QString>
+LADSPAPluginFactory::getPluginPath()
+{
+    std::vector<QString> pathList;
+    std::string path;
+
+    char *cpath = getenv("LADSPA_PATH");
+    if (cpath) path = cpath;
+
+    if (path == "") {
+	path = "/usr/local/lib/ladspa:/usr/lib/ladspa";
+	char *home = getenv("HOME");
+	if (home) path = std::string(home) + "/.ladspa:" + path;
+    }
+
+    std::string::size_type index = 0, newindex = 0;
+
+    while ((newindex = path.find(':', index)) < path.size()) {
+	pathList.push_back(path.substr(index, newindex - index).c_str());
+	index = newindex + 1;
+    }
+    
+    pathList.push_back(path.substr(index).c_str());
+
+    return pathList;
+}
+
+
+#ifdef HAVE_LIBLRDF
+std::vector<QString>
+LADSPAPluginFactory::getLRDFPath(QString &baseUri)
+{
+    std::vector<QString> pathList = getPluginPath();
+    std::vector<QString> lrdfPaths;
+
+    lrdfPaths.push_back("/usr/local/share/ladspa/rdf");
+    lrdfPaths.push_back("/usr/share/ladspa/rdf");
+
+    for (std::vector<QString>::iterator i = pathList.begin();
+	 i != pathList.end(); ++i) {
+	lrdfPaths.push_back(*i + "/rdf");
+    }
+
+    baseUri = LADSPA_BASE;
+    return lrdfPaths;
+}    
+#endif
+
+void
+LADSPAPluginFactory::discoverPlugins()
+{
+    std::vector<QString> pathList = getPluginPath();
+
+//    std::cerr << "LADSPAPluginFactory::discoverPlugins - "
+//	      << "discovering plugins; path is ";
+    for (std::vector<QString>::iterator i = pathList.begin();
+	 i != pathList.end(); ++i) {
+	std::cerr << "[" << i->toStdString() << "] ";
+    }
+    std::cerr << std::endl;
+
+#ifdef HAVE_LIBLRDF
+    // Initialise liblrdf and read the description files 
+    //
+    lrdf_init();
+
+    QString baseUri;
+    std::vector<QString> lrdfPaths = getLRDFPath(baseUri);
+
+    bool haveSomething = false;
+
+    for (size_t i = 0; i < lrdfPaths.size(); ++i) {
+	QDir dir(lrdfPaths[i], "*.rdf;*.rdfs");
+	for (unsigned int j = 0; j < dir.count(); ++j) {
+	    if (!lrdf_read_file(QString("file:" + lrdfPaths[i] + "/" + dir[j]).toStdString().c_str())) {
+//		std::cerr << "LADSPAPluginFactory: read RDF file " << (lrdfPaths[i] + "/" + dir[j]) << std::endl;
+		haveSomething = true;
+	    }
+	}
+    }
+
+    if (haveSomething) {
+	generateTaxonomy(baseUri + "Plugin", "");
+    }
+#endif // HAVE_LIBLRDF
+
+    generateFallbackCategories();
+
+    for (std::vector<QString>::iterator i = pathList.begin();
+	 i != pathList.end(); ++i) {
+
+	QDir pluginDir(*i, PLUGIN_GLOB);
+
+	for (unsigned int j = 0; j < pluginDir.count(); ++j) {
+	    discoverPlugins(QString("%1/%2").arg(*i).arg(pluginDir[j]));
+	}
+    }
+
+#ifdef HAVE_LIBLRDF
+    // Cleanup after the RDF library
+    //
+    lrdf_cleanup();
+#endif // HAVE_LIBLRDF
+}
+
+void
+LADSPAPluginFactory::discoverPlugins(QString soname)
+{
+    void *libraryHandle = DLOPEN(soname, RTLD_LAZY);
+
+    if (!libraryHandle) {
+        std::cerr << "WARNING: LADSPAPluginFactory::discoverPlugins: couldn't load plugin library "
+                  << soname.toStdString() << " - " << DLERROR() << std::endl;
+        return;
+    }
+
+    LADSPA_Descriptor_Function fn = (LADSPA_Descriptor_Function)
+	DLSYM(libraryHandle, "ladspa_descriptor");
+
+    if (!fn) {
+	std::cerr << "WARNING: LADSPAPluginFactory::discoverPlugins: No descriptor function in " << soname.toStdString() << std::endl;
+	return;
+    }
+
+    const LADSPA_Descriptor *descriptor = 0;
+    
+    int index = 0;
+    while ((descriptor = fn(index))) {
+
+#ifdef HAVE_LIBLRDF
+	char *def_uri = 0;
+	lrdf_defaults *defs = 0;
+		
+	QString category = m_taxonomy[descriptor->UniqueID];
+	
+	if (category == "" && descriptor->Name != 0) {
+	    std::string name = descriptor->Name;
+	    if (name.length() > 4 &&
+		name.substr(name.length() - 4) == " VST") {
+		category = "VST effects";
+		m_taxonomy[descriptor->UniqueID] = category;
+	    }
+	}
+	
+//	std::cerr << "Plugin id is " << descriptor->UniqueID
+//		  << ", category is \"" << (category ? category : QString("(none)"))
+//		  << "\", name is " << descriptor->Name
+//		  << ", label is " << descriptor->Label
+//		  << std::endl;
+	
+	def_uri = lrdf_get_default_uri(descriptor->UniqueID);
+	if (def_uri) {
+	    defs = lrdf_get_setting_values(def_uri);
+	}
+
+	int controlPortNumber = 1;
+	
+	for (unsigned long i = 0; i < descriptor->PortCount; i++) {
+	    
+	    if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) {
+		
+		if (def_uri && defs) {
+		    
+		    for (int j = 0; j < defs->count; j++) {
+			if (defs->items[j].pid == controlPortNumber) {
+//			    std::cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << descriptor->PortNames[i] << std::endl;
+			    m_portDefaults[descriptor->UniqueID][i] =
+				defs->items[j].value;
+			}
+		    }
+		}
+		
+		++controlPortNumber;
+	    }
+	}
+#endif // HAVE_LIBLRDF
+
+	QString identifier = PluginIdentifier::createIdentifier
+	    ("ladspa", soname, descriptor->Label);
+	m_identifiers.push_back(identifier);
+
+	++index;
+    }
+
+    if (DLCLOSE(libraryHandle) != 0) {
+        std::cerr << "WARNING: LADSPAPluginFactory::discoverPlugins - can't unload " << libraryHandle << std::endl;
+        return;
+    }
+}
+
+void
+LADSPAPluginFactory::generateFallbackCategories()
+{
+    std::vector<QString> pluginPath = getPluginPath();
+    std::vector<QString> path;
+
+    for (size_t i = 0; i < pluginPath.size(); ++i) {
+	if (pluginPath[i].contains("/lib/")) {
+	    QString p(pluginPath[i]);
+	    p.replace("/lib/", "/share/");
+	    path.push_back(p);
+//	    std::cerr << "LADSPAPluginFactory::generateFallbackCategories: path element " << p << std::endl;
+	}
+	path.push_back(pluginPath[i]);
+//	std::cerr << "LADSPAPluginFactory::generateFallbackCategories: path element " << pluginPath[i] << std::endl;
+    }
+
+    for (size_t i = 0; i < path.size(); ++i) {
+
+	QDir dir(path[i], "*.cat");
+
+//	std::cerr << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << std::endl;
+	for (unsigned int j = 0; j < dir.count(); ++j) {
+
+	    QFile file(path[i] + "/" + dir[j]);
+
+//	    std::cerr << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i] + "/" + dir[j]) << std::endl;
+
+	    if (file.open(QIODevice::ReadOnly)) {
+//		    std::cerr << "...opened" << std::endl;
+		QTextStream stream(&file);
+		QString line;
+
+		while (!stream.atEnd()) {
+		    line = stream.readLine();
+//		    std::cerr << "line is: \"" << line << "\"" << std::endl;
+		    QString id = line.section("::", 0, 0);
+		    QString cat = line.section("::", 1, 1);
+		    m_fallbackCategories[id] = cat;
+//		    std::cerr << "set id \"" << id << "\" to cat \"" << cat << "\"" << std::endl;
+		}
+	    }
+	}
+    }
+}    
+
+void
+LADSPAPluginFactory::generateTaxonomy(QString uri, QString base)
+{
+#ifdef HAVE_LIBLRDF
+    lrdf_uris *uris = lrdf_get_instances(uri.toStdString().c_str());
+
+    if (uris != NULL) {
+	for (int i = 0; i < uris->count; ++i) {
+	    m_taxonomy[lrdf_get_uid(uris->items[i])] = base;
+	}
+	lrdf_free_uris(uris);
+    }
+
+    uris = lrdf_get_subclasses(uri.toStdString().c_str());
+
+    if (uris != NULL) {
+	for (int i = 0; i < uris->count; ++i) {
+	    char *label = lrdf_get_label(uris->items[i]);
+	    generateTaxonomy(uris->items[i],
+			     base + (base.length() > 0 ? " > " : "") + label);
+	}
+	lrdf_free_uris(uris);
+    }
+#endif
+}
+    
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/LADSPAPluginFactory.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,87 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam and Richard Bown.
+*/
+
+#ifndef _LADSPA_PLUGIN_FACTORY_H_
+#define _LADSPA_PLUGIN_FACTORY_H_
+
+#include "RealTimePluginFactory.h"
+#include "api/ladspa.h"
+
+#include <vector>
+#include <map>
+#include <set>
+#include <QString>
+
+class LADSPAPluginInstance;
+
+class LADSPAPluginFactory : public RealTimePluginFactory
+{
+public:
+    virtual ~LADSPAPluginFactory();
+
+    virtual void discoverPlugins();
+
+    virtual const std::vector<QString> &getPluginIdentifiers() const;
+
+    virtual void enumeratePlugins(std::vector<QString> &list);
+
+    virtual RealTimePluginInstance *instantiatePlugin(QString identifier,
+						      int clientId,
+						      int position,
+						      unsigned int sampleRate,
+						      unsigned int blockSize,
+						      unsigned int channels);
+
+    float getPortMinimum(const LADSPA_Descriptor *, int port);
+    float getPortMaximum(const LADSPA_Descriptor *, int port);
+    float getPortDefault(const LADSPA_Descriptor *, int port);
+    int getPortDisplayHint(const LADSPA_Descriptor *, int port);
+
+protected:
+    LADSPAPluginFactory();
+    friend class RealTimePluginFactory;
+
+    virtual std::vector<QString> getPluginPath();
+
+#ifdef HAVE_LIBLRDF
+    virtual std::vector<QString> getLRDFPath(QString &baseUri);
+#endif
+
+    virtual void discoverPlugins(QString soName);
+    virtual void generateTaxonomy(QString uri, QString base);
+    virtual void generateFallbackCategories();
+
+    virtual void releasePlugin(RealTimePluginInstance *, QString);
+
+    virtual const LADSPA_Descriptor *getLADSPADescriptor(QString identifier);
+
+    void loadLibrary(QString soName);
+    void unloadLibrary(QString soName);
+    void unloadUnusedLibraries();
+
+    std::vector<QString> m_identifiers;
+
+    std::map<unsigned long, QString> m_taxonomy;
+    std::map<QString, QString> m_fallbackCategories;
+    std::map<unsigned long, std::map<int, float> > m_portDefaults;
+
+    std::set<RealTimePluginInstance *> m_instances;
+
+    typedef std::map<QString, void *> LibraryHandleMap;
+    LibraryHandleMap m_libraryHandles;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/LADSPAPluginInstance.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,418 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam and Richard Bown.
+*/
+
+#include <iostream>
+#include <cassert>
+
+#include "LADSPAPluginInstance.h"
+#include "LADSPAPluginFactory.h"
+
+//#define DEBUG_LADSPA 1
+
+
+LADSPAPluginInstance::LADSPAPluginInstance(RealTimePluginFactory *factory,
+					   int clientId,
+					   QString identifier,
+                                           int position,
+					   unsigned long sampleRate,
+					   size_t blockSize,
+					   int idealChannelCount,
+                                           const LADSPA_Descriptor* descriptor) :
+    RealTimePluginInstance(factory, identifier),
+    m_client(clientId),
+    m_position(position),
+    m_instanceCount(0),
+    m_descriptor(descriptor),
+    m_blockSize(blockSize),
+    m_sampleRate(sampleRate),
+    m_latencyPort(0),
+    m_run(false),
+    m_bypassed(false)
+{
+    init(idealChannelCount);
+
+    m_inputBuffers  = new sample_t*[m_instanceCount * m_audioPortsIn.size()];
+    m_outputBuffers = new sample_t*[m_instanceCount * m_audioPortsOut.size()];
+
+    for (size_t i = 0; i < m_instanceCount * m_audioPortsIn.size(); ++i) {
+	m_inputBuffers[i] = new sample_t[blockSize];
+    }
+    for (size_t i = 0; i < m_instanceCount * m_audioPortsOut.size(); ++i) {
+	m_outputBuffers[i] = new sample_t[blockSize];
+    }
+
+    m_ownBuffers = true;
+
+    instantiate(sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+    }
+}
+
+LADSPAPluginInstance::LADSPAPluginInstance(RealTimePluginFactory *factory,
+					   int clientId,
+					   QString identifier,
+                                           int position,
+					   unsigned long sampleRate,
+					   size_t blockSize,
+					   sample_t **inputBuffers,
+					   sample_t **outputBuffers,
+                                           const LADSPA_Descriptor* descriptor) :
+    RealTimePluginInstance(factory, identifier),
+    m_client(clientId),
+    m_position(position),
+    m_instanceCount(0),
+    m_descriptor(descriptor),
+    m_blockSize(blockSize),
+    m_inputBuffers(inputBuffers),
+    m_outputBuffers(outputBuffers),
+    m_ownBuffers(false),
+    m_sampleRate(sampleRate),
+    m_latencyPort(0),
+    m_run(false),
+    m_bypassed(false)
+{
+    init();
+
+    instantiate(sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+    }
+}
+
+
+void
+LADSPAPluginInstance::init(int idealChannelCount)
+{
+#ifdef DEBUG_LADSPA
+    std::cerr << "LADSPAPluginInstance::init(" << idealChannelCount << "): plugin has "
+	      << m_descriptor->PortCount << " ports" << std::endl;
+#endif
+
+    // Discover ports numbers and identities
+    //
+    for (unsigned long i = 0; i < m_descriptor->PortCount; ++i)
+    {
+        if (LADSPA_IS_PORT_AUDIO(m_descriptor->PortDescriptors[i]))
+        {
+            if (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[i])) {
+#ifdef DEBUG_LADSPA
+		std::cerr << "LADSPAPluginInstance::init: port " << i << " is audio in" << std::endl;
+#endif
+                m_audioPortsIn.push_back(i);
+	    } else {
+#ifdef DEBUG_LADSPA
+		std::cerr << "LADSPAPluginInstance::init: port " << i << " is audio out" << std::endl;
+#endif
+                m_audioPortsOut.push_back(i);
+	    }
+        }
+        else
+        if (LADSPA_IS_PORT_CONTROL(m_descriptor->PortDescriptors[i]))
+        {
+	    if (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[i])) {
+#ifdef DEBUG_LADSPA
+		std::cerr << "LADSPAPluginInstance::init: port " << i << " is control in" << std::endl;
+#endif
+		LADSPA_Data *data = new LADSPA_Data(0.0);
+		m_controlPortsIn.push_back(
+                    std::pair<unsigned long, LADSPA_Data*>(i, data));
+	    } else {
+#ifdef DEBUG_LADSPA
+		std::cerr << "LADSPAPluginInstance::init: port " << i << " is control out" << std::endl;
+#endif
+		LADSPA_Data *data = new LADSPA_Data(0.0);
+		m_controlPortsOut.push_back(
+                    std::pair<unsigned long, LADSPA_Data*>(i, data));
+		if (!strcmp(m_descriptor->PortNames[i], "latency") ||
+		    !strcmp(m_descriptor->PortNames[i], "_latency")) {
+#ifdef DEBUG_LADSPA
+		    std::cerr << "Wooo! We have a latency port!" << std::endl;
+#endif
+		    m_latencyPort = data;
+		}
+	    }
+        }
+#ifdef DEBUG_LADSPA
+        else
+            std::cerr << "LADSPAPluginInstance::init - "
+                      << "unrecognised port type" << std::endl;
+#endif
+    }
+
+    m_instanceCount = 1;
+
+    if (idealChannelCount > 0) {
+	if (m_audioPortsIn.size() == 1) {
+	    // mono plugin: duplicate it if need be
+	    m_instanceCount = idealChannelCount;
+	}
+    }
+}
+
+size_t
+LADSPAPluginInstance::getLatency()
+{
+    if (m_latencyPort) {
+	if (!m_run) run(RealTime::zeroTime);
+	if (*m_latencyPort > 0) return (size_t)*m_latencyPort;
+    }
+    return 0;
+}
+
+void
+LADSPAPluginInstance::silence()
+{
+    if (isOK()) {
+	deactivate();
+	activate();
+    }
+}
+
+void
+LADSPAPluginInstance::setIdealChannelCount(size_t channels)
+{
+    if (m_audioPortsIn.size() != 1 || channels == m_instanceCount) {
+	silence();
+	return;
+    }
+
+    if (isOK()) {
+	deactivate();
+    }
+
+    //!!! don't we need to reallocate inputBuffers and outputBuffers?
+
+    cleanup();
+    m_instanceCount = channels;
+    instantiate(m_sampleRate);
+    if (isOK()) {
+	connectPorts();
+	activate();
+    }
+}
+
+
+LADSPAPluginInstance::~LADSPAPluginInstance()
+{
+#ifdef DEBUG_LADSPA
+    std::cerr << "LADSPAPluginInstance::~LADSPAPluginInstance" << std::endl;
+#endif
+
+    if (m_instanceHandles.size() != 0) { // "isOK()"
+	deactivate();
+    }
+
+    cleanup();
+
+    for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i)
+        delete m_controlPortsIn[i].second;
+
+    for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i)
+        delete m_controlPortsOut[i].second;
+
+    m_controlPortsIn.clear();
+    m_controlPortsOut.clear();
+
+    if (m_ownBuffers) {
+	for (size_t i = 0; i < m_audioPortsIn.size(); ++i) {
+	    delete[] m_inputBuffers[i];
+	}
+	for (size_t i = 0; i < m_audioPortsOut.size(); ++i) {
+	    delete[] m_outputBuffers[i];
+	}
+
+	delete[] m_inputBuffers;
+	delete[] m_outputBuffers;
+    }
+
+    m_audioPortsIn.clear();
+    m_audioPortsOut.clear();
+}
+
+
+void
+LADSPAPluginInstance::instantiate(unsigned long sampleRate)
+{
+#ifdef DEBUG_LADSPA
+    std::cout << "LADSPAPluginInstance::instantiate - plugin unique id = "
+              << m_descriptor->UniqueID << std::endl;
+#endif
+    if (!m_descriptor) return;
+
+    if (!m_descriptor->instantiate) {
+	std::cerr << "Bad plugin: plugin id " << m_descriptor->UniqueID
+		  << ":" << m_descriptor->Label
+		  << " has no instantiate method!" << std::endl;
+	return;
+    }
+
+    for (size_t i = 0; i < m_instanceCount; ++i) {
+	m_instanceHandles.push_back
+	    (m_descriptor->instantiate(m_descriptor, sampleRate));
+    }
+}
+
+void
+LADSPAPluginInstance::activate()
+{
+    if (!m_descriptor || !m_descriptor->activate) return;
+
+    for (std::vector<LADSPA_Handle>::iterator hi = m_instanceHandles.begin();
+	 hi != m_instanceHandles.end(); ++hi) {
+	m_descriptor->activate(*hi);
+    }
+}
+
+void
+LADSPAPluginInstance::connectPorts()
+{
+    if (!m_descriptor || !m_descriptor->connect_port) return;
+
+    assert(sizeof(LADSPA_Data) == sizeof(float));
+    assert(sizeof(sample_t) == sizeof(float));
+
+    int inbuf = 0, outbuf = 0;
+
+    for (std::vector<LADSPA_Handle>::iterator hi = m_instanceHandles.begin();
+	 hi != m_instanceHandles.end(); ++hi) {
+
+	for (unsigned int i = 0; i < m_audioPortsIn.size(); ++i) {
+	    m_descriptor->connect_port(*hi,
+				       m_audioPortsIn[i],
+				       (LADSPA_Data *)m_inputBuffers[inbuf]);
+	    ++inbuf;
+	}
+
+	for (unsigned int i = 0; i < m_audioPortsOut.size(); ++i) {
+	    m_descriptor->connect_port(*hi,
+				       m_audioPortsOut[i],
+				       (LADSPA_Data *)m_outputBuffers[outbuf]);
+	    ++outbuf;
+	}
+
+	// If there is more than one instance, they all share the same
+	// control port ins (and outs, for the moment, because we
+	// don't actually do anything with the outs anyway -- but they
+	// do have to be connected as the plugin can't know if they're
+	// not and will write to them anyway).
+
+	for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) {
+	    m_descriptor->connect_port(*hi,
+				       m_controlPortsIn[i].first,
+				       m_controlPortsIn[i].second);
+	}
+
+	for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i) {
+	    m_descriptor->connect_port(*hi,
+				       m_controlPortsOut[i].first,
+				       m_controlPortsOut[i].second);
+	}
+    }
+}
+
+unsigned int
+LADSPAPluginInstance::getParameterCount() const
+{
+    return m_controlPortsIn.size();
+}
+
+void
+LADSPAPluginInstance::setParameterValue(unsigned int parameter, float value)
+{
+    if (parameter >= m_controlPortsIn.size()) return;
+
+    unsigned int portNumber = m_controlPortsIn[parameter].first;
+
+    LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory);
+    if (f) {
+	if (value < f->getPortMinimum(m_descriptor, portNumber)) {
+	    value = f->getPortMinimum(m_descriptor, portNumber);
+	}
+	if (value > f->getPortMaximum(m_descriptor, portNumber)) {
+	    value = f->getPortMaximum(m_descriptor, portNumber);
+	}
+    }
+
+    (*m_controlPortsIn[parameter].second) = value;
+}
+
+float
+LADSPAPluginInstance::getParameterValue(unsigned int parameter) const
+{
+    if (parameter >= m_controlPortsIn.size()) return 0.0;
+    return (*m_controlPortsIn[parameter].second);
+}
+
+float
+LADSPAPluginInstance::getParameterDefault(unsigned int parameter) const
+{
+    if (parameter >= m_controlPortsIn.size()) return 0.0;
+
+    LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory);
+    if (f) {
+	return f->getPortDefault(m_descriptor, m_controlPortsIn[parameter].first);
+    } else {
+	return 0.0f;
+    }
+}
+
+void
+LADSPAPluginInstance::run(const RealTime &)
+{
+    if (!m_descriptor || !m_descriptor->run) return;
+
+    for (std::vector<LADSPA_Handle>::iterator hi = m_instanceHandles.begin();
+	 hi != m_instanceHandles.end(); ++hi) {
+        m_descriptor->run(*hi, m_blockSize);
+    }
+
+    m_run = true;
+}
+
+void
+LADSPAPluginInstance::deactivate()
+{
+    if (!m_descriptor || !m_descriptor->deactivate) return;
+
+    for (std::vector<LADSPA_Handle>::iterator hi = m_instanceHandles.begin();
+	 hi != m_instanceHandles.end(); ++hi) {
+        m_descriptor->deactivate(*hi);
+    }
+}
+
+void
+LADSPAPluginInstance::cleanup()
+{
+    if (!m_descriptor) return;
+
+    if (!m_descriptor->cleanup) {
+	std::cerr << "Bad plugin: plugin id " << m_descriptor->UniqueID
+		  << ":" << m_descriptor->Label
+		  << " has no cleanup method!" << std::endl;
+	return;
+    }
+
+    for (std::vector<LADSPA_Handle>::iterator hi = m_instanceHandles.begin();
+	 hi != m_instanceHandles.end(); ++hi) {
+	m_descriptor->cleanup(*hi);
+    }
+
+    m_instanceHandles.clear();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/LADSPAPluginInstance.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,123 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam and Richard Bown.
+*/
+
+#ifndef _LADSPAPLUGININSTANCE_H_
+#define _LADSPAPLUGININSTANCE_H_
+
+#include <vector>
+#include <set>
+#include <QString>
+
+#include "api/ladspa.h"
+#include "RealTimePluginInstance.h"
+
+// LADSPA plugin instance.  LADSPA is a variable block size API, but
+// for one reason and another it's more convenient to use a fixed
+// block size in this wrapper.
+//
+class LADSPAPluginInstance : public RealTimePluginInstance
+{
+public:
+    virtual ~LADSPAPluginInstance();
+
+    virtual bool isOK() const { return m_instanceHandles.size() != 0; }
+
+    int getClientId() const { return m_client; }
+    virtual QString getIdentifier() const { return m_identifier; }
+    int getPosition() const { return m_position; }
+
+    virtual void run(const RealTime &rt);
+
+    virtual unsigned int getParameterCount() const;
+    virtual void setParameterValue(unsigned int parameter, float value);
+    virtual float getParameterValue(unsigned int parameter) const;
+    virtual float getParameterDefault(unsigned int parameter) const;
+
+    virtual size_t getBufferSize() const { return m_blockSize; }
+    virtual size_t getAudioInputCount() const { return m_instanceCount * m_audioPortsIn.size(); }
+    virtual size_t getAudioOutputCount() const { return m_instanceCount * m_audioPortsOut.size(); }
+    virtual sample_t **getAudioInputBuffers() { return m_inputBuffers; }
+    virtual sample_t **getAudioOutputBuffers() { return m_outputBuffers; }
+
+    virtual bool isBypassed() const { return m_bypassed; }
+    virtual void setBypassed(bool bypassed) { m_bypassed = bypassed; }
+
+    virtual size_t getLatency();
+
+    virtual void silence();
+    virtual void setIdealChannelCount(size_t channels); // may re-instantiate
+
+protected:
+    // To be constructed only by LADSPAPluginFactory
+    friend class LADSPAPluginFactory;
+
+    // Constructor that creates the buffers internally
+    // 
+    LADSPAPluginInstance(RealTimePluginFactory *factory,
+			 int client,
+			 QString identifier,
+                         int position,
+			 unsigned long sampleRate,
+			 size_t blockSize,
+			 int idealChannelCount,
+                         const LADSPA_Descriptor* descriptor);
+
+    // Constructor that uses shared buffers
+    // 
+    LADSPAPluginInstance(RealTimePluginFactory *factory,
+			 int client,
+			 QString identifier,
+                         int position,
+			 unsigned long sampleRate,
+			 size_t blockSize,
+			 sample_t **inputBuffers,
+			 sample_t **outputBuffers,
+                         const LADSPA_Descriptor* descriptor);
+
+    void init(int idealChannelCount = 0);
+    void instantiate(unsigned long sampleRate);
+    void cleanup();
+    void activate();
+    void deactivate();
+
+    // Connection of data (and behind the scenes control) ports
+    //
+    void connectPorts();
+    
+    int                        m_client;
+    int                        m_position;
+    std::vector<LADSPA_Handle> m_instanceHandles;
+    size_t                     m_instanceCount;
+    const LADSPA_Descriptor   *m_descriptor;
+
+    std::vector<std::pair<unsigned long, LADSPA_Data*> > m_controlPortsIn;
+    std::vector<std::pair<unsigned long, LADSPA_Data*> > m_controlPortsOut;
+
+    std::vector<int>          m_audioPortsIn;
+    std::vector<int>          m_audioPortsOut;
+
+    size_t                    m_blockSize;
+    sample_t                **m_inputBuffers;
+    sample_t                **m_outputBuffers;
+    bool                      m_ownBuffers;
+    size_t                    m_sampleRate;
+    float                    *m_latencyPort;
+    bool                      m_run;
+    
+    bool                      m_bypassed;
+};
+
+#endif // _LADSPAPLUGININSTANCE_H_
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PluginDescription.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,15 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _PLUGIN_DESCRIPTION_H_
+#define _PLUGIN_DESCRIPTION_H_
+
+struct PluginDescription
+{
+	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PluginIdentifier.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,60 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include "PluginIdentifier.h"
+#include <iostream>
+
+QString
+PluginIdentifier::createIdentifier(QString type,
+				   QString soName,
+				   QString label)
+{
+    QString identifier = type + ":" + soName + ":" + label;
+    return identifier;
+}
+
+void
+PluginIdentifier::parseIdentifier(QString identifier,
+				  QString &type,
+				  QString &soName,
+				  QString &label)
+{
+    type = identifier.section(':', 0, 0);
+    soName = identifier.section(':', 1, 1);
+    label = identifier.section(':', 2);
+}
+
+bool
+PluginIdentifier::areIdentifiersSimilar(QString id1, QString id2)
+{
+    QString type1, type2, soName1, soName2, label1, label2;
+
+    parseIdentifier(id1, type1, soName1, label1);
+    parseIdentifier(id2, type2, soName2, label2);
+
+    if (type1 != type2 || label1 != label2) return false;
+
+    bool similar = (soName1.section('/', -1).section('.', 0, 0) ==
+		    soName2.section('/', -1).section('.', 0, 0));
+
+    return similar;
+}
+
+QString
+PluginIdentifier::BUILTIN_PLUGIN_SONAME = "_builtin";
+
+QString
+PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY = "__QMUL__:__RESERVED__:ProjectDirectoryKey";
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/PluginIdentifier.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,43 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _PLUGIN_IDENTIFIER_H_
+#define _PLUGIN_IDENTIFIER_H_
+
+#include <QString>
+
+
+// A plugin identifier is simply a string; this class provides methods
+// to parse it into its constituent bits (plugin type, DLL path and label).
+
+class PluginIdentifier {
+
+public:
+ 
+    static QString createIdentifier(QString type, QString soName, QString label);
+
+    static void parseIdentifier(QString identifier,
+				QString &type, QString &soName, QString &label);
+
+    static bool areIdentifiersSimilar(QString id1, QString id2);
+
+    // Fake soName for use with plugins that are actually compiled in
+    static QString BUILTIN_PLUGIN_SONAME;
+
+    // Not strictly related to identifiers
+    static QString RESERVED_PROJECT_DIRECTORY_KEY;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/RealTimePluginFactory.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,122 @@
+// -*- c-basic-offset: 4 -*-
+
+/*
+    Rosegarden-4
+    A sequencer and musical notation editor.
+
+    This program is Copyright 2000-2005
+        Guillaume Laurent   <glaurent@telegraph-road.org>,
+        Chris Cannam        <cannam@all-day-breakfast.com>,
+        Richard Bown        <bownie@bownie.com>
+
+    The moral right of the authors to claim authorship of this work
+    has been asserted.
+
+    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.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "RealTimePluginFactory.h"
+#include "PluginIdentifier.h"
+
+#include "LADSPAPluginFactory.h"
+#include "DSSIPluginFactory.h"
+
+#include <iostream>
+
+int RealTimePluginFactory::m_sampleRate = 48000;
+
+static LADSPAPluginFactory *_ladspaInstance = 0;
+static LADSPAPluginFactory *_dssiInstance = 0;
+
+RealTimePluginFactory *
+RealTimePluginFactory::instance(QString pluginType)
+{
+    if (pluginType == "ladspa") {
+	if (!_ladspaInstance) {
+	    std::cerr << "RealTimePluginFactory::instance(" << pluginType.toStdString()
+		      << "): creating new LADSPAPluginFactory" << std::endl;
+	    _ladspaInstance = new LADSPAPluginFactory();
+	    _ladspaInstance->discoverPlugins();
+	}
+	return _ladspaInstance;
+    } else if (pluginType == "dssi") {
+	if (!_dssiInstance) {
+	    std::cerr << "RealTimePluginFactory::instance(" << pluginType.toStdString()
+		      << "): creating new DSSIPluginFactory" << std::endl;
+	    _dssiInstance = new DSSIPluginFactory();
+	    _dssiInstance->discoverPlugins();
+	}
+	return _dssiInstance;
+    }
+	
+    else return 0;
+}
+
+RealTimePluginFactory *
+RealTimePluginFactory::instanceFor(QString identifier)
+{
+    QString type, soName, label;
+    PluginIdentifier::parseIdentifier(identifier, type, soName, label);
+    return instance(type);
+}
+
+std::vector<QString>
+RealTimePluginFactory::getAllPluginIdentifiers()
+{
+    RealTimePluginFactory *factory;
+    std::vector<QString> rv;
+    
+    // Query DSSI plugins before LADSPA ones.
+    // This is to provide for the interesting possibility of plugins
+    // providing either DSSI or LADSPA versions of themselves,
+    // returning both versions if the LADSPA identifiers are queried
+    // first but only the DSSI version if the DSSI identifiers are
+    // queried first.
+
+    factory = instance("dssi");
+    if (factory) {
+	const std::vector<QString> &tmp = factory->getPluginIdentifiers();
+	for (size_t i = 0; i < tmp.size(); ++i) {
+	    rv.push_back(tmp[i]);
+	}
+    }
+
+    factory = instance("ladspa");
+    if (factory) {
+	const std::vector<QString> &tmp = factory->getPluginIdentifiers();
+	for (size_t i = 0; i < tmp.size(); ++i) {
+	    rv.push_back(tmp[i]);
+	}
+    }
+
+    // Plugins can change the locale, revert it to default.
+    setlocale(LC_ALL, "C");
+    return rv;
+}
+
+void
+RealTimePluginFactory::enumerateAllPlugins(std::vector<QString> &list)
+{
+    RealTimePluginFactory *factory;
+
+    // Query DSSI plugins before LADSPA ones.
+    // This is to provide for the interesting possibility of plugins
+    // providing either DSSI or LADSPA versions of themselves,
+    // returning both versions if the LADSPA identifiers are queried
+    // first but only the DSSI version if the DSSI identifiers are
+    // queried first.
+
+    factory = instance("dssi");
+    if (factory) factory->enumeratePlugins(list);
+
+    factory = instance("ladspa");
+    if (factory) factory->enumeratePlugins(list);
+    
+    // Plugins can change the locale, revert it to default.
+    setlocale(LC_ALL, "C");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/RealTimePluginFactory.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,73 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _REALTIME_PLUGIN_FACTORY_H_
+#define _REALTIME_PLUGIN_FACTORY_H_
+
+#include <QString>
+#include <vector>
+
+class RealTimePluginInstance;
+
+class RealTimePluginFactory
+{
+public:
+    static RealTimePluginFactory *instance(QString pluginType);
+    static RealTimePluginFactory *instanceFor(QString identifier);
+    static std::vector<QString> getAllPluginIdentifiers();
+    static void enumerateAllPlugins(std::vector<QString> &);
+
+    static void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; }
+
+    /**
+     * Look up the plugin path and find the plugins in it.  Called 
+     * automatically after construction of a factory.
+     */
+    virtual void discoverPlugins() = 0;
+
+    /**
+     * Return a reference to a list of all plugin identifiers that can
+     * be created by this factory.
+     */
+    virtual const std::vector<QString> &getPluginIdentifiers() const = 0;
+
+    /**
+     * Append to the given list descriptions of all the available
+     * plugins and their ports.  This is in a standard format, see
+     * the LADSPA implementation for details.
+     */
+    virtual void enumeratePlugins(std::vector<QString> &list) = 0;
+
+    /**
+     * Instantiate a plugin.
+     */
+    virtual RealTimePluginInstance *instantiatePlugin(QString identifier,
+						      int clientId,
+						      int position,
+						      unsigned int sampleRate,
+						      unsigned int blockSize,
+						      unsigned int channels) = 0;
+
+protected:
+    RealTimePluginFactory() { }
+
+    // for call by RealTimePluginInstance dtor
+    virtual void releasePlugin(RealTimePluginInstance *, QString identifier) = 0;
+    friend class RealTimePluginInstance;
+
+    static int m_sampleRate;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/RealTimePluginInstance.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,33 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#include "RealTimePluginInstance.h"
+#include "RealTimePluginFactory.h"
+
+#include <iostream>
+
+
+RealTimePluginInstance::~RealTimePluginInstance()
+{
+    std::cerr << "RealTimePluginInstance::~RealTimePluginInstance" << std::endl;
+
+    if (m_factory) {
+	std::cerr << "Asking factory to release " << m_identifier.toStdString() << std::endl;
+
+	m_factory->releasePlugin(this, m_identifier);
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/RealTimePluginInstance.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,122 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2005 Chris Cannam.
+*/
+
+#ifndef _REALTIME_PLUGIN_INSTANCE_H_
+#define _REALTIME_PLUGIN_INSTANCE_H_
+
+#include <QString>
+#include <QStringList>
+#include <vector>
+
+#include "base/RealTime.h"
+
+class RealTimePluginFactory;
+	
+/**
+ * RealTimePluginInstance is a very trivial interface that an audio
+ * process can use to refer to an instance of a plugin without needing
+ * to know what type of plugin it is.
+ *
+ * The audio code calls run() on an instance that has been passed to
+ * it, and assumes that the passing code has already initialised the
+ * plugin, connected its inputs and outputs and so on, and that there
+ * is an understanding in place about the sizes of the buffers in use
+ * by the plugin.  All of this depends on the subclass implementation.
+ */
+
+// These names are taken from LADSPA, but the values are not
+// guaranteed to match
+
+namespace PortType { // ORable
+    static const int Input   = 1;
+    static const int Output  = 2;
+    static const int Control = 4;
+    static const int Audio   = 8;
+}
+
+namespace PortHint { // ORable
+    static const int NoHint  = 0;
+    static const int Toggled = 1;
+    static const int Integer = 2;
+    static const int Logarithmic = 4;
+    static const int SampleRate = 8;
+}
+
+class RealTimePluginInstance
+{
+public:
+    typedef float sample_t;
+
+    virtual ~RealTimePluginInstance();
+
+    virtual bool isOK() const = 0;
+
+    virtual QString getIdentifier() const = 0;
+
+    /**
+     * Run for one block, starting at the given time.  The start time
+     * may be of interest to synths etc that may have queued events
+     * waiting.  Other plugins can ignore it.
+     */
+    virtual void run(const RealTime &blockStartTime) = 0;
+    
+    virtual size_t getBufferSize() const = 0;
+
+    virtual size_t getAudioInputCount() const = 0;
+    virtual size_t getAudioOutputCount() const = 0;
+
+    virtual sample_t **getAudioInputBuffers() = 0;
+    virtual sample_t **getAudioOutputBuffers() = 0;
+
+    virtual QStringList getPrograms() const { return QStringList(); }
+    virtual QString getCurrentProgram() const { return QString(); }
+    virtual QString getProgram(int /* bank */, int /* program */) const { return QString(); }
+    virtual unsigned long getProgram(QString /* name */) const { return 0; } // bank << 16 + program
+    virtual void selectProgram(QString) { }
+
+    virtual unsigned int getParameterCount() const = 0;
+    virtual void setParameterValue(unsigned int parameter, float value) = 0;
+    virtual float getParameterValue(unsigned int parameter) const = 0;
+    virtual float getParameterDefault(unsigned int parameter) const = 0;
+
+    virtual QString configure(QString /* key */, QString /* value */) { return QString(); }
+
+    virtual void sendEvent(const RealTime & /* eventTime */,
+			   const void * /* event */) { }
+
+    virtual bool isBypassed() const = 0;
+    virtual void setBypassed(bool value) = 0;
+
+    // This should be called after setup, but while not actually playing.
+    virtual size_t getLatency() = 0;
+
+    virtual void silence() = 0;
+    virtual void discardEvents() { }
+    virtual void setIdealChannelCount(size_t channels) = 0; // must also silence(); may also re-instantiate
+
+    void setFactory(RealTimePluginFactory *f) { m_factory = f; } // ew
+
+protected:
+    RealTimePluginInstance(RealTimePluginFactory *factory, QString identifier) :
+	m_factory(factory), m_identifier(identifier) { }
+
+    RealTimePluginFactory *m_factory;
+    QString m_identifier;
+
+    friend class PluginFactory;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/FEAPI.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,531 @@
+////////////////////////////////////////////////////////////////////////////////////
+//     /*!  \file FEAPI.h 
+//          \brief interface of the feature extraction plugin. 
+//        <br><br>
+//        In the following, context function pointers typedefs are referred to as 
+//        functions.<br>
+//
+//        
+//        Create a new instance of the plugin with the function 
+//        ::FEAPI_CreatePluginInstance. The call of this function is mandatory.
+//        <br><br>
+//
+//        Initialize the plugin with the call of function
+//        ::FEAPI_InitializePlugin.<br><br>
+//
+//        Information about the plugin can be requested via the following 
+//        functions:<br>
+//        ::FEAPI_GetPluginAPIVersion, <br>
+//        ::FEAPI_GetPluginName, <br> 
+//        ::FEAPI_GetPluginVendor, <br>
+//        ::FEAPI_GetPluginVendorVersion, <br>
+//        ::FEAPI_GetPluginCopyright, <br>
+//        ::FEAPI_GetPluginDescription. <br>
+//        The call of these functions is optional. Combined with a call to
+//        ::FEAPI_GetPluginId, the plugin can be uniquely identified. <br><br>
+//
+//        The technical capabilities of the plugin can be requested via the call 
+//        of the function
+//        ::FEAPI_GetPluginProperty.<br><br>
+//
+//        To get the number of the features resp. results that are computed 
+//        by the plugin, call the function <br>
+//        ::FEAPI_GetPluginNumOfResults; <br>
+//        the function <br>
+//        ::FEAPI_GetPluginResultDescription gives you detailed information about the 
+//        meaning and usage of every result (see structure ::_ResultDescription
+//        for details). <br><br>
+//
+//        To get the number of the options/ parameter settings that can be 
+//        done before processing, call the function <br>
+//        ::FEAPI_GetPluginNumOfParameters; <br>
+//        the function <br>
+//        ::FEAPI_GetPluginParamDescription gives you detailed information about 
+//        the meaning and usage of every single parameter (see structure 
+//        ::FEAPI_ParameterDescription_t for details).<br><br>
+//
+//        To get or set a specific parameter value, call the function <br>
+//        ::FEAPI_GetPluginParameter resp. <br>
+//        ::FEAPI_SetPluginParameter. <br><br>
+//
+//        After the plugin is initialized, the actual processing can begin.
+//        The ::FEAPI_ProcessPlugin <br>
+//        function can be called to do the actual feature/result 
+//        calculation. ::FEAPI_ProcessPlugin expects subsequently new blocks 
+//        of audio data. Note that ::FEAPI_ProcessPlugin does not return 
+//        computed feature values.<br>
+//
+//        After finishing one ::FEAPI_ProcessPlugin call, zero, one or more 
+//        results can be available, depending on the plug-ins implementation. 
+//        To query the available number of values for every feature/result, 
+//        call <br>
+//        ::FEAPI_GetPluginSizeOfResult, <br>
+//        which returns the number of values for this
+//        result. Afterwards, the result values for each result can
+//        be requested via <br>
+//        ::FEAPI_GetPluginResult. Note that the memory for the results has to be 
+//        allocated by the host.<br><br>
+//
+//        To signal that no more audio data is available at the end (e.g. of
+//        the audio file), call <br>
+//        ::FEAPI_ProcessPluginDone and get the last results with 
+//        ::FEAPI_GetPluginResult if available.<br><br>
+//
+//        To flush the internal buffers, the function <br>
+//        ::FEAPI_ResetPlugin <br>
+//        may be called.
+//
+//        After all processing has been done, destroy the instance of the 
+//        plugin with the function <br>
+//        ::FEAPI_DestroyPluginInstance. <br>
+//        The call of this function is mandatory. <br><br>
+//
+//        <br><br><br>
+//        The Feature Extraction plugin API is released under a BSD style
+//        license. Please make all changes available to the authors.<br>
+//        Contact information: lerch <at> zplane.de.
+//
+//     */
+//
+//        Copyright (c) 2004-2005, Alexander Lerch, zplane.development GbR
+//        All rights reserved.
+//
+//        Redistribution and use in source and binary forms, with or without 
+//        modification, are permitted provided that the following conditions 
+//        are met:
+//
+//        *   Redistributions of source code must retain the above copyright 
+//            notice, this list of conditions and the following disclaimer.
+//        *   Redistributions in binary form must link to the FEAPI website
+//            http://www.sf.net/projects/feapi,
+//            reproduce this list of conditions and the following 
+//            disclaimer in the documentation and/or other materials 
+//            provided with the distribution.
+//        *   The name of the contributors to this software must not be used 
+//            to endorse or promote products derived from this software 
+//            without specific prior written permission.
+//
+//        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+//        "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+//        LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
+//        FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
+//        COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
+//        INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+//        BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+//        LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
+//        CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
+//        LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
+//        ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+//        POSSIBILITY OF SUCH DAMAGE.
+//
+////////////////////////////////////////////////////////////////////////////////////
+//  CVS INFORMATION
+//
+//  $RCSfile: FEAPI.h,v $
+//  $Author: alex_lerch $
+//  $Date: 2005/05/20 17:08:36 $
+//
+//  $Log: FEAPI.h,v $
+//  Revision 1.2  2005/05/20 17:08:36  alex_lerch
+//  - updated documentation
+//  - added "signal" typedef for inputs and results
+//  - changed function PluginCanDo to PluginGetProperty and changed the function itself to return values instead of bools
+//  - cosmetic changes
+//
+//  Revision 1.1.1.1  2005/03/30 14:54:40  alex_lerch
+//  first draft version requiring several updates:
+//  - interface check
+//  - check of plugin base class
+//  - implementation of host base class
+//  - update of outdated documentation
+//
+//
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+//                      !!!Do never ever edit this file!!!
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+
+/*! \brief avoid multiple header includes */
+#if !defined(FEAPI_HEADER_INCLUDED) 
+#define FEAPI_HEADER_INCLUDED
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Maximum string lengths. */
+const unsigned int FEAPI_uiMaxNameLength        = 1024; //!< maximum number of characters for a name string (including null terminator)
+const unsigned int FEAPI_uiMaxUnitLength        = 1024; //!< maximum number of characters for a unit string (including null terminator)
+const unsigned int FEAPI_uiMaxDescriptionLength = 4096; //!< maximum number of characters for a description string (including null terminator)
+
+////////////////////////////////////////////////////////////////////////////////////
+// interface structures and types
+/** @defgroup types Interface Structures and Types
+ *  @{
+ */
+
+
+/*! Structure describing properties and other information about one result/feature. */
+typedef struct FEAPI_SignalDescription_t_tag
+{
+    char    acName[FEAPI_uiMaxNameLength];                //!< name of the result/feature (e.g. "Spectral Centroid", "Loudness", etc.) 
+    char    acUnit[FEAPI_uiMaxUnitLength];                //!< unit of the result/feature (e.g. "dB", "sone", "Hz", etc.) 
+    char    acDescription[FEAPI_uiMaxDescriptionLength];  //!< description of the result/feature (clear text description) 
+    float   fRangeMin;              //!< minimum value of the result/feature (if no minimum value: minimum floating point value) 
+    float   fRangeMax;              //!< maximum value of the result/feature (if no maximum value: maximum floating point value) 
+    float   fQuantizedTo;           //!< quantization step size of the result/feature (e.g. 1 for integer result, -1 for no quantization) 
+    float   fSampleRate;            //!< sample rate of the result/feature output in Hz; -1 if sample rate equals input block length, -2 for non-equidistant samples
+} FEAPI_SignalDescription_t;
+
+
+/*! Structure describing properties and other information about one parameter. */
+typedef struct FEAPI_ParameterDescription_t_tag
+{
+    char    acName[FEAPI_uiMaxNameLength];                //!< name of the parameter (e.g. "Gain", "Sensitivity", etc.) 
+    char    acUnit[FEAPI_uiMaxUnitLength];                //!< unit of the parameter (e.g. "dB", "Hz", etc.) 
+    char    acDescription[FEAPI_uiMaxDescriptionLength];  //!< description of the parameter (clear text description) 
+    float   fRangeMin,              //!< minimum value of the parameter (if no minimum value: minimum floating point value) 
+            fRangeMax,              //!< maximum value of the parameter (if no maximum value: maximum floating point value) 
+            fDefaultValue;          //!< default value for the parameter 
+    float   fQuantizedTo;           //!< quantization step size of the parameter (e.g. 1 for integer quantization, -1 for no quantization) 
+    int     bIsChangeableInRealTime;//!< 0/false if the parameter has to be set before the processing starts and can not be changed during processing, 1 if the parameter can be changed during processing
+} FEAPI_ParameterDescription_t;
+
+
+/*! Structure for user/vendor defined commands. */
+typedef struct FEAPI_UserData_t_tag
+{
+    char    *pcUserString;          //!< user defined string value (memory could be freed by host after function call)
+    void    *pcUserData;            //!< pointer to user defined data
+} FEAPI_UserData_t;
+
+
+/*! Typedef for the plugin instance handle. */
+typedef void* FEAPI_PluginInstance_t;
+
+
+/*! This typedef is used for time stamps in seconds. 
+    For a block of data, the time stamp is defined to be the time at the beginning of the block. */
+typedef double FEAPI_TimeStamp_t;
+
+
+/*! This typedef is used for input and output data. 
+    At least for this version of the API, this is exactly float. */
+typedef float FEAPI_Signal_t;
+
+
+/*! Enumerator for possible error return values, if any other value than FEAPI_kNoError is returned, 
+    the function was not able to execute the specified operation(s).<br>
+    All error defines are negative. */
+typedef enum FEAPI_Error_t_tag
+{
+    FEAPI_kNoError           = 0,       //!< no error occurred
+    FEAPI_kUnspecifiedError  = -1,      //!< an error occurred, but the type is not yet specified
+    FEAPI_kUnknownError      = -9999    //!< an error occurred, but its type is not specifyable
+} FEAPI_Error_t;
+
+
+/*! Enumerator for retrieval of version info which can be resolved as major.minor.sub. */
+typedef enum FEAPI_VersionInfo_t_tag
+{
+    FEAPI_kMajorVersion    = 0,        //!< indicates the major version
+    FEAPI_kMinorVersion    = 1,        //!< indicates the minor version
+    FEAPI_kSubVersion      = 2         //!< indicates the sub version or bug-fix version
+} FEAPI_VersionInfo_t;
+
+
+/*! Enumerator for retrieval of information about what the plug supports. */
+typedef enum FEAPI_PluginProperty_t_tag
+{
+    FEAPI_kMinSampleRate    = 0,        //!< indicates the minimum sample rate
+    FEAPI_kMaxSampleRate    = 1,        //!< indicates the maximum sample rate
+    FEAPI_kMinChannels      = 2,        //!< indicates minimum number of channels
+    FEAPI_kMaxChannels      = 3,        //!< indicates maximum number of channels
+    FEAPI_kMinFrameSize     = 4,        //!< indicates minimum number of frames per process call
+    FEAPI_kMaxFrameSize     = 5,        //!< indicates maximum number of frames per process call
+    FEAPI_kOptFrameSize     = 6         //!< indicates optimal number of frames per process call
+} FEAPI_PluginProperty_t;
+
+/** @} */ 
+
+////////////////////////////////////////////////////////////////////////////////////
+// API function declaration
+/** @defgroup apifun API function pointers
+ *  @{
+ */
+
+    /*!
+     * Creates a new instance of the plugin
+     *
+     * @param phInstanceHandle : handle to the instance to be created
+     * @return FEAPI_Error_t  : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t   (*FEAPI_CreatePluginInstance_t) ( FEAPI_PluginInstance_t *phInstanceHandle );
+
+    /*!
+     * Destroys an instance of the plugin.
+     *
+     * @param phInstanceHandle : handle to the instance to be destroyed
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t   (*FEAPI_DestroyPluginInstance_t) ( FEAPI_PluginInstance_t *phInstanceHandle );
+
+    /*!
+     * initializes a new instance of the plugin
+     *
+     * @param hInstanceHandle : handle to the instance
+     * @param fInputSampleRate : sample rate of input(s) in Hz
+     * @param iNumberOfAudioChannels : number of input audio channels
+     * @param iHostApiMajorVersion : major version number of host
+     * @param pstUserData : pointer to user or vendor defined data (may be NULL)
+     *
+     * @return FEAPI_Error_t  : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t   (*FEAPI_InitializePlugin_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                            float               fInputSampleRate, 
+                                                            int                 iNumberOfAudioChannels,
+                                                            int                 iHostApiMajorVersion,
+                                                            FEAPI_UserData_t     *pstUserData);
+
+
+    /*! 
+     * Gets the version number (major, minor or subversion) of the API used by the plugin.
+     * This is *not* the plugin version, therefore the function may be called without a previously 
+     * created instance.
+     * 
+     * @param eAPIMajorMinorOrSubVersion : flag which version type is requested
+     * @return int  : requested version number 
+     */
+    typedef int (*FEAPI_GetPluginAPIVersion_t) ( FEAPI_VersionInfo_t eAPIMajorMinorOrSubVersion ); //!< \todo change ints to (unsigned) ints where appropriate?
+
+
+    /*!
+     * Gets the name of the plugin.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param *pcPluginName : pointer to buffer of FEAPI_uiMaxNameLength chars, the name will be copied to this buffer
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginName_t) ( FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                    char    *pcPluginName);
+
+    /*!
+     * Gets the vendor name of the plugin.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param *pcPluginVendor : pointer to buffer of FEAPI_uiMaxNameLength chars, the vendor name will be copied to this buffer
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginVendor_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                    char    *pcPluginVendor);
+
+    /*!
+     * Gets an indication of the plugins capabilities.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param ePluginProperty : requested property 
+     * @return float  : corresponding value
+     */
+    typedef float (*FEAPI_GetPluginProperty_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                FEAPI_PluginProperty_t ePluginProperty);
+
+    /*!
+     * Gets the (vendor unique) plugin identification string.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param *pcPluginId : pointer to buffer of FEAPI_uiMaxNameLength chars, the id will be copied to this buffer
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginId_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                    char *pcPluginId);
+
+    /*!
+     * Gets the vendor version of the plugin.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param ePluginMajorMinorOrSubVersion : flag which version type is requested
+     * @return int  : requested version number 
+     */
+    typedef int (*FEAPI_GetPluginVendorVersion_t) ( FEAPI_PluginInstance_t  hInstanceHandle, 
+                                                    FEAPI_VersionInfo_t     ePluginMajorMinorOrSubVersion); 
+
+    /*!
+     * Gets the description of the plugin, containing information about what the plugin actually does.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param *pcPluginDescription : pointer to buffer of FEAPI_uiMaxDescriptionLength chars, the plugin description will be copied to this buffer
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginDescription_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                            char *pcPluginDescription);
+
+    /*!
+     * Gets the copyright information for the plugin.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param *pcPluginCopyright : pointer to buffer of FEAPI_uiMaxDescriptionLength chars, the plugin copyright information will be copied to this buffer
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginCopyright_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                          char *pcPluginCopyright);
+
+    /*!
+     * Gets the number of inputs for the plugin. This number will equal the 
+     * number of audio channels in many cases, otherwise exceed the number of audio channels. 
+     * The additional input channels are plugin developer specific and not recommended. If used,
+     * they have to be routed host internally.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @return int  : number of inputs
+     */
+    typedef int (*FEAPI_GetPluginNumOfInputs_t) (FEAPI_PluginInstance_t hInstanceHandle);
+
+    /*!
+     * Gets information about one of the possible inputs.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iInputIndex : index of the input the description is requested for, index ranges from 0...NumOfInputs-1
+     * @param *pstInputDescription : the requested description will be copied to this structure
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginInputDescription_t) (FEAPI_PluginInstance_t      hInstanceHandle, 
+                                                                int                         iInputIndex, 
+                                                                FEAPI_SignalDescription_t   *pstInputDescription);
+
+    /*!
+     * Gets the number of parameters.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @return int  : number of parameters
+     */
+    typedef int (*FEAPI_GetPluginNumOfParameters_t) (FEAPI_PluginInstance_t hInstanceHandle);
+
+    /*!
+     * Gets information about one of the possible parameters.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iParameterIndex : index of the parameter (setting) the description is requested for, index ranges from 0...NumOfParameters-1
+     * @param *pstParameterDescription : the requested description will be copied to this structure
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginParameterDescription_t) (FEAPI_PluginInstance_t          hInstanceHandle, 
+                                                                    int                             iParameterIndex, 
+                                                                    FEAPI_ParameterDescription_t    *pstParameterDescription);
+
+    /*!
+     * Sets a parameter to a specified value.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iParameterIndex : index of the parameter (setting) to be changed, index ranges from 0...NumOfParameters-1
+     * @param fValue : new value of the parameter
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_SetPluginParameter_t) (   FEAPI_PluginInstance_t  hInstanceHandle, 
+                                                            int                     iParameterIndex, 
+                                                            float                   fValue);
+
+    /*!
+     * Gets the current value of a parameter.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iParameterIndex : index of the parameter (setting) requested, index ranges from 0...NumOfParameters-1
+     * @return float  : value of the parameter with index iParameterIndex
+     */
+    typedef float (*FEAPI_GetPluginParameter_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                    int iParameterIndex);
+
+    /*!
+     * Processes a block of audio data.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param **ppfInputBuffer : input audio data in the format [channels][samples]; audio samples have a range from -1.0...+1.0
+     * @param *ptFEAPI_TimeStamp : time stamps in seconds for every input, may be NULL
+     * @param iNumberOfFrames : number of frames in ppfInputBuffer
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_ProcessPlugin_t) (FEAPI_PluginInstance_t  hInstanceHandle, 
+                                                    const FEAPI_Signal_t    **ppfInputBuffer, 
+                                                    const FEAPI_TimeStamp_t *ptFEAPI_TimeStamp, 
+                                                    int                     iNumberOfFrames);           
+
+    /*!
+     * Signals that no more input data is available (does the final processing).
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_ProcessPluginDone_t) (FEAPI_PluginInstance_t hInstanceHandle);
+
+    /*!
+     * Gets the number of results/features to be calculated.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @return int  : number of results
+     */
+    typedef int (*FEAPI_GetPluginNumOfResults_t) (FEAPI_PluginInstance_t hInstanceHandle);
+
+    /*!
+     * Gets information about one of the possible results/features.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iResultIndex : index of the result (feature) the description is requested for, index ranges from 0...NumOfResults-1
+     * @param *pstResultDescription : the requested description will be copied to this structure
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginResultDescription_t) (   FEAPI_PluginInstance_t      hInstanceHandle, 
+                                                                    int                         iResultIndex, 
+                                                                    FEAPI_SignalDescription_t   *pstResultDescription);
+
+    /*!
+     * Gets the size of one result in FEAPI_Signal_t values (4 bytes per value).
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iResultIndex : index of the result/feature the size is requested for, index ranges from 0...NumOfResults-1
+     * @return int : size of result in FEAPI_Signal_t values (4 bytes per value)
+     */
+    typedef int (*FEAPI_GetPluginSizeOfResult_t) (FEAPI_PluginInstance_t hInstanceHandle, 
+                                                                    int iResultIndex);
+
+    /*!
+     * Gets one result.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iResultIndex : index of the requested result/feature, index ranges from 0...NumOfResults-1
+     * @param *pfResult : the result data is copied into this buffer
+     * @param *ptFEAPI_TimeStamp : the time stamp of the result
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_GetPluginResult_t) (  FEAPI_PluginInstance_t  hInstanceHandle, 
+                                                        int                     iResultIndex, 
+                                                        FEAPI_Signal_t          *pfResult, 
+                                                        FEAPI_TimeStamp_t       *ptFEAPI_TimeStamp);           
+
+    /*!
+     * Gets the maximum latency of one result. Since the host buffer size may vary, this is only the 
+     * *internal* latency.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @param iResultIndex : index of the requested result (feature), index ranges from 0...NumOfResults-1
+     * @return int  : number of samples (at audio input sample rate) which is required to calculate this result the first time (negative values are not allowed, 0 means undefined)
+     */
+    typedef int (*FEAPI_GetPluginResultLatency_t) (FEAPI_PluginInstance_t hInstanceHandle, int iResultIndex); 
+
+    /*!
+     * Resets/clears all internal buffers and states, so that the plugin is in a state where it can start processing a new audio stream.
+     *
+     * @param hInstanceHandle : handle to instance 
+     * @return FEAPI_Error_t : FEAPI_kNoError when no error
+     */
+    typedef FEAPI_Error_t (*FEAPI_ResetPlugin_t) (FEAPI_PluginInstance_t hInstanceHandle);
+
+/** @} */ 
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // #if !defined(FEAPI_HEADER_INCLUDED)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/README	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,169 @@
+libdssialsacompat 1.0.8a
+========================
+libdssialsacompat is simply an extraction from and repackaging of
+the code from alsa-lib 1.0.8, necessary to support DSSI on non-ALSA
+systems.  It is copyright (c)2005 under the GNU Lesser General
+Public License, version 2.1 or later.  See the enclosed file COPYING
+for details.
+
+More information on DSSI can be found at:
+
+  http://dssi.sourceforge.net/
+
+Introduction
+============
+The DSSI specification makes use of the ALSA snd_seq_event_t
+structure for passing MIDI events.  This has the advantage of making
+it immediately familiar to developers familiar with ALSA, but the
+disadvantage of making porting DSSI applications and plugins to
+systems that lack ALSA more difficult.
+
+libdssialsacompat is intended to provide the snd_seq_event_t
+declarations and handling code necessary to compile and use DSSI on
+non-ALSA systems.  It aims to allows compiling DSSI code with as
+little change as possible, while not presenting itself in such a way
+as to fool other autoconf-enabled code into thinking a system has
+ALSA.
+
+libdssialsacompat is simply an extraction of the relevant
+snd_seq_event_t declarations, and raw MIDI stream to snd_seq_event_t
+encoder code, from alsa-lib version 1.0.8, packaged into a
+convenient library.
+
+This library does NOT provide any sort of emulation of the ALSA
+audio, MIDI, or sequencer devices.  The only part of ALSA that is
+required by the DSSI specification is the snd_seq_event_t definition
+and handling, and that is all libdssialsacompat is intended to
+replace.  Other ALSA code should be ported to native facilities.
+
+Installation
+============
+libdssialsacompat uses GNU autoconf and automake, so installation can
+be a simple as `./configure && make && make install'.  See the
+enclosed file INSTALL for more information.
+
+The library itself is installed to <prefix>/lib; for example, on Mac
+OS X, the following files are installed:
+
+    <prefix>/lib/libdssialsacompat.0.0.0.dylib
+    <prefix>/lib/libdssialsacompat.0.dylib
+    <prefix>/lib/libdssialsacompat.a
+    <prefix>/lib/libdssialsacompat.dylib
+    <prefix>/lib/libdssialsacompat.la
+
+The header files are installed to <prefix>/include/dssi/alsa:
+
+    <prefix>/include/dssi/alsa/asoundef.h
+    <prefix>/include/dssi/alsa/asoundlib.h
+    <prefix>/include/dssi/alsa/seq.h
+    <prefix>/include/dssi/alsa/seq_event.h
+    <prefix>/include/dssi/alsa/seq_midi_event.h
+    <prefix>/include/dssi/alsa/sound/asequencer.h
+
+Note that they are NOT installed to <prefix>/include/alsa, which
+could make them visible to non-libdssialsacompat-aware autoconf
+configure scripts, possibly fooling them into thinking the full ALSA
+was available.
+
+Finally, a pkgconfig configuration file is installed:
+
+    <prefix>/lib/pkgconfig/libdssialsacompat.pc
+
+Use
+===
+At its most basic, compiling with gcc and libdssialsacompat consists
+of adding '-I<prefix>/include/dssi -L<prefix>/lib -ldssialsacompat'
+to your gcc command line.  Note that the '-I' directive will cause
+code like to following:
+
+    #include <alsa/asoundlib.h>
+    #include <alsa/seq_event.h>
+
+to find the libdssialsacompat header files, even though they are not
+installed in the usual location for ALSA headers.
+
+libdssialsacompat is easiest to use with autoconf/automake/pkgconfig-
+enabled code.  In which case, editing the configure.in or configure.ac
+file and changing:
+
+    PKG_CHECK_MODULES(ALSA, alsa)
+
+to:
+
+    PKG_CHECK_MODULES(ALSA, alsa, , [PKG_CHECK_MODULES(ALSA, libdssialsacompat)])
+
+then doing 'autoreconf', may be all that is needed to get the
+snd_seq_event_t-using code to compile cleanly.  Of course, if the
+code uses other ALSA features, libdssialsacompat won't help with
+them....
+
+DSSI Installation
+=================
+Installation of DSSI itself (at least as recently as 2005/4/6 CVS)
+must be done by hand, which goes something like this (assuming
+you're running OS X 10.3 and want to install to /usr/local):
+
+$ tar xpzf dssi-0.9.tar.gz
+$ cd dssi-0.9
+$ sudo mkdir -p /usr/local/include
+$ sudo cp dssi/dssi.h /usr/local/include/
+$ sed s:.PREFIX.:/usr/local: dssi.pc >dssi.pc.new
+$ sudo mkdir -p /usr/local/lib/pkgconfig
+$ sudo mv dssi.pc.new /usr/local/lib/pkgconfig/dssi.pc
+
+(You may stop here if you're not interested in the example plugins.)
+
+$ cd examples
+$ PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
+$ export PKG_CONFIG_PATH
+$ make dssi_osc_send
+$ sudo cp -p dssi_osc_send /usr/local/bin
+$ gcc -Wall -fno-common -O2 `pkg-config libdssialsacompat dssi --cflags` \
+    trivial_synth.c -c -o trivial_synth.o
+$ gcc -Wall -fno-common -O2 -c -o message_buffer.o \
+    ../message_buffer/message_buffer.c
+$ gcc -bundle -flat_namespace -undefined suppress -o trivial_synth.so \
+    trivial_synth.o message_buffer.o \
+    `pkg-config libdssialsacompat dssi --libs`
+$ sudo mkdir -p /usr/local/lib/dssi
+$ sudo cp -p trivial_synth.so /usr/local/lib/dssi
+
+Building the rest of the DSSI distribution's plugins is left as an
+exercise to the reader.  Note that (as of 2005/4/6) jack-dssi-host
+will not build without patching since it uses the ALSA sequencer.
+
+What Works (Or Doesn't)
+=======================
+libdssialsacompat 1.0.8a was tested on Mac OS X 10.3.8, using the
+Apple Developer Tools, Gentoo-installed versions of pkgconfig and
+liblo 0.18, hand-installed GTK+ 1.2 and LADSPA SDK, and JackOSX
+0.6.1.  Under this configuration, the following are known to work:
+
+- ghostess (from the 20050411 release, which includes a clumsy but
+    working CoreMIDI driver.)  ghostess can be found at:
+
+    http://home.jps.net/~musound/
+
+- trivial_synth.so (DSSI 0.9 release)
+
+- fluidsynth-dssi.so and FluidSynth-DSSI_gtk (DSSI 0.9 release,
+    using a statically compiled libfluidsynth 1.0.3)
+
+- Xsynth-DSSI (CVS as of 2005/4/11)
+
+- hexter (CVS as of 2005/4/11, note that sys-ex patch editing isn't
+    supported on non-ALSA systems)
+
+The following problems are known to exist:
+
+- less_trivial_synth.so (DSSI 0.9) plays at the wrong pitch on
+    big-endian systems due the little-endian assumption of the
+    typedef union fixp in less_trivial_synth.c (line 69).  Otherwise
+    works fine.
+
+- I have not tested any of the DSSI 0.9 Qt GUIs, or
+    trivial_sampler.so.
+
+- jack-dssi-host (DSSI 0.9) works as an OSC-driven host if you
+    comment out all the ALSA seqeuncer code.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/asoundef.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,166 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for Mac OS X the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ */
+
+/**
+ * \file <alsa/asoundef.h>
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 1998-2001
+ *
+ * Definitions of constants for the ALSA driver
+ */
+/*
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_ASOUNDEF_H
+#define __ALSA_ASOUNDEF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \defgroup MIDI_Interface Constants for MIDI v1.0
+ * Constants for MIDI v1.0.
+ * \{
+ */
+
+#define MIDI_CHANNELS			16	/**< Number of channels per port/cable. */
+#define MIDI_GM_DRUM_CHANNEL		(10-1)	/**< Channel number for GM drums. */
+
+/**
+ * \defgroup MIDI_Commands MIDI Commands
+ * MIDI command codes.
+ * \{
+ */
+
+#define MIDI_CMD_NOTE_OFF		0x80	/**< note off */
+#define MIDI_CMD_NOTE_ON		0x90	/**< note on */
+#define MIDI_CMD_NOTE_PRESSURE		0xa0	/**< key pressure */
+#define MIDI_CMD_CONTROL		0xb0	/**< control change */
+#define MIDI_CMD_PGM_CHANGE		0xc0	/**< program change */
+#define MIDI_CMD_CHANNEL_PRESSURE	0xd0	/**< channel pressure */
+#define MIDI_CMD_BENDER			0xe0	/**< pitch bender */
+
+#define MIDI_CMD_COMMON_SYSEX		0xf0	/**< sysex (system exclusive) begin */
+#define MIDI_CMD_COMMON_MTC_QUARTER	0xf1	/**< MTC quarter frame */
+#define MIDI_CMD_COMMON_SONG_POS	0xf2	/**< song position */
+#define MIDI_CMD_COMMON_SONG_SELECT	0xf3	/**< song select */
+#define MIDI_CMD_COMMON_TUNE_REQUEST	0xf6	/**< tune request */
+#define MIDI_CMD_COMMON_SYSEX_END	0xf7	/**< end of sysex */
+#define MIDI_CMD_COMMON_CLOCK		0xf8	/**< clock */
+#define MIDI_CMD_COMMON_START		0xfa	/**< start */
+#define MIDI_CMD_COMMON_CONTINUE	0xfb	/**< continue */
+#define MIDI_CMD_COMMON_STOP		0xfc	/**< stop */
+#define MIDI_CMD_COMMON_SENSING		0xfe	/**< active sensing */
+#define MIDI_CMD_COMMON_RESET		0xff	/**< reset */
+
+/** \} */
+
+/**
+ * \defgroup MIDI_Controllers MIDI Controllers
+ * MIDI controller numbers.
+ * \{
+ */
+
+#define MIDI_CTL_MSB_BANK		0x00	/**< Bank selection */
+#define MIDI_CTL_MSB_MODWHEEL         	0x01	/**< Modulation */
+#define MIDI_CTL_MSB_BREATH           	0x02	/**< Breath */
+#define MIDI_CTL_MSB_FOOT             	0x04	/**< Foot */
+#define MIDI_CTL_MSB_PORTAMENTO_TIME 	0x05	/**< Portamento time */
+#define MIDI_CTL_MSB_DATA_ENTRY		0x06	/**< Data entry */
+#define MIDI_CTL_MSB_MAIN_VOLUME      	0x07	/**< Main volume */
+#define MIDI_CTL_MSB_BALANCE          	0x08	/**< Balance */
+#define MIDI_CTL_MSB_PAN              	0x0a	/**< Panpot */
+#define MIDI_CTL_MSB_EXPRESSION       	0x0b	/**< Expression */
+#define MIDI_CTL_MSB_EFFECT1		0x0c	/**< Effect1 */
+#define MIDI_CTL_MSB_EFFECT2		0x0d	/**< Effect2 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE1 	0x10	/**< General purpose 1 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE2 	0x11	/**< General purpose 2 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE3 	0x12	/**< General purpose 3 */
+#define MIDI_CTL_MSB_GENERAL_PURPOSE4 	0x13	/**< General purpose 4 */
+#define MIDI_CTL_LSB_BANK		0x20	/**< Bank selection */
+#define MIDI_CTL_LSB_MODWHEEL        	0x21	/**< Modulation */
+#define MIDI_CTL_LSB_BREATH           	0x22	/**< Breath */
+#define MIDI_CTL_LSB_FOOT             	0x24	/**< Foot */
+#define MIDI_CTL_LSB_PORTAMENTO_TIME 	0x25	/**< Portamento time */
+#define MIDI_CTL_LSB_DATA_ENTRY		0x26	/**< Data entry */
+#define MIDI_CTL_LSB_MAIN_VOLUME      	0x27	/**< Main volume */
+#define MIDI_CTL_LSB_BALANCE          	0x28	/**< Balance */
+#define MIDI_CTL_LSB_PAN              	0x2a	/**< Panpot */
+#define MIDI_CTL_LSB_EXPRESSION       	0x2b	/**< Expression */
+#define MIDI_CTL_LSB_EFFECT1		0x2c	/**< Effect1 */
+#define MIDI_CTL_LSB_EFFECT2		0x2d	/**< Effect2 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE1 	0x30	/**< General purpose 1 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE2 	0x31	/**< General purpose 2 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE3 	0x32	/**< General purpose 3 */
+#define MIDI_CTL_LSB_GENERAL_PURPOSE4 	0x33	/**< General purpose 4 */
+#define MIDI_CTL_SUSTAIN              	0x40	/**< Sustain pedal */
+#define MIDI_CTL_PORTAMENTO           	0x41	/**< Portamento */
+#define MIDI_CTL_SUSTENUTO            	0x42	/**< Sostenuto */
+#define MIDI_CTL_SOFT_PEDAL           	0x43	/**< Soft pedal */
+#define MIDI_CTL_LEGATO_FOOTSWITCH	0x44	/**< Legato foot switch */
+#define MIDI_CTL_HOLD2                	0x45	/**< Hold2 */
+#define MIDI_CTL_SC1_SOUND_VARIATION	0x46	/**< SC1 Sound Variation */
+#define MIDI_CTL_SC2_TIMBRE		0x47	/**< SC2 Timbre */
+#define MIDI_CTL_SC3_RELEASE_TIME	0x48	/**< SC3 Release Time */
+#define MIDI_CTL_SC4_ATTACK_TIME	0x49	/**< SC4 Attack Time */
+#define MIDI_CTL_SC5_BRIGHTNESS		0x4a	/**< SC5 Brightness */
+#define MIDI_CTL_SC6			0x4b	/**< SC6 */
+#define MIDI_CTL_SC7			0x4c	/**< SC7 */
+#define MIDI_CTL_SC8			0x4d	/**< SC8 */
+#define MIDI_CTL_SC9			0x4e	/**< SC9 */
+#define MIDI_CTL_SC10			0x4f	/**< SC10 */
+#define MIDI_CTL_GENERAL_PURPOSE5     	0x50	/**< General purpose 5 */
+#define MIDI_CTL_GENERAL_PURPOSE6     	0x51	/**< General purpose 6 */
+#define MIDI_CTL_GENERAL_PURPOSE7     	0x52	/**< General purpose 7 */
+#define MIDI_CTL_GENERAL_PURPOSE8     	0x53	/**< General purpose 8 */
+#define MIDI_CTL_PORTAMENTO_CONTROL	0x54	/**< Portamento control */
+#define MIDI_CTL_E1_REVERB_DEPTH	0x5b	/**< E1 Reverb Depth */
+#define MIDI_CTL_E2_TREMOLO_DEPTH	0x5c	/**< E2 Tremolo Depth */
+#define MIDI_CTL_E3_CHORUS_DEPTH	0x5d	/**< E3 Chorus Depth */
+#define MIDI_CTL_E4_DETUNE_DEPTH	0x5e	/**< E4 Detune Depth */
+#define MIDI_CTL_E5_PHASER_DEPTH	0x5f	/**< E5 Phaser Depth */
+#define MIDI_CTL_DATA_INCREMENT       	0x60	/**< Data Increment */
+#define MIDI_CTL_DATA_DECREMENT       	0x61	/**< Data Decrement */
+#define MIDI_CTL_NONREG_PARM_NUM_LSB  	0x62	/**< Non-registered parameter number */
+#define MIDI_CTL_NONREG_PARM_NUM_MSB  	0x63	/**< Non-registered parameter number */
+#define MIDI_CTL_REGIST_PARM_NUM_LSB  	0x64	/**< Registered parameter number */
+#define MIDI_CTL_REGIST_PARM_NUM_MSB	0x65	/**< Registered parameter number */
+#define MIDI_CTL_ALL_SOUNDS_OFF		0x78	/**< All sounds off */
+#define MIDI_CTL_RESET_CONTROLLERS	0x79	/**< Reset Controllers */
+#define MIDI_CTL_LOCAL_CONTROL_SWITCH	0x7a	/**< Local control switch */
+#define MIDI_CTL_ALL_NOTES_OFF		0x7b	/**< All notes off */
+#define MIDI_CTL_OMNI_OFF		0x7c	/**< Omni off */
+#define MIDI_CTL_OMNI_ON		0x7d	/**< Omni on */
+#define MIDI_CTL_MONO1			0x7e	/**< Mono1 */
+#define MIDI_CTL_MONO2			0x7f	/**< Mono2 */
+
+/** \} */
+
+/** \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_ASOUNDEF_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/asoundlib.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,12 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for Mac OS X the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ */
+
+#include <stddef.h>
+
+#include <alsa/asoundef.h>
+#include <alsa/seq_event.h>
+#include <alsa/seq_midi_event.h>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/seq.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,189 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for Mac OS X the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ */
+
+/**
+ * \file <alsa/seq.h>
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 1998-2001
+ */
+/*
+ * Application interface library for the ALSA driver
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_SEQ_H
+#define __ALSA_SEQ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *  \defgroup SeqEvType Sequencer Event Type Checks
+ *  Sequencer Event Type Checks
+ *  \ingroup Sequencer
+ *  \{
+ */
+
+/* event type macros */
+enum {
+	SND_SEQ_EVFLG_RESULT,
+	SND_SEQ_EVFLG_NOTE,
+	SND_SEQ_EVFLG_CONTROL,
+	SND_SEQ_EVFLG_QUEUE,
+	SND_SEQ_EVFLG_SYSTEM,
+	SND_SEQ_EVFLG_MESSAGE,
+	SND_SEQ_EVFLG_CONNECTION,
+	SND_SEQ_EVFLG_SAMPLE,
+	SND_SEQ_EVFLG_USERS,
+	SND_SEQ_EVFLG_INSTR,
+	SND_SEQ_EVFLG_QUOTE,
+	SND_SEQ_EVFLG_NONE,
+	SND_SEQ_EVFLG_RAW,
+	SND_SEQ_EVFLG_FIXED,
+	SND_SEQ_EVFLG_VARIABLE,
+	SND_SEQ_EVFLG_VARUSR
+};
+
+enum {
+	SND_SEQ_EVFLG_NOTE_ONEARG,
+	SND_SEQ_EVFLG_NOTE_TWOARG
+};
+
+enum {
+	SND_SEQ_EVFLG_QUEUE_NOARG,
+	SND_SEQ_EVFLG_QUEUE_TICK,
+	SND_SEQ_EVFLG_QUEUE_TIME,
+	SND_SEQ_EVFLG_QUEUE_VALUE
+};
+
+/**
+ * Exported event type table
+ *
+ * This table is referred by snd_seq_ev_is_xxx.
+ */
+extern const unsigned int snd_seq_event_types[];
+
+#define _SND_SEQ_TYPE(x)	(1<<(x))	/**< master type - 24bit */
+#define _SND_SEQ_TYPE_OPT(x)	((x)<<24)	/**< optional type - 8bit */
+
+/** check the event type */
+#define snd_seq_type_check(ev,x) (snd_seq_event_types[(ev)->type] & _SND_SEQ_TYPE(x))
+
+/** event type check: result events */
+#define snd_seq_ev_is_result_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_RESULT)
+/** event type check: note events */
+#define snd_seq_ev_is_note_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_NOTE)
+/** event type check: control events */
+#define snd_seq_ev_is_control_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_CONTROL)
+/** event type check: channel specific events */
+#define snd_seq_ev_is_channel_type(ev) \
+	(snd_seq_event_types[(ev)->type] & (_SND_SEQ_TYPE(SND_SEQ_EVFLG_NOTE) | _SND_SEQ_TYPE(SND_SEQ_EVFLG_CONTROL)))
+
+/** event type check: queue control events */
+#define snd_seq_ev_is_queue_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_QUEUE)
+/** event type check: system status messages */
+#define snd_seq_ev_is_message_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_MESSAGE)
+/** event type check: system status messages */
+#define snd_seq_ev_is_subscribe_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_CONNECTION)
+/** event type check: sample messages */
+#define snd_seq_ev_is_sample_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_SAMPLE)
+/** event type check: user-defined messages */
+#define snd_seq_ev_is_user_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_USERS)
+/** event type check: instrument layer events */
+#define snd_seq_ev_is_instr_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_INSTR)
+/** event type check: fixed length events */
+#define snd_seq_ev_is_fixed_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_FIXED)
+/** event type check: variable length events */
+#define snd_seq_ev_is_variable_type(ev)	\
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_VARIABLE)
+/** event type check: user pointer events */
+#define snd_seq_ev_is_varusr_type(ev) \
+	snd_seq_type_check(ev, SND_SEQ_EVFLG_VARUSR)
+/** event type check: reserved for kernel */
+#define snd_seq_ev_is_reserved(ev) \
+	(! snd_seq_event_types[(ev)->type])
+
+/**
+ * macros to check event flags
+ */
+/** prior events */
+#define snd_seq_ev_is_prior(ev)	\
+	(((ev)->flags & SND_SEQ_PRIORITY_MASK) == SND_SEQ_PRIORITY_HIGH)
+
+/** get the data length type */
+#define snd_seq_ev_length_type(ev) \
+	((ev)->flags & SND_SEQ_EVENT_LENGTH_MASK)
+/** fixed length events */
+#define snd_seq_ev_is_fixed(ev)	\
+	(snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_FIXED)
+/** variable length events */
+#define snd_seq_ev_is_variable(ev) \
+	(snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARIABLE)
+/** variable length on user-space */
+#define snd_seq_ev_is_varusr(ev) \
+	(snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARUSR)
+
+/** time-stamp type */
+#define snd_seq_ev_timestamp_type(ev) \
+	((ev)->flags & SND_SEQ_TIME_STAMP_MASK)
+/** event is in tick time */
+#define snd_seq_ev_is_tick(ev) \
+	(snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_TICK)
+/** event is in real-time */
+#define snd_seq_ev_is_real(ev) \
+	(snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_REAL)
+
+/** time-mode type */
+#define snd_seq_ev_timemode_type(ev) \
+	((ev)->flags & SND_SEQ_TIME_MODE_MASK)
+/** scheduled in absolute time */
+#define snd_seq_ev_is_abstime(ev) \
+	(snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_ABS)
+/** scheduled in relative time */
+#define snd_seq_ev_is_reltime(ev) \
+	(snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_REL)
+
+/** direct dispatched events */
+#define snd_seq_ev_is_direct(ev) \
+	((ev)->queue == SND_SEQ_QUEUE_DIRECT)
+
+/** \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_SEQ_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/seq_event.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,458 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for Mac OS X the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ */
+
+/**
+ * \file <alsa/seq_event.h>
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 1998-2001
+ *
+ * Application interface library for the ALSA driver
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_SEQ_EVENT_H
+#define __ALSA_SEQ_EVENT_H
+
+/**
+ *  \defgroup SeqEvents Sequencer Event Definitions
+ *  Sequencer Event Definitions
+ *  \ingroup Sequencer
+ *  \{
+ */
+
+/**
+ * Sequencer event data type
+ */
+typedef unsigned char snd_seq_event_type_t;
+
+/** Sequencer event type */
+enum snd_seq_event_type {
+	/** system status; event data type = #snd_seq_result_t */
+	SND_SEQ_EVENT_SYSTEM = 0,
+	/** returned result status; event data type = #snd_seq_result_t */
+	SND_SEQ_EVENT_RESULT,
+
+	/** note on and off with duration; event data type = #snd_seq_ev_note_t */
+	SND_SEQ_EVENT_NOTE = 5,
+	/** note on; event data type = #snd_seq_ev_note_t */
+	SND_SEQ_EVENT_NOTEON,
+	/** note off; event data type = #snd_seq_ev_note_t */
+	SND_SEQ_EVENT_NOTEOFF,
+	/** key pressure change (aftertouch); event data type = #snd_seq_ev_note_t */
+	SND_SEQ_EVENT_KEYPRESS,
+	
+	/** controller; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_CONTROLLER = 10,
+	/** program change; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_PGMCHANGE,
+	/** channel pressure; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_CHANPRESS,
+	/** pitchwheel; event data type = #snd_seq_ev_ctrl_t; data is from -8192 to 8191) */
+	SND_SEQ_EVENT_PITCHBEND,
+	/** 14 bit controller value; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_CONTROL14,
+	/** 14 bit NRPN;  event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_NONREGPARAM,
+	/** 14 bit RPN; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_REGPARAM,
+
+	/** SPP with LSB and MSB values; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_SONGPOS = 20,
+	/** Song Select with song ID number; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_SONGSEL,
+	/** midi time code quarter frame; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_QFRAME,
+	/** SMF Time Signature event; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_TIMESIGN,
+	/** SMF Key Signature event; event data type = #snd_seq_ev_ctrl_t */
+	SND_SEQ_EVENT_KEYSIGN,
+	        
+	/** MIDI Real Time Start message; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_START = 30,
+	/** MIDI Real Time Continue message; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_CONTINUE,
+	/** MIDI Real Time Stop message; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_STOP,
+	/** Set tick queue position; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_SETPOS_TICK,
+	/** Set real-time queue position; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_SETPOS_TIME,
+	/** (SMF) Tempo event; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_TEMPO,
+	/** MIDI Real Time Clock message; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_CLOCK,
+	/** MIDI Real Time Tick message; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_TICK,
+	/** Queue timer skew; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_QUEUE_SKEW,
+	/** Sync position changed; event data type = #snd_seq_ev_queue_control_t */
+	SND_SEQ_EVENT_SYNC_POS,
+
+	/** Tune request; event data type = none */
+	SND_SEQ_EVENT_TUNE_REQUEST = 40,
+	/** Reset to power-on state; event data type = none */
+	SND_SEQ_EVENT_RESET,
+	/** Active sensing event; event data type = none */
+	SND_SEQ_EVENT_SENSING,
+
+	/** Echo-back event; event data type = any type */
+	SND_SEQ_EVENT_ECHO = 50,
+	/** OSS emulation raw event; event data type = any type */
+	SND_SEQ_EVENT_OSS,
+
+	/** New client has connected; event data type = #snd_seq_addr_t */
+	SND_SEQ_EVENT_CLIENT_START = 60,
+	/** Client has left the system; event data type = #snd_seq_addr_t */
+	SND_SEQ_EVENT_CLIENT_EXIT,
+	/** Client status/info has changed; event data type = #snd_seq_addr_t */
+	SND_SEQ_EVENT_CLIENT_CHANGE,
+	/** New port was created; event data type = #snd_seq_addr_t */
+	SND_SEQ_EVENT_PORT_START,
+	/** Port was deleted from system; event data type = #snd_seq_addr_t */
+	SND_SEQ_EVENT_PORT_EXIT,
+	/** Port status/info has changed; event data type = #snd_seq_addr_t */
+	SND_SEQ_EVENT_PORT_CHANGE,
+
+	/** Ports connected; event data type = #snd_seq_connect_t */
+	SND_SEQ_EVENT_PORT_SUBSCRIBED,
+	/** Ports disconnected; event data type = #snd_seq_connect_t */
+	SND_SEQ_EVENT_PORT_UNSUBSCRIBED,
+
+	/** Sample select; event data type = #snd_seq_ev_sample_control_t */
+	SND_SEQ_EVENT_SAMPLE = 70,
+	/** Sample cluster select; event data type = #snd_seq_ev_sample_control_t */
+	SND_SEQ_EVENT_SAMPLE_CLUSTER,
+	/** voice start */
+	SND_SEQ_EVENT_SAMPLE_START,
+	/** voice stop */
+	SND_SEQ_EVENT_SAMPLE_STOP,
+	/** playback frequency */
+	SND_SEQ_EVENT_SAMPLE_FREQ,
+	/** volume and balance */
+	SND_SEQ_EVENT_SAMPLE_VOLUME,
+	/** sample loop */
+	SND_SEQ_EVENT_SAMPLE_LOOP,
+	/** sample position */
+	SND_SEQ_EVENT_SAMPLE_POSITION,
+	/** private (hardware dependent) event */
+	SND_SEQ_EVENT_SAMPLE_PRIVATE1,
+
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR0 = 90,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR1,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR2,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR3,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR4,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR5,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR6,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR7,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR8,
+	/** user-defined event; event data type = any (fixed size) */
+	SND_SEQ_EVENT_USR9,
+
+	/** begin of instrument management */
+	SND_SEQ_EVENT_INSTR_BEGIN = 100,
+	/** end of instrument management */
+	SND_SEQ_EVENT_INSTR_END,
+	/** query instrument interface info */
+	SND_SEQ_EVENT_INSTR_INFO,
+	/** result of instrument interface info */
+	SND_SEQ_EVENT_INSTR_INFO_RESULT,
+	/** query instrument format info */
+	SND_SEQ_EVENT_INSTR_FINFO,
+	/** result of instrument format info */
+	SND_SEQ_EVENT_INSTR_FINFO_RESULT,
+	/** reset instrument instrument memory */
+	SND_SEQ_EVENT_INSTR_RESET,
+	/** get instrument interface status */
+	SND_SEQ_EVENT_INSTR_STATUS,
+	/** result of instrument interface status */
+	SND_SEQ_EVENT_INSTR_STATUS_RESULT,
+	/** put an instrument to port */
+	SND_SEQ_EVENT_INSTR_PUT,
+	/** get an instrument from port */
+	SND_SEQ_EVENT_INSTR_GET,
+	/** result of instrument query */
+	SND_SEQ_EVENT_INSTR_GET_RESULT,
+	/** free instrument(s) */
+	SND_SEQ_EVENT_INSTR_FREE,
+	/** get instrument list */
+	SND_SEQ_EVENT_INSTR_LIST,
+	/** result of instrument list */
+	SND_SEQ_EVENT_INSTR_LIST_RESULT,
+	/** set cluster parameters */
+	SND_SEQ_EVENT_INSTR_CLUSTER,
+	/** get cluster parameters */
+	SND_SEQ_EVENT_INSTR_CLUSTER_GET,
+	/** result of cluster parameters */
+	SND_SEQ_EVENT_INSTR_CLUSTER_RESULT,
+	/** instrument change */
+	SND_SEQ_EVENT_INSTR_CHANGE,
+
+	/** system exclusive data (variable length);  event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_SYSEX = 130,
+	/** error event;  event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_BOUNCE,
+	/** reserved for user apps;  event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_USR_VAR0 = 135,
+	/** reserved for user apps; event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_USR_VAR1,
+	/** reserved for user apps; event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_USR_VAR2,
+	/** reserved for user apps; event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_USR_VAR3,
+	/** reserved for user apps; event data type = #snd_seq_ev_ext_t */
+	SND_SEQ_EVENT_USR_VAR4,
+
+	/** NOP; ignored in any case */
+	SND_SEQ_EVENT_NONE = 255
+};
+
+
+/** Sequencer event address */
+typedef struct snd_seq_addr {
+	unsigned char client;	/**< Client id */
+	unsigned char port;	/**< Port id */
+} snd_seq_addr_t;
+
+/** Connection (subscription) between ports */
+typedef struct snd_seq_connect {
+	snd_seq_addr_t sender;	/**< sender address */
+	snd_seq_addr_t dest;	/**< destination address */
+} snd_seq_connect_t;
+
+
+/** Real-time data record */
+typedef struct snd_seq_real_time {
+	unsigned int tv_sec;		/**< seconds */
+	unsigned int tv_nsec;		/**< nanoseconds */
+} snd_seq_real_time_t;
+
+/** (MIDI) Tick-time data record */
+typedef unsigned int snd_seq_tick_time_t;
+
+/** unioned time stamp */
+typedef union snd_seq_timestamp {
+	snd_seq_tick_time_t tick;	/**< tick-time */
+	struct snd_seq_real_time time;	/**< real-time */
+} snd_seq_timestamp_t;
+
+
+/**
+ * Event mode flags
+ *
+ * NOTE: only 8 bits available!
+ */
+#define SND_SEQ_TIME_STAMP_TICK		(0<<0)	/**< timestamp in clock ticks */
+#define SND_SEQ_TIME_STAMP_REAL		(1<<0)	/**< timestamp in real time */
+#define SND_SEQ_TIME_STAMP_MASK		(1<<0)	/**< mask for timestamp bits */
+
+#define SND_SEQ_TIME_MODE_ABS		(0<<1)	/**< absolute timestamp */
+#define SND_SEQ_TIME_MODE_REL		(1<<1)	/**< relative to current time */
+#define SND_SEQ_TIME_MODE_MASK		(1<<1)	/**< mask for time mode bits */
+
+#define SND_SEQ_EVENT_LENGTH_FIXED	(0<<2)	/**< fixed event size */
+#define SND_SEQ_EVENT_LENGTH_VARIABLE	(1<<2)	/**< variable event size */
+#define SND_SEQ_EVENT_LENGTH_VARUSR	(2<<2)	/**< variable event size - user memory space */
+#define SND_SEQ_EVENT_LENGTH_MASK	(3<<2)	/**< mask for event length bits */
+
+#define SND_SEQ_PRIORITY_NORMAL		(0<<4)	/**< normal priority */
+#define SND_SEQ_PRIORITY_HIGH		(1<<4)	/**< event should be processed before others */
+#define SND_SEQ_PRIORITY_MASK		(1<<4)	/**< mask for priority bits */
+
+
+/** Note event */
+typedef struct snd_seq_ev_note {
+	unsigned char channel;		/**< channel number */
+	unsigned char note;		/**< note */
+	unsigned char velocity;		/**< velocity */
+	unsigned char off_velocity;	/**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */
+	unsigned int duration;		/**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */
+} snd_seq_ev_note_t;
+
+/** Controller event */
+typedef struct snd_seq_ev_ctrl {
+	unsigned char channel;		/**< channel number */
+	unsigned char unused[3];	/**< reserved */
+	unsigned int param;		/**< control parameter */
+	signed int value;		/**< control value */
+} snd_seq_ev_ctrl_t;
+
+/** generic set of bytes (12x8 bit) */
+typedef struct snd_seq_ev_raw8 {
+	unsigned char d[12];		/**< 8 bit value */
+} snd_seq_ev_raw8_t;
+
+/** generic set of integers (3x32 bit) */
+typedef struct snd_seq_ev_raw32 {
+	unsigned int d[3];		/**< 32 bit value */
+} snd_seq_ev_raw32_t;
+
+/** external stored data */
+typedef struct snd_seq_ev_ext {
+	unsigned int len;		/**< length of data */
+	void *ptr;			/**< pointer to data (note: can be 64-bit) */
+} __attribute__((packed)) snd_seq_ev_ext_t;
+
+/** Instrument cluster type */
+typedef unsigned int snd_seq_instr_cluster_t;
+
+/** Instrument type */
+typedef struct snd_seq_instr {
+	snd_seq_instr_cluster_t cluster;	/**< cluster id */
+	unsigned int std;	/**< instrument standard id; the upper byte means a private instrument (owner - client id) */
+	unsigned short bank;	/**< instrument bank id */
+	unsigned short prg;	/**< instrument program id */
+} snd_seq_instr_t;
+
+/** sample number */
+typedef struct snd_seq_ev_sample {
+	unsigned int std;	/**< sample standard id */
+	unsigned short bank;	/**< sample bank id */
+	unsigned short prg;	/**< sample program id */
+} snd_seq_ev_sample_t;
+
+/** sample cluster */
+typedef struct snd_seq_ev_cluster {
+	snd_seq_instr_cluster_t cluster;	/**< cluster id */
+} snd_seq_ev_cluster_t;
+
+/** sample position */
+typedef unsigned int snd_seq_position_t; /**< playback position (in samples) * 16 */
+
+/** sample stop mode */
+typedef enum snd_seq_stop_mode {
+	SND_SEQ_SAMPLE_STOP_IMMEDIATELY = 0,	/**< terminate playing immediately */
+	SND_SEQ_SAMPLE_STOP_VENVELOPE = 1,	/**< finish volume envelope */
+	SND_SEQ_SAMPLE_STOP_LOOP = 2		/**< terminate loop and finish wave */
+} snd_seq_stop_mode_t;
+
+/** sample frequency */
+typedef int snd_seq_frequency_t; /**< playback frequency in HZ * 16 */
+
+/** sample volume control; if any value is set to -1 == do not change */
+typedef struct snd_seq_ev_volume {
+	signed short volume;	/**< range: 0-16383 */
+	signed short lr;	/**< left-right balance; range: 0-16383 */
+	signed short fr;	/**< front-rear balance; range: 0-16383 */
+	signed short du;	/**< down-up balance; range: 0-16383 */
+} snd_seq_ev_volume_t;
+
+/** simple loop redefinition */
+typedef struct snd_seq_ev_loop {
+	unsigned int start;	/**< loop start (in samples) * 16 */
+	unsigned int end;	/**< loop end (in samples) * 16 */
+} snd_seq_ev_loop_t;
+
+/** Sample control events */
+typedef struct snd_seq_ev_sample_control {
+	unsigned char channel;		/**< channel */
+	unsigned char unused[3];	/**< reserved */
+	union {
+		snd_seq_ev_sample_t sample;	/**< sample number */
+		snd_seq_ev_cluster_t cluster;	/**< cluster number */
+		snd_seq_position_t position;	/**< position */
+		snd_seq_stop_mode_t stop_mode;	/**< stop mode */
+		snd_seq_frequency_t frequency;	/**< frequency */
+		snd_seq_ev_volume_t volume;	/**< volume */
+		snd_seq_ev_loop_t loop;		/**< loop control */
+		unsigned char raw8[8];		/**< raw 8-bit */
+	} param;		/**< control parameters */
+} snd_seq_ev_sample_control_t;
+
+
+
+/** INSTR_BEGIN event */
+typedef struct snd_seq_ev_instr_begin {
+	int timeout;		/**< zero = forever, otherwise timeout in ms */
+} snd_seq_ev_instr_begin_t;
+
+/** Result events */
+typedef struct snd_seq_result {
+	int event;		/**< processed event type */
+	int result;		/**< status */
+} snd_seq_result_t;
+
+/** Queue skew values */
+typedef struct snd_seq_queue_skew {
+	unsigned int value;	/**< skew value */
+	unsigned int base;	/**< skew base */
+} snd_seq_queue_skew_t;
+
+/** queue timer control */
+typedef struct snd_seq_ev_queue_control {
+	unsigned char queue;			/**< affected queue */
+	unsigned char unused[3];		/**< reserved */
+	union {
+		signed int value;		/**< affected value (e.g. tempo) */
+		snd_seq_timestamp_t time;	/**< time */
+		unsigned int position;		/**< sync position */
+		snd_seq_queue_skew_t skew;	/**< queue skew */
+		unsigned int d32[2];		/**< any data */
+		unsigned char d8[8];		/**< any data */
+	} param;				/**< data value union */
+} snd_seq_ev_queue_control_t;
+
+
+/** Sequencer event */
+typedef struct snd_seq_event {
+	snd_seq_event_type_t type;	/**< event type */
+	unsigned char flags;		/**< event flags */
+	unsigned char tag;		/**< tag */
+	
+	unsigned char queue;		/**< schedule queue */
+	snd_seq_timestamp_t time;	/**< schedule time */
+
+	snd_seq_addr_t source;		/**< source address */
+	snd_seq_addr_t dest;		/**< destination address */
+
+	union {
+		snd_seq_ev_note_t note;		/**< note information */
+		snd_seq_ev_ctrl_t control;	/**< MIDI control information */
+		snd_seq_ev_raw8_t raw8;		/**< raw8 data */
+		snd_seq_ev_raw32_t raw32;	/**< raw32 data */
+		snd_seq_ev_ext_t ext;		/**< external data */
+		snd_seq_ev_queue_control_t queue; /**< queue control */
+		snd_seq_timestamp_t time;	/**< timestamp */
+		snd_seq_addr_t addr;		/**< address */
+		snd_seq_connect_t connect;	/**< connect information */
+		snd_seq_result_t result;	/**< operation result code */
+		snd_seq_ev_instr_begin_t instr_begin; /**< instrument */
+		snd_seq_ev_sample_control_t sample; /**< sample control */
+	} data;				/**< event data... */
+} snd_seq_event_t;
+
+
+/** \} */
+
+#endif /* __ALSA_SEQ_EVENT_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/seq_midi_event.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,71 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for Mac OS X the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ */
+
+/**
+ * \file <alsa/seq_midi_event.h>
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 1998-2001
+ *
+ * Application interface library for the ALSA driver
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#ifndef __ALSA_SEQ_MIDI_EVENT_H
+#define __ALSA_SEQ_MIDI_EVENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ *  \defgroup MIDI_Event Sequencer event <-> MIDI byte stream coder
+ *  \ingroup Sequencer
+ *  Sequencer event <-> MIDI byte stream coder
+ *  \{
+ */
+
+/** container for sequencer midi event parsers */
+typedef struct snd_midi_event snd_midi_event_t;
+
+int snd_midi_event_new(size_t bufsize, snd_midi_event_t **rdev);
+/* int snd_midi_event_resize_buffer(snd_midi_event_t *dev, size_t bufsize); */
+void snd_midi_event_free(snd_midi_event_t *dev);
+/* void snd_midi_event_init(snd_midi_event_t *dev); */
+void snd_midi_event_reset_encode(snd_midi_event_t *dev);
+/* void snd_midi_event_reset_decode(snd_midi_event_t *dev); */
+/* void snd_midi_event_no_status(snd_midi_event_t *dev, int on); */
+/* encode from byte stream - return number of written bytes if success */
+long snd_midi_event_encode(snd_midi_event_t *dev, const unsigned char *buf, long count, snd_seq_event_t *ev);
+int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev);
+/* decode from event to bytes - return number of written bytes if success */
+/* long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, const snd_seq_event_t *ev); */
+
+/** \} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ALSA_SEQ_MIDI_EVENT_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/alsa/sound/asequencer.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,38 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for Mac OS X the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ */
+
+/*
+ *  Main header file for the ALSA sequencer
+ *  Copyright (c) 1998-1999 by Frank van de Pol <fvdpol@coil.demon.nl>
+ *            (c) 1998-1999 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+#ifndef __SOUND_ASEQUENCER_H
+#define __SOUND_ASEQUENCER_H
+
+#define SNDRV_SEQ_EVENT_SYSEX		130	/* system exclusive data (variable length) */
+
+#define SNDRV_SEQ_EVENT_LENGTH_FIXED	(0<<2)	/* fixed event size */
+#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE	(1<<2)	/* variable event size */
+
+#define SNDRV_SEQ_EVENT_LENGTH_MASK	(3<<2)
+
+#endif /* __SOUND_ASEQUENCER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/dssi.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,662 @@
+/* -*- c-basic-offset: 4 -*- */
+
+/* dssi.h
+
+   DSSI version 0.10
+   Copyright (c) 2004,2005 Chris Cannam, Steve Harris and Sean Bolton
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+   
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA.
+*/
+
+#ifndef DSSI_INCLUDED
+#define DSSI_INCLUDED
+
+#include "ladspa.h"
+#include "alsa/seq_event.h"
+
+#define DSSI_VERSION "0.10"
+#define DSSI_VERSION_MAJOR 0
+#define DSSI_VERSION_MINOR 10
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 
+   There is a need for an API that supports hosted MIDI soft synths
+   with GUIs in Linux audio applications.  In time the GMPI initiative
+   should comprehensively address this need, but the requirement for
+   Linux applications to be able to support simple hosted synths is
+   here now, and GMPI is not.  This proposal (the "DSSI Soft Synth
+   Interface" or DSSI, pronounced "dizzy") aims to provide a simple
+   solution in a way that we hope will prove complete and compelling
+   enough to support now, yet not so compelling as to supplant GMPI or
+   any other comprehensive future proposal.
+
+   For simplicity and familiarity, this API is based as far as
+   possible on existing work -- the LADSPA plugin API for control
+   values and audio processing, and the ALSA sequencer event types for
+   MIDI event communication.  The GUI part of the proposal is quite
+   new, but may also be applicable retroactively to LADSPA plugins
+   that do not otherwise support this synth interface.
+*/
+
+/*
+   A program wishing to use the DSSI v2 API should set the following
+   symbol to 2 before including this header.
+*/
+#if (!defined DSSI_API_LEVEL)
+#define DSSI_API_LEVEL 1
+#endif
+
+typedef struct _DSSI_Program_Descriptor {
+
+    /** Bank number for this program.  Note that DSSI does not support
+        MIDI-style separation of bank LSB and MSB values.  There is no
+        restriction on the set of available banks: the numbers do not
+        need to be contiguous, there does not need to be a bank 0, etc. */
+    unsigned long Bank;
+
+    /** Program number (unique within its bank) for this program.
+	There is no restriction on the set of available programs: the
+	numbers do not need to be contiguous, there does not need to
+	be a program 0, etc. */
+    unsigned long Program;
+
+    /** Name of the program. */
+    const char * Name;
+
+} DSSI_Program_Descriptor;
+
+
+#define DSSI_TRANSPORT_VALID_STATE  0x01
+#define DSSI_TRANSPORT_VALID_BPM    0x02
+#define DSSI_TRANSPORT_VALID_BBT    0x10
+#define DSSI_TRANSPORT_VALID_TIME   0x20
+
+#define DSSI_TRANSPORT_STATE_STOPPED       0
+#define DSSI_TRANSPORT_STATE_RUNNING       1
+#define DSSI_TRANSPORT_STATE_FREEWHEELING  2
+#define DSSI_TRANSPORT_STATE_OTHER         3  /* waiting for sync, ? */
+
+typedef struct _DSSI_Transport_Info {
+
+    /** The value of this field indicates which of the following
+     *  transport information fields contain valid values. It is
+     *  the logical OR of the DSSI_TRANSPORT_VALID_* bits defined
+     *  above, and may be zero. */
+    int  Valid;
+
+
+    /** This field is valid when (Valid & DSSI_TRANSPORT_VALID_STATE)
+     *  is true:
+     *
+     *  ---- The current transport state, one of the DSSI_TRANSPORT_STATE_*
+     *       values defined above. */
+    int  State;
+
+
+    /** This field is valid when (Valid & DSSI_TRANSPORT_VALID_BPM)
+     *  is true:
+     *
+     *  ---- The current tempo, in beats per minute.  */
+    double Beats_Per_Minute;
+
+
+    /** These six fields are valid when (Valid & DSSI_TRANSPORT_VALID_BBT)
+     *  is true:
+     *
+     *  ---- The bar number at the beginning of the current process cycle. */
+    unsigned long Bar;
+
+    /** ---- The beat within that Bar. */
+    unsigned long Beat;
+    
+    /** ---- The tick within that Beat. */
+    unsigned long Tick;
+
+    /** ---- The (possibly fractional) tick count since transport 'start'
+     *       and the beginning of the current Bar. */
+    double Bar_Start_Tick;
+
+    /** ---- The number of beats per bar. */
+    float  Beats_Per_Bar;
+
+    /** ---- The number of ticks for each beat. */
+    double Ticks_Per_Beat;
+
+    /* [Sean says: I left out the 'beat_type' (time signature "denominator")
+     * field of the jack_position_t structure, because I think it's useless
+     * except to a notation program. Does anybody else feel like we need it?]
+     */
+
+    /** These two fields are valid when (Valid & DSSI_TRANSPORT_VALID_TIME)
+     *  is true:
+     *
+     *  ---- The transport time at the beginning of the current process
+     *       cycle, in seconds. */
+    double  Current_Time;
+
+    /** ---- The transport time at the beginning of the next process
+             cycle, unless repositioning occurs. */
+    double  Next_Time;
+
+} DSSI_Transport_Info;
+
+typedef struct _DSSI_Host_Descriptor DSSI_Host_Descriptor; /* below */
+
+typedef struct _DSSI_Descriptor {
+
+    /**
+     * DSSI_API_Version
+     *
+     * This member indicates the DSSI API level used by this plugin.
+     * All plugins must set this to 1 or 2.  The version 1 API contains
+     * all DSSI_Descriptor fields through run_multiple_synths_adding(),
+     * while the version 2 API adds the receive_host_descriptor().
+     */
+    int DSSI_API_Version;
+
+    /**
+     * LADSPA_Plugin
+     *
+     * A DSSI synth plugin consists of a LADSPA plugin plus an
+     * additional framework for controlling program settings and
+     * transmitting MIDI events.  A plugin must fully implement the
+     * LADSPA descriptor fields as well as the required LADSPA
+     * functions including instantiate() and (de)activate().  It
+     * should also implement run(), with the same behaviour as if
+     * run_synth() (below) were called with no synth events.
+     *
+     * In order to instantiate a synth the host calls the LADSPA
+     * instantiate function, passing in this LADSPA_Descriptor
+     * pointer.  The returned LADSPA_Handle is used as the argument
+     * for the DSSI functions below as well as for the LADSPA ones.
+     */
+    const LADSPA_Descriptor *LADSPA_Plugin;
+
+    /**
+     * configure()
+     *
+     * This member is a function pointer that sends a piece of
+     * configuration data to the plugin.  The key argument specifies
+     * some aspect of the synth's configuration that is to be changed,
+     * and the value argument specifies a new value for it.  A plugin
+     * that does not require this facility at all may set this member
+     * to NULL.
+     *
+     * This call is intended to set some session-scoped aspect of a
+     * plugin's behaviour, for example to tell the plugin to load
+     * sample data from a particular file.  The plugin should act
+     * immediately on the request.  The call should return NULL on
+     * success, or an error string that may be shown to the user.  The
+     * host will free the returned value after use if it is non-NULL.
+     *
+     * Calls to configure() are not automated as timed events.
+     * Instead, a host should remember the last value associated with
+     * each key passed to configure() during a given session for a
+     * given plugin instance, and should call configure() with the
+     * correct value for each key the next time it instantiates the
+     * "same" plugin instance, for example on reloading a project in
+     * which the plugin was used before.  Plugins should note that a
+     * host may typically instantiate a plugin multiple times with the
+     * same configuration values, and should share data between
+     * instances where practical.
+     *
+     * Calling configure() completely invalidates the program and bank
+     * information last obtained from the plugin.
+     *
+     * Reserved and special key prefixes
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * The DSSI: prefix
+     * ----------------
+     * Configure keys starting with DSSI: are reserved for particular
+     * purposes documented in the DSSI specification.  At the moment,
+     * there is one such key: DSSI:PROJECT_DIRECTORY.  A host may call
+     * configure() passing this key and a directory path value.  This
+     * indicates to the plugin and its UI that a directory at that
+     * path exists and may be used for project-local data.  Plugins
+     * may wish to use the project directory as a fallback location
+     * when looking for other file data, or as a base for relative
+     * paths in other configuration values.
+     *
+     * The GLOBAL: prefix
+     * ------------------
+     * Configure keys starting with GLOBAL: may be used by the plugin
+     * and its UI for any purpose, but are treated specially by the
+     * host.  When one of these keys is used in a configure OSC call
+     * from the plugin UI, the host makes the corresponding configure
+     * call (preserving the GLOBAL: prefix) not only to the target
+     * plugin but also to all other plugins in the same instance
+     * group, as well as their UIs.  Note that if any instance
+     * returns non-NULL from configure to indicate error, the host
+     * may stop there (and the set of plugins on which configure has
+     * been called will thus depend on the host implementation).
+     * See also the configure OSC call documentation in RFC.txt.
+     */
+    char *(*configure)(LADSPA_Handle Instance,
+		       const char *Key,
+		       const char *Value);
+
+    #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:"
+    #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:"
+    #define DSSI_PROJECT_DIRECTORY_KEY \
+	DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY"
+
+    /**
+     * get_program()
+     *
+     * This member is a function pointer that provides a description
+     * of a program (named preset sound) available on this synth.  A
+     * plugin that does not support programs at all should set this
+     * member to NULL.
+     *
+     * The Index argument is an index into the plugin's list of
+     * programs, not a program number as represented by the Program
+     * field of the DSSI_Program_Descriptor.  (This distinction is
+     * needed to support synths that use non-contiguous program or
+     * bank numbers.)
+     *
+     * This function returns a DSSI_Program_Descriptor pointer that is
+     * guaranteed to be valid only until the next call to get_program,
+     * deactivate, or configure, on the same plugin instance.  This
+     * function must return NULL if passed an Index argument out of
+     * range, so that the host can use it to query the number of
+     * programs as well as their properties.
+     */
+    const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance,
+						  unsigned long Index);
+    
+    /**
+     * select_program()
+     *
+     * This member is a function pointer that selects a new program
+     * for this synth.  The program change should take effect
+     * immediately at the start of the next run_synth() call.  (This
+     * means that a host providing the capability of changing programs
+     * between any two notes on a track must vary the block size so as
+     * to place the program change at the right place.  A host that
+     * wanted to avoid this would probably just instantiate a plugin
+     * for each program.)
+     * 
+     * A plugin that does not support programs at all should set this
+     * member NULL.  Plugins should ignore a select_program() call
+     * with an invalid bank or program.
+     *
+     * A plugin is not required to select any particular default
+     * program on activate(): it's the host's duty to set a program
+     * explicitly.  The current program is invalidated by any call to
+     * configure().
+     *
+     * A plugin is permitted to re-write the values of its input
+     * control ports when select_program is called.  The host should
+     * re-read the input control port values and update its own
+     * records appropriately.  (This is the only circumstance in
+     * which a DSSI plugin is allowed to modify its own input ports.)
+     */
+    void (*select_program)(LADSPA_Handle Instance,
+			   unsigned long Bank,
+			   unsigned long Program);
+
+    /**
+     * get_midi_controller_for_port()
+     *
+     * This member is a function pointer that returns the MIDI
+     * controller number or NRPN that should be mapped to the given
+     * input control port.  If the given port should not have any MIDI
+     * controller mapped to it, the function should return DSSI_NONE.
+     * The behaviour of this function is undefined if the given port
+     * number does not correspond to an input control port.  A plugin
+     * that does not want MIDI controllers mapped to ports at all may
+     * set this member NULL.
+     *
+     * Correct values can be got using the macros DSSI_CC(num) and
+     * DSSI_NRPN(num) as appropriate, and values can be combined using
+     * bitwise OR: e.g. DSSI_CC(23) | DSSI_NRPN(1069) means the port
+     * should respond to CC #23 and NRPN #1069.
+     *
+     * The host is responsible for doing proper scaling from MIDI
+     * controller and NRPN value ranges to port ranges according to
+     * the plugin's LADSPA port hints.  Hosts should not deliver
+     * through run_synth any MIDI controller events that have already
+     * been mapped to control port values.
+     *
+     * A plugin should not attempt to request mappings from
+     * controllers 0 or 32 (MIDI Bank Select MSB and LSB).
+     */
+    int (*get_midi_controller_for_port)(LADSPA_Handle Instance,
+					unsigned long Port);
+
+    /**
+     * run_synth()
+     *
+     * This member is a function pointer that runs a synth for a
+     * block.  This is identical in function to the LADSPA run()
+     * function, except that it also supplies events to the synth.
+     *
+     * A plugin may provide this function, run_multiple_synths() (see
+     * below), both, or neither (if it is not in fact a synth).  A
+     * plugin that does not provide this function must set this member
+     * to NULL.  Authors of synth plugins are encouraged to provide
+     * this function if at all possible.
+     *
+     * The Events pointer points to a block of EventCount ALSA
+     * sequencer events, which is used to communicate MIDI and related
+     * events to the synth.  Each event is timestamped relative to the
+     * start of the block, (mis)using the ALSA "tick time" field as a
+     * frame count. The host is responsible for ensuring that events
+     * with differing timestamps are already ordered by time.
+     *
+     * See also the notes on activation, port connection etc in
+     * ladpsa.h, in the context of the LADSPA run() function.
+     *
+     * Note Events
+     * ~~~~~~~~~~~
+     * There are two minor requirements aimed at making the plugin
+     * writer's life as simple as possible:
+     * 
+     * 1. A host must never send events of type SND_SEQ_EVENT_NOTE.
+     * Notes should always be sent as separate SND_SEQ_EVENT_NOTE_ON
+     * and NOTE_OFF events.  A plugin should discard any one-point
+     * NOTE events it sees.
+     * 
+     * 2. A host must not attempt to switch notes off by sending
+     * zero-velocity NOTE_ON events.  It should always send true
+     * NOTE_OFFs.  It is the host's responsibility to remap events in
+     * cases where an external MIDI source has sent it zero-velocity
+     * NOTE_ONs.
+     *
+     * Bank and Program Events
+     * ~~~~~~~~~~~~~~~~~~~~~~~
+     * Hosts must map MIDI Bank Select MSB and LSB (0 and 32)
+     * controllers and MIDI Program Change events onto the banks and
+     * programs specified by the plugin, using the DSSI select_program
+     * call.  No host should ever deliver a program change or bank
+     * select controller to a plugin via run_synth.
+     */
+    void (*run_synth)(LADSPA_Handle    Instance,
+		      unsigned long    SampleCount,
+		      snd_seq_event_t *Events,
+		      unsigned long    EventCount);
+
+    /**
+     * run_synth_adding()
+     *
+     * This member is a function pointer that runs an instance of a
+     * synth for a block, adding its outputs to the values already
+     * present at the output ports.  This is provided for symmetry
+     * with LADSPA run_adding(), and is equally optional.  A plugin
+     * that does not provide it must set this member to NULL.
+     */
+    void (*run_synth_adding)(LADSPA_Handle    Instance,
+			     unsigned long    SampleCount,
+			     snd_seq_event_t *Events,
+			     unsigned long    EventCount);
+
+    /**
+     * run_multiple_synths()
+     *
+     * This member is a function pointer that runs multiple synth
+     * instances for a block.  This is very similar to run_synth(),
+     * except that Instances, Events, and EventCounts each point to
+     * arrays that hold the LADSPA handles, event buffers, and
+     * event counts for each of InstanceCount instances.  That is,
+     * Instances points to an array of InstanceCount pointers to
+     * DSSI plugin instantiations, Events points to an array of
+     * pointers to each instantiation's respective event list, and
+     * EventCounts points to an array containing each instantiation's
+     * respective event count.
+     *
+     * A host using this function must guarantee that ALL active
+     * instances of the plugin are represented in each call to the
+     * function -- that is, a host may not call run_multiple_synths()
+     * for some instances of a given plugin and then call run_synth()
+     * as well for others.  'All .. instances of the plugin' means
+     * every instance sharing the same LADSPA label and shared object
+     * (*.so) file (rather than every instance sharing the same *.so).
+     * 'Active' means any instance for which activate() has been called
+     * but deactivate() has not.
+     *
+     * A plugin may provide this function, run_synths() (see above),
+     * both, or neither (if it not in fact a synth).  A plugin that
+     * does not provide this function must set this member to NULL.
+     * Plugin authors implementing run_multiple_synths are strongly
+     * encouraged to implement run_synth as well if at all possible,
+     * to aid simplistic hosts, even where it would be less efficient
+     * to use it.
+     */
+    void (*run_multiple_synths)(unsigned long     InstanceCount,
+                                LADSPA_Handle    *Instances,
+                                unsigned long     SampleCount,
+                                snd_seq_event_t **Events,
+                                unsigned long    *EventCounts);
+
+    /**
+     * run_multiple_synths_adding()
+     *
+     * This member is a function pointer that runs multiple synth
+     * instances for a block, adding each synth's outputs to the
+     * values already present at the output ports.  This is provided
+     * for symmetry with both the DSSI run_multiple_synths() and LADSPA
+     * run_adding() functions, and is equally optional.  A plugin
+     * that does not provide it must set this member to NULL.
+     */
+    void (*run_multiple_synths_adding)(unsigned long     InstanceCount,
+                                       LADSPA_Handle    *Instances,
+                                       unsigned long     SampleCount,
+                                       snd_seq_event_t **Events,
+                                       unsigned long    *EventCounts);
+
+#if (DSSI_API_LEVEL > 1)
+
+    /**
+     * receive_host_descriptor()
+     *
+     * This member is a function pointer by which a host may provide
+     * a plugin with a pointer to its DSSI_Host_Descriptor. Hosts
+     * which provide host descriptor support must call this function
+     * once per plugin shared object file, before any calls to
+     * instantiate().
+     *
+     * NOTE: This field was added in version 2 of the DSSI API. Hosts
+     * supporting version 2 must not access this field in a plugin
+     * whose DSSI_API_Version is 1, and plugins supporting version 2
+     * should behave reasonably under hosts (of any version) which do
+     * not implement this function. A version 2 plugin that does not
+     * provide this function must set this member to NULL.
+     */
+    void (*receive_host_descriptor)(const DSSI_Host_Descriptor *Descriptor);
+
+#endif
+
+} DSSI_Descriptor;
+
+struct _DSSI_Host_Descriptor {
+
+    /**
+     * DSSI_API_Version
+     *
+     * This member indicates the DSSI API level used by this host.
+     * All hosts must set this to 2.  Hopefully, we'll get this right
+     * the first time, and this will never be needed.
+     */
+    int DSSI_API_Version;
+
+    /**
+     * request_transport_information()
+     *
+     * This member is a function pointer by which a plugin instance may
+     * request that a host begin providing transport information (if
+     * Request is non-zero), or notify the host that it no longer needs
+     * transport information (if Request is zero).  Upon receiving a
+     * non-zero request, the host should return a pointer to a
+     * DSSI_Transport_Info structure if it is able to provide transport
+     * information, or NULL otherwise.
+     *
+     * Once a plugin instance has received a non-null transport
+     * information pointer, it may read from the structure at any time
+     * within the execution of an audio class function (see doc/RFC.txt).
+     * It should not consider the structure contents to be meaningful
+     * while within a instantiation or control class function.  Also,
+     * since the validity of fields within the structure may change
+     * between each new invocation of an audio class function, a plugin
+     * instance must check the Valid field of the structure accordingly
+     * before using the structure's other contents.
+     *
+     * A host which does not support this function must set this member
+     * to NULL.
+     */
+    DSSI_Transport_Info *
+        (*request_transport_information)(LADSPA_Handle Instance,
+                                         int           Request);
+
+    /**
+     * request_midi_send()
+     *
+     * This member is a function pointer that allows a plugin to
+     * request the ability to send MIDI events to the host.
+     *
+     * While the interpretation of plugin-generated MIDI events is
+     * host implementation specific, a mechanism exists by which a
+     * plugin may declare to the host the number of destination
+     * 'ports' and MIDI channels it can expect will be used in the
+     * plugin-generated events.  Plugins which generate unchannelized
+     * MIDI should supply zero for both Ports and Channels, otherwise
+     * they should supply the maximum numbers for Ports and Channels
+     * they expect to use.
+     *
+     * A plugin instance must call this function during instantiate().
+     * [Sean says: this restriction seems reasonable to me, since
+     * the host may need to create output ports, etc., and instantiate()
+     * seems like a good place to do such things.  I'm sure I haven't
+     * fully thought through all the details, though....]
+     *
+     * The host should return a non-zero value if it is able to
+     * provide MIDI send for the plugin instance, otherwise it should
+     * return zero, and the plugin instance may not subsequently call
+     * midi_send().
+     *
+     * A host which does not support the MIDI send function must set
+     * both this member and (*midi_send)() below to NULL.
+     */
+    int (*request_midi_send)(LADSPA_Handle Instance,
+                             unsigned char Ports,
+                             unsigned char Channels);
+
+    /**
+     * midi_send()
+     *
+     * This member is a function pointer by which a plugin actually
+     * sends MIDI events to the host (provided it has received a non-
+     * zero return from request_midi_send()). As in the run_synth()
+     * functions, the Event pointer points to a block of EventCount
+     * ALSA sequencer events.  The dest.port and data.*.channel fields
+     * of each event are used to specify destination port and channel,
+     * respectively, when the plugin is supplying channelized events.
+     *
+     * A plugin may only call this function from within the execution
+     * of the audio class run_*() or select_program() functions. When
+     * called from a run_*() functions, the events are timestamped
+     * relative to the start of the block, (mis)using the ALSA "tick
+     * time" field as a frame count. The plugin is responsible for
+     * ensuring that events with differing timestamps are already
+     * ordered by time, and that timestamps across multiple calls to
+     * midi_send() from within the same run_*() invocation are
+     * monotonic.  When midi_send() is called from within
+     * select_program(), the timestamps are ignored, and the events
+     * are considered to originate at the same frame time as the
+     * select_program() call, if such a timing can be considered
+     * meaningful.
+     *
+     * The memory pointed to by Event belongs to the plugin, and it is
+     * the host's responsibility to copy the events as needed before
+     * returning from the midi_send() call.
+     *
+     * A host which does not support the MIDI send function must set
+     * both this member and (*request_midi_send)() above to NULL.
+     */
+    void (*midi_send)(LADSPA_Handle    Instance,
+                      snd_seq_event_t *Event,
+                      unsigned long    EventCount);
+
+   /**
+    * . . . additional fields could follow here, possibly supporting:
+    *
+    *   - a facility by which a plugin instance may request from a
+    *       host a non-realtime thread in which to do off-line
+    *       rendering, I/O, etc., thus (hopefully) avoiding the
+    *       crashes that seem to occur when plugins create their own
+    *       threads.  I got this idea after noticing that ZynAddSubFX
+    *       achieves its gorgeous textures while remaining very
+    *       responsive by doing a lot of non-real-time rendering.
+    *       Several other uses for it have been mentioned on the DSSI
+    *       list; I forget what.
+    *
+    *   - per-voice audio output
+    */
+
+    int (*request_non_rt_thread)(LADSPA_Handle Instance,
+				 void (*RunFunction)(LADSPA_Handle Instance));
+};
+
+/**
+ * DSSI supports a plugin discovery method similar to that of LADSPA:
+ *
+ * - DSSI hosts may wish to locate DSSI plugin shared object files by
+ *    searching the paths contained in the DSSI_PATH and LADSPA_PATH
+ *    environment variables, if they are present.  Both are expected
+ *    to be colon-separated lists of directories to be searched (in
+ *    order), and DSSI_PATH should be searched first if both variables
+ *    are set.
+ *
+ * - Each shared object file containing DSSI plugins must include a
+ *   function dssi_descriptor(), with the following function prototype
+ *   and C-style linkage.  Hosts may enumerate the plugin types
+ *   available in the shared object file by repeatedly calling
+ *   this function with successive Index values (beginning from 0),
+ *   until a return value of NULL indicates no more plugin types are
+ *   available.  Each non-NULL return is the DSSI_Descriptor
+ *   of a distinct plugin type.
+ */
+
+const DSSI_Descriptor *dssi_descriptor(unsigned long Index);
+  
+typedef const DSSI_Descriptor *(*DSSI_Descriptor_Function)(unsigned long Index);
+
+/*
+ * Macros to specify particular MIDI controllers in return values from
+ * get_midi_controller_for_port()
+ */
+
+#define DSSI_CC_BITS			0x20000000
+#define DSSI_NRPN_BITS			0x40000000
+
+#define DSSI_NONE			-1
+#define DSSI_CONTROLLER_IS_SET(n)	(DSSI_NONE != (n))
+
+#define DSSI_CC(n)			(DSSI_CC_BITS | (n))
+#define DSSI_IS_CC(n)			(DSSI_CC_BITS & (n))
+#define DSSI_CC_NUMBER(n)		((n) & 0x7f)
+
+#define DSSI_NRPN(n)			(DSSI_NRPN_BITS | ((n) << 7))
+#define DSSI_IS_NRPN(n)			(DSSI_NRPN_BITS & (n))
+#define DSSI_NRPN_NUMBER(n)		(((n) >> 7) & 0x3fff)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DSSI_INCLUDED */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/dssi_alsa_compat.c	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,649 @@
+/* DSSI ALSA compatibility library
+ *
+ * This library provides for non-ALSA systems the ALSA snd_seq_event_t handling
+ * necessary to compile and run DSSI.  It was extracted from alsa-lib 1.0.8.
+ *
+ * See ./alsa/README for more information.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "alsa/asoundef.h"
+#include "alsa/sound/asequencer.h"
+#include "alsa/seq.h"
+#include "alsa/seq_event.h"
+#include "alsa/seq_midi_event.h"
+
+/**
+ * \file seq/seq_event.c
+ * \brief Sequencer Event Types
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 2001
+ */
+
+#define FIXED_EV(x)	(_SND_SEQ_TYPE(SND_SEQ_EVFLG_FIXED) | _SND_SEQ_TYPE(x))
+
+/** Event types conversion array */
+const unsigned int snd_seq_event_types[256] = {
+	[SND_SEQ_EVENT_SYSTEM ... SND_SEQ_EVENT_RESULT]
+	= FIXED_EV(SND_SEQ_EVFLG_RESULT),
+	[SND_SEQ_EVENT_NOTE]
+	= FIXED_EV(SND_SEQ_EVFLG_NOTE) | _SND_SEQ_TYPE_OPT(SND_SEQ_EVFLG_NOTE_TWOARG),
+	[SND_SEQ_EVENT_NOTEON ... SND_SEQ_EVENT_KEYPRESS]
+	= FIXED_EV(SND_SEQ_EVFLG_NOTE),
+	[SND_SEQ_EVENT_CONTROLLER ... SND_SEQ_EVENT_REGPARAM]
+	= FIXED_EV(SND_SEQ_EVFLG_CONTROL),
+	[SND_SEQ_EVENT_START ... SND_SEQ_EVENT_STOP]
+	= FIXED_EV(SND_SEQ_EVFLG_QUEUE),
+	[SND_SEQ_EVENT_SETPOS_TICK]
+	= FIXED_EV(SND_SEQ_EVFLG_QUEUE) | _SND_SEQ_TYPE_OPT(SND_SEQ_EVFLG_QUEUE_TICK),
+	[SND_SEQ_EVENT_SETPOS_TIME]
+	= FIXED_EV(SND_SEQ_EVFLG_QUEUE) | _SND_SEQ_TYPE_OPT(SND_SEQ_EVFLG_QUEUE_TIME),
+	[SND_SEQ_EVENT_TEMPO ... SND_SEQ_EVENT_SYNC_POS]
+	= FIXED_EV(SND_SEQ_EVFLG_QUEUE) | _SND_SEQ_TYPE_OPT(SND_SEQ_EVFLG_QUEUE_VALUE),
+	[SND_SEQ_EVENT_TUNE_REQUEST ... SND_SEQ_EVENT_SENSING]
+	= FIXED_EV(SND_SEQ_EVFLG_NONE),
+	[SND_SEQ_EVENT_ECHO ... SND_SEQ_EVENT_OSS]
+	= FIXED_EV(SND_SEQ_EVFLG_RAW) | FIXED_EV(SND_SEQ_EVFLG_SYSTEM),
+	[SND_SEQ_EVENT_CLIENT_START ... SND_SEQ_EVENT_PORT_CHANGE]
+	= FIXED_EV(SND_SEQ_EVFLG_MESSAGE),
+	[SND_SEQ_EVENT_PORT_SUBSCRIBED ... SND_SEQ_EVENT_PORT_UNSUBSCRIBED]
+	= FIXED_EV(SND_SEQ_EVFLG_CONNECTION),
+	[SND_SEQ_EVENT_SAMPLE ... SND_SEQ_EVENT_SAMPLE_PRIVATE1]
+	= FIXED_EV(SND_SEQ_EVFLG_SAMPLE),
+	[SND_SEQ_EVENT_USR0 ... SND_SEQ_EVENT_USR9]
+	= FIXED_EV(SND_SEQ_EVFLG_RAW) | FIXED_EV(SND_SEQ_EVFLG_USERS),
+	[SND_SEQ_EVENT_INSTR_BEGIN ... SND_SEQ_EVENT_INSTR_CHANGE]
+	= _SND_SEQ_TYPE(SND_SEQ_EVFLG_INSTR) | _SND_SEQ_TYPE(SND_SEQ_EVFLG_VARUSR),
+	[SND_SEQ_EVENT_SYSEX ... SND_SEQ_EVENT_BOUNCE]
+	= _SND_SEQ_TYPE(SND_SEQ_EVFLG_VARIABLE),
+	[SND_SEQ_EVENT_USR_VAR0 ... SND_SEQ_EVENT_USR_VAR4]
+	= _SND_SEQ_TYPE(SND_SEQ_EVFLG_VARIABLE) | _SND_SEQ_TYPE(SND_SEQ_EVFLG_USERS),
+	[SND_SEQ_EVENT_NONE]
+	= FIXED_EV(SND_SEQ_EVFLG_NONE),
+};
+
+/**
+ * \file seq/seq_midi_event.c
+ * \brief MIDI byte <-> sequencer event coder
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \date 2000-2001
+ */
+
+/*
+ *  MIDI byte <-> sequencer event coder
+ *
+ *  Copyright (C) 1998,99,2000 Takashi Iwai <tiwai@suse.de>,
+ *			       Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as
+ *   published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/* midi status */
+struct snd_midi_event {
+	size_t qlen;	/* queue length */
+	size_t read;	/* chars read */
+	int type;	/* current event type */
+	unsigned char lastcmd;
+	unsigned char nostat;
+	size_t bufsize;
+	unsigned char *buf;	/* input buffer */
+};
+
+
+/* queue type */
+/* from 0 to 7 are normal commands (note off, on, etc.) */
+#define ST_NOTEOFF	0
+#define ST_NOTEON	1
+#define ST_SPECIAL	8
+#define ST_SYSEX	ST_SPECIAL
+/* from 8 to 15 are events for 0xf0-0xf7 */
+
+
+/* status event types */
+typedef void (*event_encode_t)(snd_midi_event_t *dev, snd_seq_event_t *ev);
+typedef void (*event_decode_t)(const snd_seq_event_t *ev, unsigned char *buf);
+
+/*
+ * prototypes
+ */
+static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev);
+static void note_decode(const snd_seq_event_t *ev, unsigned char *buf);
+static void one_param_decode(const snd_seq_event_t *ev, unsigned char *buf);
+static void pitchbend_decode(const snd_seq_event_t *ev, unsigned char *buf);
+static void two_param_decode(const snd_seq_event_t *ev, unsigned char *buf);
+static void songpos_decode(const snd_seq_event_t *ev, unsigned char *buf);
+
+/*
+ * event list
+ */
+static struct status_event_list_t {
+	int event;
+	int qlen;
+	event_encode_t encode;
+	event_decode_t decode;
+} status_event[] = {
+	/* 0x80 - 0xf0 */
+	{SND_SEQ_EVENT_NOTEOFF,		2, note_event, note_decode},
+	{SND_SEQ_EVENT_NOTEON,		2, note_event, note_decode},
+	{SND_SEQ_EVENT_KEYPRESS,	2, note_event, note_decode},
+	{SND_SEQ_EVENT_CONTROLLER,	2, two_param_ctrl_event, two_param_decode},
+	{SND_SEQ_EVENT_PGMCHANGE,	1, one_param_ctrl_event, one_param_decode},
+	{SND_SEQ_EVENT_CHANPRESS,	1, one_param_ctrl_event, one_param_decode},
+	{SND_SEQ_EVENT_PITCHBEND,	2, pitchbend_ctrl_event, pitchbend_decode},
+	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf0 */
+	/* 0xf0 - 0xff */
+	{SND_SEQ_EVENT_SYSEX,		1, NULL, NULL}, /* sysex: 0xf0 */
+	{SND_SEQ_EVENT_QFRAME,		1, one_param_event, one_param_decode}, /* 0xf1 */
+	{SND_SEQ_EVENT_SONGPOS,		2, songpos_event, songpos_decode}, /* 0xf2 */
+	{SND_SEQ_EVENT_SONGSEL,		1, one_param_event, one_param_decode}, /* 0xf3 */
+	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf4 */
+	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf5 */
+	{SND_SEQ_EVENT_TUNE_REQUEST,	0, NULL, NULL},	/* 0xf6 */
+	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf7 */
+	{SND_SEQ_EVENT_CLOCK,		0, NULL, NULL}, /* 0xf8 */
+	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf9 */
+	{SND_SEQ_EVENT_START,		0, NULL, NULL}, /* 0xfa */
+	{SND_SEQ_EVENT_CONTINUE,	0, NULL, NULL}, /* 0xfb */
+	{SND_SEQ_EVENT_STOP, 		0, NULL, NULL}, /* 0xfc */
+	{SND_SEQ_EVENT_NONE, 		0, NULL, NULL}, /* 0xfd */
+	{SND_SEQ_EVENT_SENSING, 	0, NULL, NULL}, /* 0xfe */
+	{SND_SEQ_EVENT_RESET, 		0, NULL, NULL}, /* 0xff */
+};
+
+#ifdef UNNEEDED_BY_DSSI
+static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, const snd_seq_event_t *ev);
+static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, const snd_seq_event_t *ev);
+
+static struct extra_event_list_t {
+	int event;
+	int (*decode)(snd_midi_event_t *dev, unsigned char *buf, int len, const snd_seq_event_t *ev);
+} extra_event[] = {
+	{SND_SEQ_EVENT_CONTROL14, extra_decode_ctrl14},
+	{SND_SEQ_EVENT_NONREGPARAM, extra_decode_xrpn},
+	{SND_SEQ_EVENT_REGPARAM, extra_decode_xrpn},
+};
+
+#define numberof(ary)	(sizeof(ary)/sizeof(ary[0]))
+#endif /* UNNEEDED_BY_DSSI */
+
+/**
+ * \brief Initialize MIDI event parser
+ * \param bufsize buffer size for MIDI message
+ * \param rdev allocated MIDI event parser
+ * \return 0 on success otherwise a negative error code
+ *
+ * Allocates and initializes MIDI event parser.
+ */
+int snd_midi_event_new(size_t bufsize, snd_midi_event_t **rdev)
+{
+	snd_midi_event_t *dev;
+
+	*rdev = NULL;
+	dev = (snd_midi_event_t *)calloc(1, sizeof(snd_midi_event_t));
+	if (dev == NULL)
+		return -ENOMEM;
+	if (bufsize > 0) {
+		dev->buf = malloc(bufsize);
+		if (dev->buf == NULL) {
+			free(dev);
+			return -ENOMEM;
+		}
+	}
+	dev->bufsize = bufsize;
+	dev->lastcmd = 0xff;
+	*rdev = dev;
+	return 0;
+}
+
+/**
+ * \brief Free MIDI event parser
+ * \param rdev MIDI event parser
+ * \return 0 on success otherwise a negative error code
+ *
+ * Frees MIDI event parser.
+ */
+void snd_midi_event_free(snd_midi_event_t *dev)
+{
+	if (dev != NULL) {
+		if (dev->buf)
+			free(dev->buf);
+		free(dev);
+	}
+}
+
+#ifdef UNNEEDED_BY_DSSI
+/**
+ * \brief Enable/disable MIDI command merging
+ * \param dev MIDI event parser
+ * \param on 0 - enable MIDI command merging, 1 - always pass the command
+ *
+ * Enable/disable MIDI command merging
+ */
+void snd_midi_event_no_status(snd_midi_event_t *dev, int on)
+{
+	dev->nostat = on ? 1 : 0;
+}
+#endif /* UNNEEDED_BY_DSSI */
+
+/*
+ * initialize record
+ */
+inline static void reset_encode(snd_midi_event_t *dev)
+{
+	dev->read = 0;
+	dev->qlen = 0;
+	dev->type = 0;
+}
+
+/**
+ * \brief Reset MIDI encode parser
+ * \param dev MIDI event parser
+ * \return 0 on success otherwise a negative error code
+ *
+ * Resets MIDI encode parser
+ */
+void snd_midi_event_reset_encode(snd_midi_event_t *dev)
+{
+	reset_encode(dev);
+}
+
+#ifdef UNNEEDED_BY_DSSI
+/**
+ * \brief Reset MIDI decode parser
+ * \param dev MIDI event parser
+ * \return 0 on success otherwise a negative error code
+ *
+ * Resets MIDI decode parser
+ */
+void snd_midi_event_reset_decode(snd_midi_event_t *dev)
+{
+	dev->lastcmd = 0xff;
+}
+
+/**
+ * \brief Initializes MIDI parsers
+ * \param dev MIDI event parser
+ * \return 0 on success otherwise a negative error code
+ *
+ * Initializes MIDI parsers (both encode and decode)
+ */
+void snd_midi_event_init(snd_midi_event_t *dev)
+{
+	snd_midi_event_reset_encode(dev);
+	snd_midi_event_reset_decode(dev);
+}
+
+/**
+ * \brief Resize MIDI message (event) buffer
+ * \param dev MIDI event parser
+ * \param bufsize new requested buffer size
+ * \return 0 on success otherwise a negative error code
+ *
+ * Resizes MIDI message (event) buffer.
+ */
+int snd_midi_event_resize_buffer(snd_midi_event_t *dev, size_t bufsize)
+{
+	unsigned char *new_buf, *old_buf;
+
+	if (bufsize == dev->bufsize)
+		return 0;
+	new_buf = malloc(bufsize);
+	if (new_buf == NULL)
+		return -ENOMEM;
+	old_buf = dev->buf;
+	dev->buf = new_buf;
+	dev->bufsize = bufsize;
+	reset_encode(dev);
+	if (old_buf)
+		free(old_buf);
+	return 0;
+}
+#endif /* UNNEEDED_BY_DSSI */
+
+/**
+ * \brief Read bytes and encode to sequencer event if finished
+ * \param dev MIDI event parser
+ * \param buf MIDI byte stream
+ * \param count count of bytes of MIDI byte stream to encode
+ * \param ev Result - sequencer event
+ * \return count of encoded bytes otherwise a negative error code
+ *
+ * Read bytes and encode to sequencer event if finished.
+ * If complete sequencer event is available, ev->type is not
+ * equal to #SND_SEQ_EVENT_NONE.
+ */
+long snd_midi_event_encode(snd_midi_event_t *dev, const unsigned char *buf, long count, snd_seq_event_t *ev)
+{
+	long result = 0;
+	int rc;
+
+	ev->type = SND_SEQ_EVENT_NONE;
+
+	while (count-- > 0) {
+		rc = snd_midi_event_encode_byte(dev, *buf++, ev);
+		result++;
+		if (rc < 0)
+			return rc;
+		else if (rc > 0)
+			return result;
+	}
+
+	return result;
+}
+
+/**
+ * \brief Read one byte and encode to sequencer event if finished
+ * \param dev MIDI event parser
+ * \param c a byte of MIDI stream
+ * \param ev Result - sequencer event
+ * \return 1 - sequencer event is completed, 0 - next byte is required for completion, otherwise a negative error code
+ *
+ * Read byte and encode to sequencer event if finished.
+ */
+int snd_midi_event_encode_byte(snd_midi_event_t *dev, int c, snd_seq_event_t *ev)
+{
+	int rc = 0;
+
+	c &= 0xff;
+
+	if (c >= MIDI_CMD_COMMON_CLOCK) {
+		/* real-time event */
+		ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		return 1;
+	}
+
+	if (dev->qlen > 0) {
+		/* rest of command */
+		dev->buf[dev->read++] = c;
+		if (dev->type != ST_SYSEX)
+			dev->qlen--;
+	} else {
+		/* new command */
+		dev->read = 1;
+		if (c & 0x80) {
+			dev->buf[0] = c;
+			if ((c & 0xf0) == 0xf0) /* special events */
+				dev->type = (c & 0x0f) + ST_SPECIAL;
+			else
+				dev->type = (c >> 4) & 0x07;
+			dev->qlen = status_event[dev->type].qlen;
+		} else {
+			/* process this byte as argument */
+			dev->buf[dev->read++] = c;
+			dev->qlen = status_event[dev->type].qlen - 1;
+		}
+	}
+	if (dev->qlen == 0) {
+		ev->type = status_event[dev->type].event;
+		ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+		ev->flags |= SNDRV_SEQ_EVENT_LENGTH_FIXED;
+		if (status_event[dev->type].encode) /* set data values */
+			status_event[dev->type].encode(dev, ev);
+		rc = 1;
+	} else 	if (dev->type == ST_SYSEX) {
+		if (c == MIDI_CMD_COMMON_SYSEX_END ||
+		    dev->read >= dev->bufsize) {
+			ev->flags &= ~SNDRV_SEQ_EVENT_LENGTH_MASK;
+			ev->flags |= SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
+			ev->type = SNDRV_SEQ_EVENT_SYSEX;
+			ev->data.ext.len = dev->read;
+			ev->data.ext.ptr = dev->buf;
+			if (c != MIDI_CMD_COMMON_SYSEX_END)
+				dev->read = 0; /* continue to parse */
+			else
+				reset_encode(dev); /* all parsed */
+			rc = 1;
+		}
+	}
+
+	return rc;
+}
+
+/* encode note event */
+static void note_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.note.channel = dev->buf[0] & 0x0f;
+	ev->data.note.note = dev->buf[1];
+	ev->data.note.velocity = dev->buf[2];
+}
+
+/* encode one parameter controls */
+static void one_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode pitch wheel change */
+static void pitchbend_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1] - 8192;
+}
+
+/* encode midi control change */
+static void two_param_ctrl_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.channel = dev->buf[0] & 0x0f;
+	ev->data.control.param = dev->buf[1];
+	ev->data.control.value = dev->buf[2];
+}
+
+/* encode one parameter value*/
+static void one_param_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.value = dev->buf[1];
+}
+
+/* encode song position */
+static void songpos_event(snd_midi_event_t *dev, snd_seq_event_t *ev)
+{
+	ev->data.control.value = (int)dev->buf[2] * 128 + (int)dev->buf[1];
+}
+
+#ifdef UNNEEDED_BY_DSSI
+/**
+ * \brief Decode sequencer event to MIDI byte stream
+ * \param dev MIDI event parser
+ * \param buf Result - MIDI byte stream
+ * \param count Available bytes in MIDI byte stream
+ * \param ev Event to decode
+ * \return count of decoded bytes otherwise a negative error code
+ *
+ * Decode sequencer event to MIDI byte stream.
+ */
+long snd_midi_event_decode(snd_midi_event_t *dev, unsigned char *buf, long count, const snd_seq_event_t *ev)
+{
+	int cmd;
+	long qlen;
+	unsigned int type;
+
+	if (ev->type == SNDRV_SEQ_EVENT_NONE)
+		return -ENOENT;
+
+	for (type = 0; type < numberof(status_event); type++) {
+		if (ev->type == status_event[type].event)
+			goto __found;
+	}
+	for (type = 0; type < numberof(extra_event); type++) {
+		if (ev->type == extra_event[type].event)
+			return extra_event[type].decode(dev, buf, count, ev);
+	}
+	return -ENOENT;
+
+      __found:
+	if (type >= ST_SPECIAL)
+		cmd = 0xf0 + (type - ST_SPECIAL);
+	else
+		/* data.note.channel and data.control.channel is identical */
+		cmd = 0x80 | (type << 4) | (ev->data.note.channel & 0x0f);
+
+
+	if (cmd == MIDI_CMD_COMMON_SYSEX) {
+		qlen = ev->data.ext.len;
+		if (count < qlen)
+			return -ENOMEM;
+		switch (ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) {
+		case SNDRV_SEQ_EVENT_LENGTH_FIXED:
+			return -EINVAL;	/* invalid event */
+		}
+		memcpy(buf, ev->data.ext.ptr, qlen);
+		return qlen;
+	} else {
+		unsigned char xbuf[4];
+
+		if ((cmd & 0xf0) == 0xf0 || dev->lastcmd != cmd || dev->nostat) {
+			dev->lastcmd = cmd;
+			xbuf[0] = cmd;
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 1);
+			qlen = status_event[type].qlen + 1;
+		} else {
+			if (status_event[type].decode)
+				status_event[type].decode(ev, xbuf + 0);
+			qlen = status_event[type].qlen;
+		}
+		if (count < qlen)
+			return -ENOMEM;
+		memcpy(buf, xbuf, qlen);
+		return qlen;
+	}
+}
+#endif /* UNNEEDED_BY_DSSI */
+
+/* decode note event */
+static void note_decode(const snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.note.note & 0x7f;
+	buf[1] = ev->data.note.velocity & 0x7f;
+}
+
+/* decode one parameter controls */
+static void one_param_decode(const snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+}
+
+/* decode pitch wheel change */
+static void pitchbend_decode(const snd_seq_event_t *ev, unsigned char *buf)
+{
+	int value = ev->data.control.value + 8192;
+	buf[0] = value & 0x7f;
+	buf[1] = (value >> 7) & 0x7f;
+}
+
+/* decode midi control change */
+static void two_param_decode(const snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.param & 0x7f;
+	buf[1] = ev->data.control.value & 0x7f;
+}
+
+/* decode song position */
+static void songpos_decode(const snd_seq_event_t *ev, unsigned char *buf)
+{
+	buf[0] = ev->data.control.value & 0x7f;
+	buf[1] = (ev->data.control.value >> 7) & 0x7f;
+}
+
+#ifdef UNNEEDED_BY_DSSI
+/* decode 14bit control */
+static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int count, const snd_seq_event_t *ev)
+{
+	unsigned char cmd;
+	int idx = 0;
+
+	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+	if (ev->data.control.param < 32) {
+		if (count < 4)
+			return -ENOMEM;
+		if (dev->nostat && count < 6)
+			return -ENOMEM;
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 5)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param;
+		buf[idx++] = (ev->data.control.value >> 7) & 0x7f;
+		if (dev->nostat)
+			buf[idx++] = cmd;
+		buf[idx++] = ev->data.control.param + 32;
+		buf[idx++] = ev->data.control.value & 0x7f;
+	} else {
+		if (count < 2)
+			return -ENOMEM;
+		if (cmd != dev->lastcmd || dev->nostat) {
+			if (count < 3)
+				return -ENOMEM;
+			buf[idx++] = dev->lastcmd = cmd;
+		}
+		buf[idx++] = ev->data.control.param & 0x7f;
+		buf[idx++] = ev->data.control.value & 0x7f;
+	}
+	return idx;
+}
+
+/* decode reg/nonreg param */
+static int extra_decode_xrpn(snd_midi_event_t *dev, unsigned char *buf, int count, const snd_seq_event_t *ev)
+{
+	unsigned char cmd;
+	char *cbytes;
+	static char cbytes_nrpn[4] = { MIDI_CTL_NONREG_PARM_NUM_MSB,
+				       MIDI_CTL_NONREG_PARM_NUM_LSB,
+				       MIDI_CTL_MSB_DATA_ENTRY,
+				       MIDI_CTL_LSB_DATA_ENTRY };
+	static char cbytes_rpn[4] =  { MIDI_CTL_REGIST_PARM_NUM_MSB,
+				       MIDI_CTL_REGIST_PARM_NUM_LSB,
+				       MIDI_CTL_MSB_DATA_ENTRY,
+				       MIDI_CTL_LSB_DATA_ENTRY };
+	unsigned char bytes[4];
+	int idx = 0, i;
+
+	if (count < 8)
+		return -ENOMEM;
+	if (dev->nostat && count < 12)
+		return -ENOMEM;
+	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
+	bytes[0] = ev->data.control.param & 0x007f;
+	bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
+	bytes[2] = ev->data.control.value & 0x007f;
+	bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
+	if (cmd != dev->lastcmd && !dev->nostat) {
+		if (count < 9)
+			return -ENOMEM;
+		buf[idx++] = dev->lastcmd = cmd;
+	}
+	cbytes = ev->type == SND_SEQ_EVENT_NONREGPARAM ? cbytes_nrpn : cbytes_rpn;
+	for (i = 0; i < 4; i++) {
+		if (dev->nostat)
+			buf[idx++] = dev->lastcmd = cmd;
+		buf[idx++] = cbytes[i];
+		buf[idx++] = bytes[i];
+	}
+	return idx;
+}
+#endif /* UNNEEDED_BY_DSSI */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/ladspa.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,603 @@
+/* ladspa.h
+
+   Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
+   Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
+   Stefan Westerfeld.
+   
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+   
+   This library is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   Lesser General Public License for more details.
+   
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+   USA. */
+
+#ifndef LADSPA_INCLUDED
+#define LADSPA_INCLUDED
+
+#define LADSPA_VERSION "1.1"
+#define LADSPA_VERSION_MAJOR 1
+#define LADSPA_VERSION_MINOR 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+/* Overview: 
+
+   There is a large number of synthesis packages in use or development
+   on the Linux platform at this time. This API (`The Linux Audio
+   Developer's Simple Plugin API') attempts to give programmers the
+   ability to write simple `plugin' audio processors in C/C++ and link
+   them dynamically (`plug') into a range of these packages (`hosts').
+   It should be possible for any host and any plugin to communicate
+   completely through this interface.
+
+   This API is deliberately short and simple. To achieve compatibility
+   with a range of promising Linux sound synthesis packages it
+   attempts to find the `greatest common divisor' in their logical
+   behaviour. Having said this, certain limiting decisions are
+   implicit, notably the use of a fixed type (LADSPA_Data) for all
+   data transfer and absence of a parameterised `initialisation'
+   phase. See below for the LADSPA_Data typedef.
+
+   Plugins are expected to distinguish between control and audio
+   data. Plugins have `ports' that are inputs or outputs for audio or
+   control data and each plugin is `run' for a `block' corresponding
+   to a short time interval measured in samples. Audio data is
+   communicated using arrays of LADSPA_Data, allowing a block of audio
+   to be processed by the plugin in a single pass. Control data is
+   communicated using single LADSPA_Data values. Control data has a
+   single value at the start of a call to the `run()' or `run_adding()'
+   function, and may be considered to remain this value for its
+   duration. The plugin may assume that all its input and output ports
+   have been connected to the relevant data location (see the
+   `connect_port()' function below) before it is asked to run.
+
+   Plugins will reside in shared object files suitable for dynamic
+   linking by dlopen() and family. The file will provide a number of
+   `plugin types' that can be used to instantiate actual plugins
+   (sometimes known as `plugin instances') that can be connected
+   together to perform tasks.
+
+   This API contains very limited error-handling. */
+
+/*****************************************************************************/
+
+/* Fundamental data type passed in and out of plugin. This data type
+   is used to communicate audio samples and control values. It is
+   assumed that the plugin will work sensibly given any numeric input
+   value although it may have a preferred range (see hints below). 
+
+   For audio it is generally assumed that 1.0f is the `0dB' reference
+   amplitude and is a `normal' signal level. */
+
+typedef float LADSPA_Data;
+
+/*****************************************************************************/
+
+/* Special Plugin Properties: 
+ 
+   Optional features of the plugin type are encapsulated in the
+   LADSPA_Properties type. This is assembled by ORing individual
+   properties together. */
+
+typedef int LADSPA_Properties;
+
+/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
+   real-time dependency (e.g. listens to a MIDI device) and so its
+   output must not be cached or subject to significant latency. */
+#define LADSPA_PROPERTY_REALTIME        0x1
+
+/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
+   may cease to work correctly if the host elects to use the same data
+   location for both input and output (see connect_port()). This
+   should be avoided as enabling this flag makes it impossible for
+   hosts to use the plugin to process audio `in-place.' */
+#define LADSPA_PROPERTY_INPLACE_BROKEN  0x2
+
+/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
+   is capable of running not only in a conventional host but also in a
+   `hard real-time' environment. To qualify for this the plugin must
+   satisfy all of the following:
+
+   (1) The plugin must not use malloc(), free() or other heap memory
+   management within its run() or run_adding() functions. All new
+   memory used in run() must be managed via the stack. These
+   restrictions only apply to the run() function.
+
+   (2) The plugin will not attempt to make use of any library
+   functions with the exceptions of functions in the ANSI standard C
+   and C maths libraries, which the host is expected to provide.
+
+   (3) The plugin will not access files, devices, pipes, sockets, IPC
+   or any other mechanism that might result in process or thread
+   blocking.
+      
+   (4) The plugin will take an amount of time to execute a run() or
+   run_adding() call approximately of form (A+B*SampleCount) where A
+   and B depend on the machine and host in use. This amount of time
+   may not depend on input signals or plugin state. The host is left
+   the responsibility to perform timings to estimate upper bounds for
+   A and B. */
+#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
+
+#define LADSPA_IS_REALTIME(x)        ((x) & LADSPA_PROPERTY_REALTIME)
+#define LADSPA_IS_INPLACE_BROKEN(x)  ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
+#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
+
+/*****************************************************************************/
+
+/* Plugin Ports: 
+
+   Plugins have `ports' that are inputs or outputs for audio or
+   data. Ports can communicate arrays of LADSPA_Data (for audio
+   inputs/outputs) or single LADSPA_Data values (for control
+   input/outputs). This information is encapsulated in the
+   LADSPA_PortDescriptor type which is assembled by ORing individual
+   properties together.
+
+   Note that a port must be an input or an output port but not both
+   and that a port must be a control or audio port but not both. */
+
+typedef int LADSPA_PortDescriptor;
+
+/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
+#define LADSPA_PORT_INPUT   0x1
+
+/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
+#define LADSPA_PORT_OUTPUT  0x2
+
+/* Property LADSPA_PORT_CONTROL indicates that the port is a control
+   port. */
+#define LADSPA_PORT_CONTROL 0x4
+
+/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
+   port. */
+#define LADSPA_PORT_AUDIO   0x8
+
+#define LADSPA_IS_PORT_INPUT(x)   ((x) & LADSPA_PORT_INPUT)
+#define LADSPA_IS_PORT_OUTPUT(x)  ((x) & LADSPA_PORT_OUTPUT)
+#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
+#define LADSPA_IS_PORT_AUDIO(x)   ((x) & LADSPA_PORT_AUDIO)
+
+/*****************************************************************************/
+
+/* Plugin Port Range Hints: 
+
+   The host may wish to provide a representation of data entering or
+   leaving a plugin (e.g. to generate a GUI automatically). To make
+   this more meaningful, the plugin should provide `hints' to the host
+   describing the usual values taken by the data.
+   
+   Note that these are only hints. The host may ignore them and the
+   plugin must not assume that data supplied to it is meaningful. If
+   the plugin receives invalid input data it is expected to continue
+   to run without failure and, where possible, produce a sensible
+   output (e.g. a high-pass filter given a negative cutoff frequency
+   might switch to an all-pass mode).
+    
+   Hints are meaningful for all input and output ports but hints for
+   input control ports are expected to be particularly useful.
+   
+   More hint information is encapsulated in the
+   LADSPA_PortRangeHintDescriptor type which is assembled by ORing
+   individual hint types together. Hints may require further
+   LowerBound and UpperBound information.
+
+   All the hint information for a particular port is aggregated in the
+   LADSPA_PortRangeHint structure. */
+
+typedef int LADSPA_PortRangeHintDescriptor;
+
+/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) lower
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of LowerBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_BELOW   0x1
+
+/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+   of the LADSPA_PortRangeHint should be considered meaningful. The
+   value in this field should be considered the (inclusive) upper
+   bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+   specified then the value of UpperBound should be multiplied by the
+   sample rate. */
+#define LADSPA_HINT_BOUNDED_ABOVE   0x2
+
+/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
+   considered a Boolean toggle. Data less than or equal to zero should
+   be considered `off' or `false,' and data above zero should be
+   considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
+   conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
+   LADSPA_HINT_DEFAULT_1. */
+#define LADSPA_HINT_TOGGLED         0x4
+
+/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
+   should be interpreted as multiples of the sample rate. For
+   instance, a frequency range from 0Hz to the Nyquist frequency (half
+   the sample rate) could be requested by this hint in conjunction
+   with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+   at all must support this hint to retain meaning. */
+#define LADSPA_HINT_SAMPLE_RATE     0x8
+
+/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
+   user will find it more intuitive to view values using a logarithmic
+   scale. This is particularly useful for frequencies and gains. */
+#define LADSPA_HINT_LOGARITHMIC     0x10
+
+/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
+   probably wish to provide a stepped control taking only integer
+   values. Any bounds set should be slightly wider than the actual
+   integer range required to avoid floating point rounding errors. For
+   instance, the integer set {0,1,2,3} might be described as [-0.1,
+   3.1]. */
+#define LADSPA_HINT_INTEGER         0x20
+
+/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
+   value for the port that is sensible as a default. For instance,
+   this value is suitable for use as an initial value in a user
+   interface or as a value the host might assign to a control port
+   when the user has not provided one. Defaults are encoded using a
+   mask so only one default may be specified for a port. Some of the
+   hints make use of lower and upper bounds, in which case the
+   relevant bound or bounds must be available and
+   LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
+   default must be rounded if LADSPA_HINT_INTEGER is present. Default
+   values were introduced in LADSPA v1.1. */
+#define LADSPA_HINT_DEFAULT_MASK    0x3C0
+
+/* This default values indicates that no default is provided. */
+#define LADSPA_HINT_DEFAULT_NONE    0x0
+
+/* This default hint indicates that the suggested lower bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
+
+/* This default hint indicates that a low value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
+   log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
+   * 0.25). */
+#define LADSPA_HINT_DEFAULT_LOW     0x80
+
+/* This default hint indicates that a middle value between the
+   suggested lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
+   log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
+   0.5). */
+#define LADSPA_HINT_DEFAULT_MIDDLE  0xC0
+
+/* This default hint indicates that a high value between the suggested
+   lower and upper bounds should be chosen. For ports with
+   LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
+   log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
+   * 0.75). */
+#define LADSPA_HINT_DEFAULT_HIGH    0x100
+
+/* This default hint indicates that the suggested upper bound for the
+   port should be used. */
+#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
+
+/* This default hint indicates that the number 0 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_0       0x200
+
+/* This default hint indicates that the number 1 should be used. Note
+   that this default may be used in conjunction with
+   LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_1       0x240
+
+/* This default hint indicates that the number 100 should be used. */
+#define LADSPA_HINT_DEFAULT_100     0x280
+
+/* This default hint indicates that the Hz frequency of `concert A'
+   should be used. This will be 440 unless the host uses an unusual
+   tuning convention, in which case it may be within a few Hz. */
+#define LADSPA_HINT_DEFAULT_440     0x2C0
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x)   ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x)   ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x)         ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x)     ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x)     ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x)         ((x) & LADSPA_HINT_INTEGER)
+
+#define LADSPA_IS_HINT_HAS_DEFAULT(x)     ((x) & LADSPA_HINT_DEFAULT_MASK)
+#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MINIMUM)
+#define LADSPA_IS_HINT_DEFAULT_LOW(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_LOW)
+#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x)  (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MIDDLE)
+#define LADSPA_IS_HINT_DEFAULT_HIGH(x)    (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_HIGH)
+#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_MAXIMUM)
+#define LADSPA_IS_HINT_DEFAULT_0(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_0)
+#define LADSPA_IS_HINT_DEFAULT_1(x)       (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_1)
+#define LADSPA_IS_HINT_DEFAULT_100(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                           == LADSPA_HINT_DEFAULT_100)
+#define LADSPA_IS_HINT_DEFAULT_440(x)     (((x) & LADSPA_HINT_DEFAULT_MASK)   \
+                                            == LADSPA_HINT_DEFAULT_440)
+
+typedef struct _LADSPA_PortRangeHint {
+
+  /* Hints about the port. */
+  LADSPA_PortRangeHintDescriptor HintDescriptor;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data LowerBound;
+
+  /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
+     LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+     multiplied by the relevant sample rate. */
+  LADSPA_Data UpperBound;
+
+} LADSPA_PortRangeHint;
+
+/*****************************************************************************/
+
+/* Plugin Handles: 
+
+   This plugin handle indicates a particular instance of the plugin
+   concerned. It is valid to compare this to NULL (0 for C++) but
+   otherwise the host should not attempt to interpret it. The plugin
+   may use it to reference internal instance data. */
+
+typedef void * LADSPA_Handle;
+
+/*****************************************************************************/
+
+/* Descriptor for a Type of Plugin: 
+
+   This structure is used to describe a plugin type. It provides a
+   number of functions to examine the type, instantiate it, link it to
+   buffers and workspaces and to run it. */
+
+typedef struct _LADSPA_Descriptor { 
+
+  /* This numeric identifier indicates the plugin type
+     uniquely. Plugin programmers may reserve ranges of IDs from a
+     central body to avoid clashes. Hosts may assume that IDs are
+     below 0x1000000. */
+  unsigned long UniqueID;
+
+  /* This identifier can be used as a unique, case-sensitive
+     identifier for the plugin type within the plugin file. Plugin
+     types should be identified by file and label rather than by index
+     or plugin name, which may be changed in new plugin
+     versions. Labels must not contain white-space characters. */
+  const char * Label;
+
+  /* This indicates a number of properties of the plugin. */
+  LADSPA_Properties Properties;
+
+  /* This member points to the null-terminated name of the plugin
+     (e.g. "Sine Oscillator"). */
+  const char * Name;
+
+  /* This member points to the null-terminated string indicating the
+     maker of the plugin. This can be an empty string but not NULL. */
+  const char * Maker;
+
+  /* This member points to the null-terminated string indicating any
+     copyright applying to the plugin. If no Copyright applies the
+     string "None" should be used. */
+  const char * Copyright;
+
+  /* This indicates the number of ports (input AND output) present on
+     the plugin. */
+  unsigned long PortCount;
+
+  /* This member indicates an array of port descriptors. Valid indices
+     vary from 0 to PortCount-1. */
+  const LADSPA_PortDescriptor * PortDescriptors;
+
+  /* This member indicates an array of null-terminated strings
+     describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
+     0 to PortCount-1. */
+  const char * const * PortNames;
+
+  /* This member indicates an array of range hints for each port (see
+     above). Valid indices vary from 0 to PortCount-1. */
+  const LADSPA_PortRangeHint * PortRangeHints;
+
+  /* This may be used by the plugin developer to pass any custom
+     implementation data into an instantiate call. It must not be used
+     or interpreted by the host. It is expected that most plugin
+     writers will not use this facility as LADSPA_Handle should be
+     used to hold instance data. */
+  void * ImplementationData;
+
+  /* This member is a function pointer that instantiates a plugin. A
+     handle is returned indicating the new plugin instance. The
+     instantiation function accepts a sample rate as a parameter. The
+     plugin descriptor from which this instantiate function was found
+     must also be passed. This function must return NULL if
+     instantiation fails. 
+
+     Note that instance initialisation should generally occur in
+     activate() rather than here. */
+  LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
+                               unsigned long                     SampleRate);
+
+  /* This member is a function pointer that connects a port on an
+     instantiated plugin to a memory location at which a block of data
+     for the port will be read/written. The data location is expected
+     to be an array of LADSPA_Data for audio ports or a single
+     LADSPA_Data value for control ports. Memory issues will be
+     managed by the host. The plugin must read/write the data at these
+     locations every time run() or run_adding() is called and the data
+     present at the time of this connection call should not be
+     considered meaningful.
+
+     connect_port() may be called more than once for a plugin instance
+     to allow the host to change the buffers that the plugin is
+     reading or writing. These calls may be made before or after
+     activate() or deactivate() calls.
+
+     connect_port() must be called at least once for each port before
+     run() or run_adding() is called. When working with blocks of
+     LADSPA_Data the plugin should pay careful attention to the block
+     size passed to the run function as the block allocated may only
+     just be large enough to contain the block of samples.
+
+     Plugin writers should be aware that the host may elect to use the
+     same buffer for more than one port and even use the same buffer
+     for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
+     However, overlapped buffers or use of a single buffer for both
+     audio and control data may result in unexpected behaviour. */
+   void (*connect_port)(LADSPA_Handle Instance,
+                        unsigned long Port,
+                        LADSPA_Data * DataLocation);
+
+  /* This member is a function pointer that initialises a plugin
+     instance and activates it for use. This is separated from
+     instantiate() to aid real-time support and so that hosts can
+     reinitialise a plugin instance by calling deactivate() and then
+     activate(). In this case the plugin instance must reset all state
+     information dependent on the history of the plugin instance
+     except for any data locations provided by connect_port() and any
+     gain set by set_run_adding_gain(). If there is nothing for
+     activate() to do then the plugin writer may provide a NULL rather
+     than an empty function.
+
+     When present, hosts must call this function once before run() (or
+     run_adding()) is called for the first time. This call should be
+     made as close to the run() call as possible and indicates to
+     real-time plugins that they are now live. Plugins should not rely
+     on a prompt call to run() after activate(). activate() may not be
+     called again unless deactivate() is called first. Note that
+     connect_port() may be called before or after a call to
+     activate(). */
+  void (*activate)(LADSPA_Handle Instance);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. Two parameters are required: the first is a
+     handle to the particular instance to be run and the second
+     indicates the block size (in samples) for which the plugin
+     instance may run.
+
+     Note that if an activate() function exists then it must be called
+     before run() or run_adding(). If deactivate() is called for a
+     plugin instance then the plugin instance may not be reused until
+     activate() has been called again.
+
+     If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
+     then there are various things that the plugin should not do
+     within the run() or run_adding() functions (see above). */
+  void (*run)(LADSPA_Handle Instance,
+              unsigned long SampleCount);
+
+  /* This method is a function pointer that runs an instance of a
+     plugin for a block. This has identical behaviour to run() except
+     in the way data is output from the plugin. When run() is used,
+     values are written directly to the memory areas associated with
+     the output ports. However when run_adding() is called, values
+     must be added to the values already present in the memory
+     areas. Furthermore, output values written must be scaled by the
+     current gain set by set_run_adding_gain() (see below) before
+     addition.
+
+     run_adding() is optional. When it is not provided by a plugin,
+     this function pointer must be set to NULL. When it is provided,
+     the function set_run_adding_gain() must be provided also. */
+  void (*run_adding)(LADSPA_Handle Instance,
+                     unsigned long SampleCount);
+
+  /* This method is a function pointer that sets the output gain for
+     use when run_adding() is called (see above). If this function is
+     never called the gain is assumed to default to 1. Gain
+     information should be retained when activate() or deactivate()
+     are called.
+
+     This function should be provided by the plugin if and only if the
+     run_adding() function is provided. When it is absent this
+     function pointer must be set to NULL. */
+  void (*set_run_adding_gain)(LADSPA_Handle Instance,
+                              LADSPA_Data   Gain);
+
+  /* This is the counterpart to activate() (see above). If there is
+     nothing for deactivate() to do then the plugin writer may provide
+     a NULL rather than an empty function.
+
+     Hosts must deactivate all activated units after they have been
+     run() (or run_adding()) for the last time. This call should be
+     made as close to the last run() call as possible and indicates to
+     real-time plugins that they are no longer live. Plugins should
+     not rely on prompt deactivation. Note that connect_port() may be
+     called before or after a call to deactivate().
+
+     Deactivation is not similar to pausing as the plugin instance
+     will be reinitialised when activate() is called to reuse it. */
+  void (*deactivate)(LADSPA_Handle Instance);
+
+  /* Once an instance of a plugin has been finished with it can be
+     deleted using the following function. The instance handle passed
+     ceases to be valid after this call.
+  
+     If activate() was called for a plugin instance then a
+     corresponding call to deactivate() must be made before cleanup()
+     is called. */
+  void (*cleanup)(LADSPA_Handle Instance);
+
+} LADSPA_Descriptor;
+
+/**********************************************************************/
+
+/* Accessing a Plugin: */
+
+/* The exact mechanism by which plugins are loaded is host-dependent,
+   however all most hosts will need to know is the name of shared
+   object file containing the plugin types. To allow multiple hosts to
+   share plugin types, hosts may wish to check for environment
+   variable LADSPA_PATH. If present, this should contain a
+   colon-separated path indicating directories that should be searched
+   (in order) when loading plugin types.
+
+   A plugin programmer must include a function called
+   "ladspa_descriptor" with the following function prototype within
+   the shared object file. This function will have C-style linkage (if
+   you are using C++ this is taken care of by the `extern "C"' clause
+   at the top of the file).
+
+   A host will find the plugin shared object file by one means or
+   another, find the ladspa_descriptor() function, call it, and
+   proceed from there.
+
+   Plugin types are accessed by index (not ID) using values from 0
+   upwards. Out of range indexes must result in this function
+   returning NULL, so the plugin count can be determined by checking
+   for the least index that results in NULL being returned. */
+
+const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
+
+/* Datatype corresponding to the ladspa_descriptor() function. */
+typedef const LADSPA_Descriptor * 
+(*LADSPA_Descriptor_Function)(unsigned long Index);
+
+/**********************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LADSPA_INCLUDED */
+
+/* EOF */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/api/new_dssi_transport_patch	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,245 @@
+diff -r dssi-CVS-20051012=0.9.1/dssi/dssi.h _dssi-transport-mine-new/dssi/dssi.h
+5,6c5,6
+<    DSSI version 0.9
+<    Copyright (c) 2004 Chris Cannam, Steve Harris and Sean Bolton
+---
+>    DSSI version 0.10
+>    Copyright (c) 2004,2005 Chris Cannam, Steve Harris and Sean Bolton
+30c30
+< #define DSSI_VERSION "0.9"
+---
+> #define DSSI_VERSION "0.10"
+32c32
+< #define DSSI_VERSION_MINOR 9
+---
+> #define DSSI_VERSION_MINOR 10
+76a77,152
+> #define DSSI_TRANSPORT_VALID_STATE  0x01
+> #define DSSI_TRANSPORT_VALID_BPM    0x02
+> #define DSSI_TRANSPORT_VALID_BBT    0x10
+> #define DSSI_TRANSPORT_VALID_TIME   0x20
+> 
+> #define DSSI_TRANSPORT_STATE_STOPPED       0
+> #define DSSI_TRANSPORT_STATE_RUNNING       1
+> #define DSSI_TRANSPORT_STATE_FREEWHEELING  2
+> #define DSSI_TRANSPORT_STATE_OTHER         3  /* waiting for sync, ? */
+> 
+> typedef struct _DSSI_Transport_Info {
+> 
+>     /** The value of this field indicates which of the following
+>      *  transport information fields contain valid values. It is
+>      *  the logical OR of the DSSI_TRANSPORT_VALID_* bits defined
+>      *  above, and may be zero. */
+>     int  Valid;
+> 
+> 
+>     /** This field is valid when (Valid & DSSI_TRANSPORT_VALID_STATE)
+>      *  is true:
+>      *
+>      *  ---- The current transport state, one of the DSSI_TRANSPORT_STATE_*
+>      *       values defined above. */
+>     int  State;
+> 
+> 
+>     /** This field is valid when (Valid & DSSI_TRANSPORT_VALID_BPM)
+>      *  is true:
+>      *
+>      *  ---- The current tempo, in beats per minute.  */
+>     double Beats_Per_Minute;
+> 
+> 
+>     /** These six fields are valid when (Valid & DSSI_TRANSPORT_VALID_BBT)
+>      *  is true:
+>      *
+>      *  ---- The bar number at the beginning of the current process cycle. */
+>     unsigned long Bar;
+> 
+>      *  ---- The beat within that Bar. */
+>     unsigned long Beat;
+>     
+>     /** ---- The tick within that Beat. */
+>     unsigned long Tick;
+> 
+>     /** ---- The (possibly fractional) tick count since transport 'start'
+>      *       and the beginning of the current Bar. */
+>     double Bar_Start_Tick;
+> 
+>     /** ---- The number of beats per bar. */
+>     float  Beats_Per_Bar;
+> 
+>     /** ---- The number of ticks for each beat. */
+>     double Ticks_Per_Beat;
+> 
+>     /* [Sean says: I left out the 'beat_type' (time signature "denominator")
+>      * field of the jack_position_t structure, because I think it's useless
+>      * except to a notation program. Does anybody else feel like we need it?]
+>     
+> 
+>     /** These two fields are valid when (Valid & DSSI_TRANSPORT_VALID_TIME)
+>      *  is true:
+>      *
+>      *  ---- The transport time at the beginning of the current process
+>      *       cycle, in seconds. */
+>     double  Current_Time;
+> 
+>     /** ---- The transport time at the beginning of the next process
+>              cycle, unless repositioning occurs. */
+>     double  Next_Time;
+> 
+> } DSSI_Transport_Info;
+> 
+> typedef struct _DSSI_Host_Descriptor DSSI_Host_Descriptor; /* below */
+> 
+83,84c159,161
+<      * If we're lucky, this will never be needed.  For now all plugins
+<      * must set it to 1.
+---
+>      * All plugins must set this to 1 or 2.  The version 1 API contains
+>      * all DSSI_Descriptor fields through run_multiple_synths_adding(),
+>      * while the version 2 API adds the receive_host_descriptor().
+376a454,472
+> 
+>     /**
+>      * receive_host_descriptor()
+>      *
+>      * This member is a function pointer by which a host may provide
+>      * a plugin with a pointer to its DSSI_Host_Descriptor. Hosts
+>      * which provide host descriptor support must call this function
+>      * once per plugin shared object file, before any calls to
+>      * instantiate().
+>      *
+>      * NOTE: This field was added in version 2 of the DSSI API. Hosts
+>      * supporting version 2 must not access this field in a plugin
+>      * whose DSSI_API_Version is 1, and plugins supporting version 2
+>      * should behave reasonably under hosts (of any version) which do
+>      * not implement this function. A version 2 plugin that does not
+>      * provide this function must set this member to NULL.
+>      */
+>     void (*receive_host_descriptor)(DSSI_Host_Descriptor *Descriptor);
+> 
+377a474,598
+> 
+> struct _DSSI_Host_Descriptor {
+> 
+>     /**
+>      * DSSI_API_Version
+>      *
+>      * This member indicates the DSSI API level used by this host.
+>      * All hosts must set this to 2.  Hopefully, we'll get this right
+>      * the first time, and this will never be needed.
+>      */
+>     int DSSI_API_Version;
+> 
+>     /**
+>      * request_tranport_information()
+>      *
+>      * This member is a function pointer by which a plugin instance may
+>      * request that a host begin providing transport information (if
+>      * Request is non-zero), or notify the host that it no longer needs
+>      * transport information (if Request is zero).  Upon receiving a
+>      * non-zero request, the host should return a pointer to a
+>      * DSSI_Transport_Info structure if it is able to provide transport
+>      * information, or NULL otherwise.
+>      *
+>      * Once a plugin instance has received a non-null transport
+>      * information pointer, it may read from the structure at any time
+>      * within the execution of an audio class function (see doc/RFC.txt).
+>      * It should not consider the structure contents to be meaningful
+>      * while within a instantiation or control class function.  Also,
+>      * since the validity of fields within the structure may change
+>      * between each new invocation of an audio class function, a plugin
+>      * instance must check the Valid field of the structure accordingly
+>      * before using the structure's other contents.
+>      *
+>      * A host which does not support this function must set this member
+>      * to NULL.
+>      */
+>     DSSI_Transport_Info *
+>         (*request_transport_information)(LADSPA_Handle Instance,
+>                                          int           Request);
+> 
+>     /**
+>      * request_midi_send()
+>      *
+>      * This member is a function pointer that allows a plugin to
+>      * request the ability to send MIDI events to the host.
+>      *
+>      * While the interpretation of plugin-generated MIDI events is
+>      * host implementation specific, a mechanism exists by which a
+>      * plugin may declare to the host the number of destination
+>      * 'ports' and MIDI channels it can expect will be used in the
+>      * plugin-generated events.  Plugins which generate unchannelized
+>      * MIDI should supply zero for both Ports and Channels, otherwise
+>      * they should supply the maximum numbers for Ports and Channels
+>      * they expect to use.
+>      *
+>      * A plugin instance must call this function during instantiate().
+>      * [Sean says: this restriction seems reasonable to me, since
+>      * the host may need to create output ports, etc., and instantiate()
+>      * seems like a good place to do such things.  I'm sure I haven't
+>      * fully thought through all the details, though....]
+>      *
+>      * The host should return a non-zero value if it is able to
+>      * provide MIDI send for the plugin instance, otherwise it should
+>      * return zero, and the plugin instance may not subsequently call
+>      * midi_send().
+>      *
+>      * A host which does not support the MIDI send function must set
+>      * both this member and (*midi_send)() below to NULL.
+>      */
+>     int (*request_midi_send)(LADSPA_Handle Instance,
+>                              unsigned char Ports,
+>                              unsigned char Channels);
+> 
+>     /**
+>      * midi_send()
+>      *
+>      * This member is a function pointer by which a plugin actually
+>      * sends MIDI events to the host (provided it has received a non-
+>      * zero return from request_midi_send()). As in the run_synth()
+>      * functions, the Event pointer points to a block of EventCount
+>      * ALSA sequencer events.  The dest.port and data.*.channel fields
+>      * of each event are used to specify destination port and channel,
+>      * respectively, when the plugin is supplying channelized events.
+>      *
+>      * A plugin may only call this function from within the execution
+>      * of the audio class run_*() or select_program() functions. When
+>      * called from a run_*() functions, the events are timestamped
+>      * relative to the start of the block, (mis)using the ALSA "tick
+>      * time" field as a frame count. The plugin is responsible for
+>      * ensuring that events with differing timestamps are already
+>      * ordered by time, and that timestamps across multiple calls to
+>      * midi_send() from within the same run_*() invocation are
+>      * monotonic.  When midi_send() is called from within
+>      * select_program(), the timestamps are ignored, and the events
+>      * are considered to originate at the same frame time as the
+>      * select_program() call, if such a timing can be considered
+>      * meaningful.
+>      *
+>      * The memory pointed to by Event belongs to the plugin, and it is
+>      * the host's responsibility to copy the events as needed before
+>      * returning from the midi_send() call.
+>      *
+>      * A host which does not support the MIDI send function must set
+>      * both this member and (*request_midi_send)() above to NULL.
+>      */
+>     void (*midi_send)(LADSPA_Handle    Instance,
+>                       snd_seq_event_t *Event,
+>                       unsigned long    EventCount);
+> 
+>    /**
+>     * . . . additional fields could follow here, possibly supporting:
+>     *
+>     *   - a facility by which a plugin instance may request from a
+>     *       host a non-realtime thread in which to do off-line
+>     *       rendering, I/O, etc., thus (hopefully) avoiding the
+>     *       crashes that seem to occur when plugins create their own
+>     *       threads.  I got this idea after noticing that ZynAddSubFX
+>     *       achieves its gorgeous textures while remaining very
+>     *       responsive by doing a lot of non-real-time rendering.
+>     *       Several other uses for it have been mentioned on the DSSI
+>     *       list; I forget what.
+>     *
+>     *   - per-voice audio output
+>     */
+> };
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/plugins/SamplePlayer.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,553 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+/*
+    Based on trivial_sampler from the DSSI distribution
+    (by Chris Cannam, public domain).
+*/
+
+#include "SamplePlayer.h"
+
+#include <dssi.h>
+#include <cmath>
+
+#include <QMutexLocker>
+#include <QDir>
+#include <QFileInfo>
+
+#include <sndfile.h>
+#include <samplerate.h>
+#include <iostream>
+
+const char *const
+SamplePlayer::portNames[PortCount] =
+{
+    "Output",
+    "Tuned (on/off)",
+    "Base Pitch (MIDI)",
+    "Sustain (on/off)",
+    "Release time (s)"
+};
+
+const LADSPA_PortDescriptor 
+SamplePlayer::ports[PortCount] =
+{
+    LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO,
+    LADSPA_PORT_INPUT  | LADSPA_PORT_CONTROL,
+    LADSPA_PORT_INPUT  | LADSPA_PORT_CONTROL,
+    LADSPA_PORT_INPUT  | LADSPA_PORT_CONTROL,
+    LADSPA_PORT_INPUT  | LADSPA_PORT_CONTROL
+};
+
+const LADSPA_PortRangeHint 
+SamplePlayer::hints[PortCount] =
+{
+    { 0, 0, 0 },
+    { LADSPA_HINT_DEFAULT_MAXIMUM | LADSPA_HINT_INTEGER |
+      LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
+    { LADSPA_HINT_DEFAULT_MIDDLE | LADSPA_HINT_INTEGER |
+      LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 120 },
+    { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_INTEGER |
+      LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0, 1 },
+    { LADSPA_HINT_DEFAULT_MINIMUM | LADSPA_HINT_LOGARITHMIC |
+      LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE, 0.001, 2.0 }
+};
+
+const LADSPA_Properties
+SamplePlayer::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE;
+
+const LADSPA_Descriptor 
+SamplePlayer::ladspaDescriptor =
+{
+    0, // "Unique" ID
+    "sample_player", // Label
+    properties,
+    "Library Sample Player", // Name
+    "Chris Cannam", // Maker
+    "GPL", // Copyright
+    PortCount,
+    ports,
+    portNames,
+    hints,
+    0, // Implementation data
+    instantiate,
+    connectPort,
+    activate,
+    run,
+    0, // Run adding
+    0, // Set run adding gain
+    deactivate,
+    cleanup
+};
+
+const DSSI_Descriptor 
+SamplePlayer::dssiDescriptor =
+{
+    2, // DSSI API version
+    &ladspaDescriptor,
+    0, // Configure
+    getProgram,
+    selectProgram,
+    getMidiController,
+    runSynth,
+    0, // Run synth adding
+    0, // Run multiple synths
+    0, // Run multiple synths adding
+    receiveHostDescriptor
+};
+
+const DSSI_Host_Descriptor *
+SamplePlayer::hostDescriptor = 0;
+
+
+const DSSI_Descriptor *
+SamplePlayer::getDescriptor(unsigned long index)
+{
+    if (index == 0) return &dssiDescriptor;
+    return 0;
+}
+
+SamplePlayer::SamplePlayer(int sampleRate) :
+    m_output(0),
+    m_retune(0),
+    m_basePitch(0),
+    m_sustain(0),
+    m_release(0),
+    m_sampleData(0),
+    m_sampleCount(0),
+    m_sampleRate(sampleRate),
+    m_sampleNo(0),
+    m_sampleSearchComplete(false),
+    m_pendingProgramChange(-1)
+{
+}
+
+SamplePlayer::~SamplePlayer()
+{
+    if (m_sampleData) free(m_sampleData);
+}
+    
+LADSPA_Handle
+SamplePlayer::instantiate(const LADSPA_Descriptor *, unsigned long rate)
+{
+    if (!hostDescriptor || !hostDescriptor->request_non_rt_thread) {
+	std::cerr << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << std::endl;
+	return 0;
+    }
+
+    SamplePlayer *player = new SamplePlayer(rate);
+
+    if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) {
+	std::cerr << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << std::endl;
+	delete player;
+	return 0;
+    }
+
+    return player;
+}
+
+void
+SamplePlayer::connectPort(LADSPA_Handle handle,
+			  unsigned long port, LADSPA_Data *location)
+{
+    SamplePlayer *player = (SamplePlayer *)handle;
+
+    float **ports[PortCount] = {
+	&player->m_output,
+	&player->m_retune,
+	&player->m_basePitch,
+	&player->m_sustain,
+	&player->m_release
+    };
+
+    *ports[port] = (float *)location;
+}
+
+void
+SamplePlayer::activate(LADSPA_Handle handle)
+{
+    SamplePlayer *player = (SamplePlayer *)handle;
+    QMutexLocker locker(&player->m_mutex);
+
+    player->m_sampleNo = 0;
+
+    for (size_t i = 0; i < Polyphony; ++i) {
+	player->m_ons[i] = -1;
+	player->m_offs[i] = -1;
+	player->m_velocities[i] = 0;
+    }
+}
+
+void
+SamplePlayer::run(LADSPA_Handle handle, unsigned long samples)
+{
+    runSynth(handle, samples, 0, 0);
+}
+
+void
+SamplePlayer::deactivate(LADSPA_Handle handle)
+{
+    activate(handle); // both functions just reset the plugin
+}
+
+void
+SamplePlayer::cleanup(LADSPA_Handle handle)
+{
+    delete (SamplePlayer *)handle;
+}
+
+const DSSI_Program_Descriptor *
+SamplePlayer::getProgram(LADSPA_Handle handle, unsigned long program)
+{
+    SamplePlayer *player = (SamplePlayer *)handle;
+
+    if (!player->m_sampleSearchComplete) {
+	QMutexLocker locker(&player->m_mutex);
+	if (!player->m_sampleSearchComplete) {
+	    player->searchSamples();
+	}
+    }
+    if (program >= player->m_samples.size()) return 0;
+
+    static DSSI_Program_Descriptor descriptor;
+    static char name[60];
+
+    strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 60);
+    name[59] = '\0';
+
+    descriptor.Bank = 0;
+    descriptor.Program = program;
+    descriptor.Name = name;
+
+    return &descriptor;
+}
+
+void
+SamplePlayer::selectProgram(LADSPA_Handle handle,
+			    unsigned long,
+			    unsigned long program)
+{
+    SamplePlayer *player = (SamplePlayer *)handle;
+    player->m_pendingProgramChange = program;
+}
+
+int
+SamplePlayer::getMidiController(LADSPA_Handle, unsigned long port)
+{
+    int controllers[PortCount] = {
+	DSSI_NONE,
+	DSSI_CC(12),
+	DSSI_CC(13),
+	DSSI_CC(64),
+	DSSI_CC(72)
+    };
+
+    return controllers[port];
+}
+
+void
+SamplePlayer::runSynth(LADSPA_Handle handle, unsigned long samples,
+		       snd_seq_event_t *events, unsigned long eventCount)
+{
+    SamplePlayer *player = (SamplePlayer *)handle;
+
+    player->runImpl(samples, events, eventCount);
+}
+
+void
+SamplePlayer::receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor)
+{
+    hostDescriptor = descriptor;
+}
+
+void
+SamplePlayer::workThreadCallback(LADSPA_Handle handle)
+{
+    SamplePlayer *player = (SamplePlayer *)handle;
+
+    if (player->m_pendingProgramChange >= 0) {
+
+	std::cerr << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << std::endl;
+
+	player->m_mutex.lock();
+
+	int program = player->m_pendingProgramChange;
+	player->m_pendingProgramChange = -1;
+
+	if (!player->m_sampleSearchComplete) {
+	    player->searchSamples();
+	}
+	
+	if (program < int(player->m_samples.size())) {
+	    QString path = player->m_samples[program].second;
+	    QString programName = player->m_samples[program].first;
+	    if (programName != player->m_program) {
+		player->m_program = programName;
+		player->m_mutex.unlock();
+		player->loadSampleData(path);
+	    } else {
+		player->m_mutex.unlock();
+	    }
+	}
+    }
+
+    if (!player->m_sampleSearchComplete) {
+
+	QMutexLocker locker(&player->m_mutex);
+
+	if (!player->m_sampleSearchComplete) {
+	    player->searchSamples();
+	}
+    }
+}
+
+void
+SamplePlayer::searchSamples()
+{
+    if (m_sampleSearchComplete) return;
+
+    //!!!
+//    QString path = "/usr/share/hydrogen/data/drumkits/EasternHop-1";
+
+    std::cerr << "Current working directory is \"" << getcwd(0, 0) << "\"" << std::endl;
+
+    QString path = "samples";
+    
+    std::cerr << "SamplePlayer::searchSamples: Path is \""
+	      << path.toLocal8Bit().data() << "\"" << std::endl;
+
+    QDir dir(path, "*.wav");
+    for (unsigned int i = 0; i < dir.count(); ++i) {
+	QFileInfo file(dir.filePath(dir[i]));
+	m_samples.push_back(std::pair<QString, QString>
+			    (file.baseName(), file.filePath()));
+	std::cerr << "Found: " << dir[i].toLocal8Bit().data() << std::endl;
+    }
+
+    m_sampleSearchComplete = true;
+}
+
+void
+SamplePlayer::loadSampleData(QString path)
+{
+    SF_INFO info;
+    SNDFILE *file;
+    size_t samples = 0;
+    float *tmpFrames, *tmpSamples, *tmpResamples, *tmpOld;
+    size_t i;
+
+    info.format = 0;
+    file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
+    if (!file) {
+	std::cerr << "SamplePlayer::loadSampleData: Failed to open file "
+		  << path.toLocal8Bit().data() << ": "
+		  << sf_strerror(file) << std::endl;
+	return;
+    }
+    
+    samples = info.frames;
+    tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
+    if (!tmpFrames) return;
+
+    sf_readf_float(file, tmpFrames, info.frames);
+    sf_close(file);
+
+    tmpResamples = 0;
+
+    if (info.samplerate != m_sampleRate) {
+	
+	double ratio = (double)m_sampleRate / (double)info.samplerate;
+	size_t target = (size_t)(info.frames * ratio);
+	SRC_DATA data;
+
+	tmpResamples = (float *)malloc(target * info.channels * sizeof(float));
+	if (!tmpResamples) {
+	    free(tmpFrames);
+	    return;
+	}
+
+	memset(tmpResamples, 0, target * info.channels * sizeof(float));
+
+	data.data_in = tmpFrames;
+	data.data_out = tmpResamples;
+	data.input_frames = info.frames;
+	data.output_frames = target;
+	data.src_ratio = ratio;
+
+	if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) {
+	    free(tmpFrames);
+	    tmpFrames = tmpResamples;
+	    samples = target;
+	} else {
+	    free(tmpResamples);
+	}
+    }
+
+    /* add an extra sample for linear interpolation */
+    tmpSamples = (float *)malloc((samples + 1) * sizeof(float));
+    if (!tmpSamples) {
+	free(tmpFrames);
+	return;
+    }
+
+    for (i = 0; i < samples; ++i) {
+	int j;
+	tmpSamples[i] = 0.0f;
+	for (j = 0; j < info.channels; ++j) {
+	    tmpSamples[i] += tmpFrames[i * info.channels + j];
+	}
+    }
+
+    free(tmpFrames);
+
+    /* add an extra sample for linear interpolation */
+    tmpSamples[samples] = 0.0f;
+    
+    QMutexLocker locker(&m_mutex);
+
+    tmpOld = m_sampleData;
+    m_sampleData = tmpSamples;
+    m_sampleCount = samples;
+
+    for (i = 0; i < Polyphony; ++i) {
+	m_ons[i] = -1;
+	m_offs[i] = -1;
+	m_velocities[i] = 0;
+    }
+
+    if (tmpOld) free(tmpOld);
+
+    printf("%s: loaded %s (%ld samples from original %ld channels resampled from %ld frames at %ld Hz)\n", "sampler", path.toLocal8Bit().data(), (long)samples, (long)info.channels, (long)info.frames, (long)info.samplerate);
+}
+
+void
+SamplePlayer::runImpl(unsigned long sampleCount,
+		      snd_seq_event_t *events,
+		      unsigned long eventCount)
+{
+    unsigned long pos;
+    unsigned long count;
+    unsigned long event_pos;
+    int i;
+
+    memset(m_output, 0, sampleCount * sizeof(float));
+
+    if (!m_mutex.tryLock()) return;
+
+    if (!m_sampleData || !m_sampleCount) {
+	m_sampleNo += sampleCount;
+	m_mutex.unlock();
+	return;
+    }
+
+    for (pos = 0, event_pos = 0; pos < sampleCount; ) {
+
+	while (event_pos < eventCount
+	       && pos >= events[event_pos].time.tick) {
+
+	    if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) {
+		snd_seq_ev_note_t n = events[event_pos].data.note;
+		if (n.velocity > 0) {
+		    m_ons[n.note] =
+			m_sampleNo + events[event_pos].time.tick;
+		    m_offs[n.note] = -1;
+		    m_velocities[n.note] = n.velocity;
+		} else {
+		    if (!m_sustain || (*m_sustain < 0.001)) {
+			m_offs[n.note] = 
+			    m_sampleNo + events[event_pos].time.tick;
+		    }
+		}
+	    } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF &&
+		       (!m_sustain || (*m_sustain < 0.001))) {
+		snd_seq_ev_note_t n = events[event_pos].data.note;
+		m_offs[n.note] = 
+		    m_sampleNo + events[event_pos].time.tick;
+	    }
+
+	    ++event_pos;
+	}
+
+	count = sampleCount - pos;
+	if (event_pos < eventCount &&
+	    events[event_pos].time.tick < sampleCount) {
+	    count = events[event_pos].time.tick - pos;
+	}
+
+	for (i = 0; i < Polyphony; ++i) {
+	    if (m_ons[i] >= 0) {
+		addSample(i, pos, count);
+	    }
+	}
+
+	pos += count;
+    }
+
+    m_sampleNo += sampleCount;
+    m_mutex.unlock();
+}
+
+void
+SamplePlayer::addSample(int n, unsigned long pos, unsigned long count)
+{
+    float ratio = 1.0;
+    float gain = 1.0;
+    unsigned long i, s;
+
+    if (m_retune && *m_retune) {
+	if (m_basePitch && n != *m_basePitch) {
+	    ratio = powf(1.059463094, n - *m_basePitch);
+	}
+    }
+
+    if (long(pos + m_sampleNo) < m_ons[n]) return;
+
+    gain = (float)m_velocities[n] / 127.0f;
+
+    for (i = 0, s = pos + m_sampleNo - m_ons[n];
+	 i < count;
+	 ++i, ++s) {
+
+	float         lgain = gain;
+	float         rs = s * ratio;
+	unsigned long rsi = lrintf(floor(rs));
+
+	if (rsi >= m_sampleCount) {
+	    m_ons[n] = -1;
+	    break;
+	}
+
+	if (m_offs[n] >= 0 &&
+	    long(pos + i + m_sampleNo) > m_offs[n]) {
+
+	    unsigned long dist =
+		pos + i + m_sampleNo - m_offs[n];
+
+	    unsigned long releaseFrames = 200;
+	    if (m_release) {
+		releaseFrames = long(*m_release * m_sampleRate + 0.0001);
+	    }
+
+	    if (dist > releaseFrames) {
+		m_ons[n] = -1;
+		break;
+	    } else {
+		lgain = lgain * (float)(releaseFrames - dist) /
+		    (float)releaseFrames;
+	    }
+	}
+	
+	float sample = m_sampleData[rsi] +
+	    ((m_sampleData[rsi + 1] -
+	      m_sampleData[rsi]) *
+	     (rs - (float)rsi));
+
+	m_output[pos + i] += lgain * sample;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plugin/plugins/SamplePlayer.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,96 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _SAMPLE_PLAYER_H_
+#define _SAMPLE_PLAYER_H_
+
+#define DSSI_API_LEVEL 2
+
+#include <ladspa.h>
+#include <dssi.h>
+#include <seq_event.h>
+
+#include <QMutex>
+#include <QString>
+#include <vector>
+
+class SamplePlayer
+{
+public:
+    static const DSSI_Descriptor *getDescriptor(unsigned long index);
+
+private:
+    SamplePlayer(int sampleRate);
+    ~SamplePlayer();
+
+    enum {
+	OutputPort    = 0,
+	RetunePort    = 1,
+	BasePitchPort = 2,
+	SustainPort   = 3,
+	ReleasePort   = 4,
+	PortCount     = 5
+    };
+
+    enum {
+	Polyphony = 128
+    };
+
+    static const char *const portNames[PortCount];
+    static const LADSPA_PortDescriptor ports[PortCount];
+    static const LADSPA_PortRangeHint hints[PortCount];
+    static const LADSPA_Properties properties;
+    static const LADSPA_Descriptor ladspaDescriptor;
+    static const DSSI_Descriptor dssiDescriptor;
+    static const DSSI_Host_Descriptor *hostDescriptor;
+
+    static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long);
+    static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *);
+    static void activate(LADSPA_Handle);
+    static void run(LADSPA_Handle, unsigned long);
+    static void deactivate(LADSPA_Handle);
+    static void cleanup(LADSPA_Handle);
+    static const DSSI_Program_Descriptor *getProgram(LADSPA_Handle, unsigned long);
+    static void selectProgram(LADSPA_Handle, unsigned long, unsigned long);
+    static int getMidiController(LADSPA_Handle, unsigned long);
+    static void runSynth(LADSPA_Handle, unsigned long,
+			 snd_seq_event_t *, unsigned long);
+    static void receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor);
+    static void workThreadCallback(LADSPA_Handle);
+
+    void searchSamples();
+    void loadSampleData(QString path);
+    void runImpl(unsigned long, snd_seq_event_t *, unsigned long);
+    void addSample(int, unsigned long, unsigned long);
+
+    float *m_output;
+    float *m_retune;
+    float *m_basePitch;
+    float *m_sustain;
+    float *m_release;
+
+    float *m_sampleData;
+    size_t m_sampleCount;
+    int m_sampleRate;
+
+    long m_ons[Polyphony];
+    long m_offs[Polyphony];
+    int m_velocities[Polyphony];
+    long m_sampleNo;
+
+    QString m_program;
+    std::vector<std::pair<QString, QString> > m_samples; // program name, path
+    bool m_sampleSearchComplete;
+    int m_pendingProgramChange;
+
+    QMutex m_mutex;
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/BeatDetectTransform.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,203 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#include "BeatDetectTransform.h"
+
+#include "model/DenseTimeValueModel.h"
+#include "model/SparseOneDimensionalModel.h"
+
+#include <iostream>
+#include "dsp/onsets/DetectionFunction.h"
+#include "dsp/tempotracking/TempoTrack.h"
+
+
+BeatDetectTransform::BeatDetectTransform(Model *inputModel) :
+    Transform(inputModel)
+{
+    // Step resolution for the detection function in seconds
+    double stepSecs = 0.01161;
+
+    // Step resolution for the detection function in samples
+    size_t stepSize = (size_t)floor((double)inputModel->getSampleRate() * 
+				    stepSecs); 
+
+
+//    m_w->m_bdf->setResolution(stepSize);
+//    output->setResolution(stepSize);
+
+    std::cerr << "BeatDetectTransform::BeatDetectTransform: input sample rate " << inputModel->getSampleRate() << ", stepSecs " << stepSecs << ", stepSize " << stepSize << ", unrounded stepSize " << double(inputModel->getSampleRate()) * stepSecs << ", output sample rate " << inputModel->getSampleRate() / stepSize << ", unrounded output sample rate " << double(inputModel->getSampleRate()) / double(stepSize) << std::endl;
+
+    m_output = new SparseOneDimensionalModel(inputModel->getSampleRate(), 1);
+}
+
+BeatDetectTransform::~BeatDetectTransform()
+{
+    // parent does it all
+}
+
+TransformName
+BeatDetectTransform::getName()
+{
+    return tr("Beats");
+}
+
+void
+BeatDetectTransform::run()
+{
+    SparseOneDimensionalModel *output = getOutput();
+    DenseTimeValueModel *input = getInput();
+    if (!input) return;
+
+    DFConfig config;
+
+    config.DFType = DF_COMPLEXSD;
+
+    // Step resolution for the detection function in seconds
+    config.stepSecs = 0.01161;
+
+    // Step resolution for the detection function in samples
+    config.stepSize = (unsigned int)floor((double)input->getSampleRate() * 
+					  config.stepSecs ); 
+
+    config.frameLength = 2 * config.stepSize;
+
+    unsigned int stepSize = config.stepSize;
+    unsigned int frameLength = config.frameLength;
+
+//    m_w->m_bdf->setResolution(stepSize);
+    output->setResolution(stepSize);
+
+    //Tempo Tracking Configuration Parameters
+    TTParams ttparams;
+    
+    // Low Pass filter coefficients for detection function smoothing
+    double* aCoeffs = new double[3];
+    double* bCoeffs = new double[3];
+	
+    aCoeffs[ 0 ] = 1;
+    aCoeffs[ 1 ] = -0.5949;
+    aCoeffs[ 2 ] = 0.2348;
+    bCoeffs[ 0 ] = 0.1600;
+    bCoeffs[ 1 ] = 0.3200;
+    bCoeffs[ 2 ] = 0.1600;
+
+    ttparams.winLength = 512;
+    ttparams.lagLength = 128;
+    ttparams.LPOrd = 2;
+    ttparams.LPACoeffs = aCoeffs;
+    ttparams.LPBCoeffs = bCoeffs; 
+    ttparams.alpha = 9;
+    ttparams.WinT.post = 8;
+    ttparams.WinT.pre = 7;
+
+    ////////////////////////////////////////////////////////////
+    // DetectionFunction
+    ////////////////////////////////////////////////////////////
+    // Instantiate and configure detection function object
+
+    DetectionFunction df(config);
+
+    size_t origin = input->getStartFrame();
+    size_t frameCount = input->getEndFrame() - origin;
+    size_t blocks = (frameCount / stepSize);
+    if (blocks * stepSize < frameCount) ++blocks;
+
+    double *buffer = new double[frameLength];
+
+    // DF output with causal extension
+    unsigned int clen = blocks + ttparams.winLength;
+    double *dfOutput = new double[clen];
+
+    std::cerr << "Detecting beats at step size " << stepSize << "..." << std::endl;
+
+    for (size_t i = 0; i < clen; ++i) {
+
+//	std::cerr << "block " << i << "/" << clen << std::endl;
+//	std::cerr << ".";
+
+	if (i < blocks) {
+	    size_t got = input->getValues(-1, //!!! needs to come from parent layer -- which is not supposed to be in scope at this point
+					  origin + i * stepSize,
+					  origin + i * stepSize + frameLength,
+					  buffer);
+	    while (got < frameLength) buffer[got++] = 0.0;
+	    dfOutput[i] = df.process(buffer);
+	} else {
+	    dfOutput[i] = 0.0;
+	}
+
+//	m_w->m_bdf->addPoint(SparseTimeValueModel::Point
+//			     (i * stepSize, dfOutput[i],
+//			      QString("%1").arg(dfOutput[i])));
+//	m_w->m_bdf->setCompletion(i * 99 / clen);
+	output->setCompletion(i * 99 / clen);
+
+	if (m_deleting) {
+	    delete [] buffer;
+	    delete [] dfOutput;
+	    delete [] aCoeffs;
+	    delete [] bCoeffs;
+	    return;
+	}
+    }
+
+//    m_w->m_bdf->setCompletion(100);
+
+    // Tempo Track Object instantiation and configuration
+    TempoTrack tempoTracker(ttparams);
+
+    // Vector of detected onsets
+    vector<int> beats; 
+
+    std::cerr << "Running tempo tracker..." << std::endl;
+
+    beats = tempoTracker.process(dfOutput, blocks);
+
+    delete [] buffer;
+    delete [] dfOutput;
+    delete [] aCoeffs;
+    delete [] bCoeffs;
+
+    for (size_t i = 0; i < beats.size(); ++i) {
+//	std::cerr << "Beat value " << beats[i] << ", multiplying out to " << beats[i] * stepSize << std::endl;
+	float bpm = 0.0;
+	int fdiff = 0;
+	if (i < beats.size() - 1) {
+	    fdiff = (beats[i+1] - beats[i]) * stepSize;
+	    // one beat is fdiff frames, so there are samplerate/fdiff bps,
+	    // so 60*samplerate/fdiff bpm
+	    if (fdiff > 0) {
+		bpm = (60.0 * input->getSampleRate()) / fdiff;
+	    }
+	}
+	output->addPoint(SparseOneDimensionalModel::Point
+			 (origin + beats[i] * stepSize, QString("%1").arg(bpm)));
+	if (m_deleting) return;
+    }
+
+    output->setCompletion(100);
+}
+
+DenseTimeValueModel *
+BeatDetectTransform::getInput()
+{
+    DenseTimeValueModel *dtvm =
+	dynamic_cast<DenseTimeValueModel *>(getInputModel());
+    if (!dtvm) {
+	std::cerr << "BeatDetectTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+    }
+    return dtvm;
+}
+
+SparseOneDimensionalModel *
+BeatDetectTransform::getOutput()
+{
+    return static_cast<SparseOneDimensionalModel *>(getOutputModel());
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/BeatDetectTransform.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,38 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _BEAT_DETECT_TRANSFORM_H_
+#define _BEAT_DETECT_TRANSFORM_H_
+
+#include "Transform.h"
+
+//!!! This should be replaced by a plugin, when we have a plugin
+// transform.  But it's easier to start by testing concrete examples.
+
+class DenseTimeValueModel;
+class SparseOneDimensionalModel;
+
+class BeatDetectTransform : public Transform
+{
+public:
+    BeatDetectTransform(Model *inputModel);
+    virtual ~BeatDetectTransform();
+
+    static TransformName getName();
+
+protected:
+    virtual void run();
+
+    // just casts
+    DenseTimeValueModel *getInput();
+    SparseOneDimensionalModel *getOutput();
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/BeatDetectionFunctionTransform.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,154 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#include "BeatDetectionFunctionTransform.h"
+
+#include "model/DenseTimeValueModel.h"
+#include "model/SparseTimeValueModel.h"
+
+#include <iostream>
+#include "dsp/onsets/DetectionFunction.h"
+#include "dsp/tempotracking/TempoTrack.h"
+
+
+BeatDetectionFunctionTransform::BeatDetectionFunctionTransform(Model *inputModel) :
+    Transform(inputModel)
+{
+    m_output = new SparseTimeValueModel(inputModel->getSampleRate(), 1,
+					0.0, 0.0, false);
+}
+
+BeatDetectionFunctionTransform::~BeatDetectionFunctionTransform()
+{
+    // parent does it all
+}
+
+TransformName
+BeatDetectionFunctionTransform::getName()
+{
+    return tr("Beat Detection Function");
+}
+
+void
+BeatDetectionFunctionTransform::run()
+{
+    SparseTimeValueModel *output = getOutput();
+    DenseTimeValueModel *input = getInput();
+    if (!input) {
+	std::cerr << "BeatDetectionFunctionTransform::run: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+	return;
+    }
+
+    DFConfig config;
+
+    config.DFType = DF_COMPLEXSD;
+
+    // Step resolution for the detection function in seconds
+    config.stepSecs = 0.01161;
+
+    // Step resolution for the detection function in samples
+    config.stepSize = (unsigned int)floor((double)input->getSampleRate() * 
+					  config.stepSecs ); 
+
+    config.frameLength = 2 * config.stepSize;
+
+    unsigned int stepSize = config.stepSize;
+    unsigned int frameLength = config.frameLength;
+
+    output->setResolution(stepSize);
+
+    //Tempo Tracking Configuration Parameters
+    TTParams ttparams;
+    
+    // Low Pass filter coefficients for detection function smoothing
+    double* aCoeffs = new double[3];
+    double* bCoeffs = new double[3];
+	
+    aCoeffs[ 0 ] = 1;
+    aCoeffs[ 1 ] = -0.5949;
+    aCoeffs[ 2 ] = 0.2348;
+    bCoeffs[ 0 ] = 0.1600;
+    bCoeffs[ 1 ] = 0.3200;
+    bCoeffs[ 2 ] = 0.1600;
+
+    ttparams.winLength = 512;
+    ttparams.lagLength = 128;
+    ttparams.LPOrd = 2;
+    ttparams.LPACoeffs = aCoeffs;
+    ttparams.LPBCoeffs = bCoeffs; 
+    ttparams.alpha = 9;
+    ttparams.WinT.post = 8;
+    ttparams.WinT.pre = 7;
+
+    ////////////////////////////////////////////////////////////
+    // DetectionFunction
+    ////////////////////////////////////////////////////////////
+    // Instantiate and configure detection function object
+
+    DetectionFunction df(config);
+
+    size_t origin = input->getStartFrame();
+    size_t frameCount = input->getEndFrame() - origin;
+    size_t blocks = (frameCount / stepSize);
+    if (blocks * stepSize < frameCount) ++blocks;
+
+    double *buffer = new double[frameLength];
+
+    // DF output with causal extension
+    unsigned int clen = blocks + ttparams.winLength;
+    double *dfOutput = new double[clen];
+
+    std::cerr << "Running beat detection function at step size " << stepSize << "..." << std::endl;
+
+    for (size_t i = 0; i < clen; ++i) {
+
+//	std::cerr << "block " << i << "/" << clen << std::endl;
+//	std::cerr << ".";
+
+	if (i < blocks) {
+	    size_t got = input->getValues(-1, //!!! needs to come from parent layer -- which is not supposed to be in scope at this point
+					  origin + i * stepSize,
+					  origin + i * stepSize + frameLength,
+					  buffer);
+	    while (got < frameLength) buffer[got++] = 0.0;
+	    dfOutput[i] = df.process(buffer);
+	} else {
+	    dfOutput[i] = 0.0;
+	}
+
+	output->addPoint(SparseTimeValueModel::Point
+			 (i * stepSize, dfOutput[i],
+			  QString("%1").arg(dfOutput[i])));
+//	m_w->m_bdf->setCompletion(i * 99 / clen);
+	output->setCompletion(i * 99 / clen);
+
+	if (m_deleting) {
+	    delete [] buffer;
+	    delete [] dfOutput;
+	    delete [] aCoeffs;
+	    delete [] bCoeffs;
+	    return;
+	}
+    }
+
+    output->setCompletion(100);
+}
+
+DenseTimeValueModel *
+BeatDetectionFunctionTransform::getInput()
+{
+    return dynamic_cast<DenseTimeValueModel *>(getInputModel());
+}
+
+SparseTimeValueModel *
+BeatDetectionFunctionTransform::getOutput()
+{
+    return static_cast<SparseTimeValueModel *>(getOutputModel());
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/BeatDetectionFunctionTransform.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,38 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _BEAT_DETECTION_FUNCTION_TRANSFORM_H_
+#define _BEAT_DETECTION_FUNCTION_TRANSFORM_H_
+
+#include "Transform.h"
+
+//!!! This should be replaced by a plugin, when we have a plugin
+// transform.  But it's easier to start by testing concrete examples.
+
+class DenseTimeValueModel;
+class SparseTimeValueModel;
+
+class BeatDetectionFunctionTransform : public Transform
+{
+public:
+    BeatDetectionFunctionTransform(Model *inputModel);
+    virtual ~BeatDetectionFunctionTransform();
+
+    static TransformName getName();
+
+protected:
+    virtual void run();
+
+    // just casts
+    DenseTimeValueModel *getInput();
+    SparseTimeValueModel *getOutput();
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/FeatureExtractionPluginTransform.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,341 @@
+
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#include "FeatureExtractionPluginTransform.h"
+
+#include "plugin/FeatureExtractionPluginFactory.h"
+#include "plugin/FeatureExtractionPlugin.h"
+
+#include "base/Model.h"
+#include "model/SparseOneDimensionalModel.h"
+#include "model/SparseTimeValueModel.h"
+#include "model/DenseThreeDimensionalModel.h"
+#include "model/DenseTimeValueModel.h"
+
+#include <iostream>
+
+FeatureExtractionPluginTransform::FeatureExtractionPluginTransform(Model *inputModel,
+								   QString pluginId,
+								   QString outputName) :
+    Transform(inputModel),
+    m_plugin(0),
+    m_descriptor(0),
+    m_outputFeatureNo(0)
+{
+    std::cerr << "FeatureExtractionPluginTransform::FeatureExtractionPluginTransform: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl;
+
+    FeatureExtractionPluginFactory *factory =
+	FeatureExtractionPluginFactory::instanceFor(pluginId);
+
+    if (!factory) {
+	std::cerr << "FeatureExtractionPluginTransform: No factory available for plugin id \""
+		  << pluginId.toStdString() << "\"" << std::endl;
+	return;
+    }
+
+    m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate());
+
+    if (!m_plugin) {
+	std::cerr << "FeatureExtractionPluginTransform: Failed to instantiate plugin \""
+		  << pluginId.toStdString() << "\"" << std::endl;
+	return;
+    }
+
+    FeatureExtractionPlugin::OutputList outputs =
+	m_plugin->getOutputDescriptors();
+
+    if (outputs.empty()) {
+	std::cerr << "FeatureExtractionPluginTransform: Plugin \""
+		  << pluginId.toStdString() << "\" has no outputs" << std::endl;
+	return;
+    }
+    
+    for (size_t i = 0; i < outputs.size(); ++i) {
+	if (outputName == "" || outputs[i].name == outputName.toStdString()) {
+	    m_outputFeatureNo = i;
+	    m_descriptor = new FeatureExtractionPlugin::OutputDescriptor
+		(outputs[i]);
+	    break;
+	}
+    }
+
+    if (!m_descriptor) {
+	std::cerr << "FeatureExtractionPluginTransform: Plugin \""
+		  << pluginId.toStdString() << "\" has no output named \""
+		  << outputName.toStdString() << "\"" << std::endl;
+	return;
+    }
+
+    std::cerr << "FeatureExtractionPluginTransform: output sample type "
+	      << m_descriptor->sampleType << std::endl;
+
+    int valueCount = 1;
+    float minValue = 0.0, maxValue = 0.0;
+    
+    if (m_descriptor->hasFixedValueCount) {
+	valueCount = m_descriptor->valueCount;
+    }
+
+    if (valueCount > 0 && m_descriptor->hasKnownExtents) {
+	minValue = m_descriptor->minValue;
+	maxValue = m_descriptor->maxValue;
+    }
+
+    size_t modelRate = m_input->getSampleRate();
+    size_t modelResolution = 1;
+    
+    switch (m_descriptor->sampleType) {
+
+    case FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate:
+	if (m_descriptor->sampleRate != 0.0) {
+	    modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001);
+	}
+	break;
+
+    case FeatureExtractionPlugin::OutputDescriptor::OneSamplePerStep:
+	modelResolution = m_plugin->getPreferredStepSize();
+	break;
+
+    case FeatureExtractionPlugin::OutputDescriptor::FixedSampleRate:
+	modelRate = m_descriptor->sampleRate;
+	break;
+    }
+
+    if (valueCount == 0) {
+
+	m_output = new SparseOneDimensionalModel(modelRate, modelResolution);
+
+    } else if (valueCount == 1 ||
+
+	       // We don't have a sparse 3D model
+	       m_descriptor->sampleType ==
+	       FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate) {
+	
+	m_output = new SparseTimeValueModel(modelRate, modelResolution,
+					    minValue, maxValue, false);
+
+    } else {
+	
+	m_output = new DenseThreeDimensionalModel(modelRate, modelResolution,
+						  valueCount);
+    }
+}
+
+FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform()
+{
+    delete m_plugin;
+    delete m_descriptor;
+}
+
+DenseTimeValueModel *
+FeatureExtractionPluginTransform::getInput()
+{
+    DenseTimeValueModel *dtvm =
+	dynamic_cast<DenseTimeValueModel *>(getInputModel());
+    if (!dtvm) {
+	std::cerr << "FeatureExtractionPluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
+    }
+    return dtvm;
+}
+
+void
+FeatureExtractionPluginTransform::run()
+{
+    DenseTimeValueModel *input = getInput();
+    if (!input) return;
+
+    if (!m_output) return;
+
+    size_t channelCount = input->getChannelCount();
+    if (m_plugin->getMaxChannelCount() < channelCount) {
+	channelCount = 1;
+    }
+    if (m_plugin->getMinChannelCount() > channelCount) {
+	std::cerr << "FeatureExtractionPluginTransform::run: "
+		  << "Can't provide enough channels to plugin (plugin min "
+		  << m_plugin->getMinChannelCount() << ", max "
+		  << m_plugin->getMaxChannelCount() << ", input model has "
+		  << input->getChannelCount() << ")" << std::endl;
+	return;
+    }
+
+    size_t sampleRate = m_input->getSampleRate();
+
+    size_t stepSize = m_plugin->getPreferredStepSize();
+    size_t blockSize = m_plugin->getPreferredBlockSize();
+
+    m_plugin->initialise(channelCount, stepSize, blockSize);
+
+    float **buffers = new float*[channelCount];
+    for (size_t ch = 0; ch < channelCount; ++ch) {
+	buffers[ch] = new float[blockSize];
+    }
+
+    size_t startFrame = m_input->getStartFrame();
+    size_t   endFrame = m_input->getEndFrame();
+    size_t blockFrame = startFrame;
+
+    size_t prevCompletion = 0;
+
+    while (blockFrame < endFrame) {
+
+//	std::cerr << "FeatureExtractionPluginTransform::run: blockFrame "
+//		  << blockFrame << std::endl;
+
+	size_t completion =
+	    (((blockFrame - startFrame) / stepSize) * 99) /
+	    (   (endFrame - startFrame) / stepSize);
+
+	// channelCount is either m_input->channelCount or 1
+
+	size_t got = 0;
+
+	if (channelCount == 1) {
+	    got = input->getValues
+		(-1, blockFrame, blockFrame + blockSize, buffers[0]);
+	    while (got < blockSize) {
+		buffers[0][got++] = 0.0;
+	    }
+	} else {
+	    for (size_t ch = 0; ch < channelCount; ++ch) {
+		got = input->getValues
+		    (ch, blockFrame, blockFrame + blockSize, buffers[ch]);
+		while (got < blockSize) {
+		    buffers[ch][got++] = 0.0;
+		}
+	    }
+	}
+
+	FeatureExtractionPlugin::FeatureSet features = m_plugin->process
+	    (buffers, RealTime::frame2RealTime(blockFrame, sampleRate));
+
+	for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
+	    FeatureExtractionPlugin::Feature feature =
+		features[m_outputFeatureNo][fi];
+	    addFeature(blockFrame, feature);
+	}
+
+	if (blockFrame == startFrame || completion > prevCompletion) {
+	    setCompletion(completion);
+	    prevCompletion = completion;
+	}
+
+	blockFrame += stepSize;
+    }
+
+    FeatureExtractionPlugin::FeatureSet features = m_plugin->getRemainingFeatures();
+
+    for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
+	FeatureExtractionPlugin::Feature feature =
+	    features[m_outputFeatureNo][fi];
+	addFeature(blockFrame, feature);
+    }
+
+    setCompletion(100);
+}
+
+
+void
+FeatureExtractionPluginTransform::addFeature(size_t blockFrame,
+					     const FeatureExtractionPlugin::Feature &feature)
+{
+    size_t inputRate = m_input->getSampleRate();
+
+//    std::cerr << "FeatureExtractionPluginTransform::addFeature("
+//	      << blockFrame << ")" << std::endl;
+
+    int valueCount = 1;
+    if (m_descriptor->hasFixedValueCount) {
+	valueCount = m_descriptor->valueCount;
+    }
+
+    size_t frame = blockFrame;
+
+    if (m_descriptor->sampleType ==
+	FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate) {
+
+	if (!feature.hasTimestamp) {
+	    std::cerr
+		<< "WARNING: FeatureExtractionPluginTransform::addFeature: "
+		<< "Feature has variable sample rate but no timestamp!"
+		<< std::endl;
+	    return;
+	} else {
+	    frame = RealTime::realTime2Frame(feature.timestamp, inputRate);
+	}
+
+    } else if (m_descriptor->sampleType ==
+	       FeatureExtractionPlugin::OutputDescriptor::FixedSampleRate) {
+
+	if (feature.hasTimestamp) {
+	    //!!! warning: sampleRate may be non-integral
+	    frame = RealTime::realTime2Frame(feature.timestamp,
+					     m_descriptor->sampleRate);
+	} else {
+	    frame = m_output->getEndFrame() + 1;
+	}
+    }
+	
+    if (valueCount == 0) {
+
+	SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
+	if (!model) return;
+	model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str()));
+	
+    } else if (valueCount == 1 ||
+	       m_descriptor->sampleType == 
+	       FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate) {
+
+	float value = 0.0;
+	if (feature.values.size() > 0) value = feature.values[0];
+
+	SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
+	if (!model) return;
+	model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str()));
+	
+    } else {
+	
+	DenseThreeDimensionalModel::BinValueSet values = feature.values;
+	
+	DenseThreeDimensionalModel *model = getOutput<DenseThreeDimensionalModel>();
+	if (!model) return;
+
+	model->setBinValues(frame, values);
+    }
+}
+
+void
+FeatureExtractionPluginTransform::setCompletion(int completion)
+{
+    int valueCount = 1;
+    if (m_descriptor->hasFixedValueCount) {
+	valueCount = m_descriptor->valueCount;
+    }
+
+    if (valueCount == 0) {
+
+	SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
+	if (!model) return;
+	model->setCompletion(completion);
+
+    } else if (valueCount == 1 ||
+	       m_descriptor->sampleType ==
+	       FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate) {
+
+	SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
+	if (!model) return;
+	model->setCompletion(completion);
+
+    } else {
+
+	//!!! Can't actually do this with the 3D model (yet?)
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/FeatureExtractionPluginTransform.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,50 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+    
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_
+#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_
+
+#include "Transform.h"
+#include "FeatureExtractionPlugin.h"
+
+class DenseTimeValueModel;
+
+class FeatureExtractionPluginTransform : public Transform
+{
+public:
+    FeatureExtractionPluginTransform(Model *inputModel,
+				     QString plugin,
+				     QString outputName = "");
+    virtual ~FeatureExtractionPluginTransform();
+
+protected:
+    virtual void run();
+
+    FeatureExtractionPlugin *m_plugin;
+    FeatureExtractionPlugin::OutputDescriptor *m_descriptor;
+    int m_outputFeatureNo;
+
+    void addFeature(size_t blockFrame,
+		    const FeatureExtractionPlugin::Feature &feature);
+
+    void setCompletion(int);
+
+    // just casts
+    DenseTimeValueModel *getInput();
+    template <typename ModelClass> ModelClass *getOutput() {
+	ModelClass *mc = dynamic_cast<ModelClass *>(m_output);
+	if (!mc) {
+	    std::cerr << "FeatureExtractionPluginTransform::getOutput: Output model not conformable" << std::endl;
+	}
+	return mc;
+    }
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/Transform.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,26 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#include "Transform.h"
+
+Transform::Transform(Model *m) :
+    m_input(m),
+    m_output(0),
+    m_detached(false),
+    m_deleting(false)
+{
+}
+
+Transform::~Transform()
+{
+    m_deleting = true;
+    wait();
+    if (!m_detached) delete m_output;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/Transform.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,50 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _TRANSFORM_H_
+#define _TRANSFORM_H_
+
+#include <QThread>
+
+#include "base/Model.h"
+
+typedef QString TransformName;
+
+/**
+ * A Transform turns one data model into another.
+ *
+ * Typically in this application, a Transform might have a
+ * DenseTimeValueModel as its input (e.g. an audio waveform) and a
+ * SparseOneDimensionalModel (e.g. detected beats) as its output.
+ *
+ * The Transform typically runs in the background, as a separate
+ * thread populating the output model.  The model is available to the
+ * user of the Transform immediately, but may be initially empty until
+ * the background thread has populated it.
+ */
+
+class Transform : public QThread
+{
+public:
+    virtual ~Transform();
+
+    Model *getInputModel()  { return m_input; }
+    Model *getOutputModel() { return m_output; }
+    Model *detachOutputModel() { m_detached = true; return m_output; }
+
+protected:
+    Transform(Model *m);
+
+    Model *m_input; // I don't own this
+    Model *m_output; // I own this, unless...
+    bool m_detached; // ... this is true.
+    bool m_deleting;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/TransformFactory.cpp	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,144 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#include "TransformFactory.h"
+
+#include "BeatDetectTransform.h"
+#include "BeatDetectionFunctionTransform.h"
+#include "FeatureExtractionPluginTransform.h"
+
+#include "plugin/FeatureExtractionPluginFactory.h"
+
+#include <iostream>
+
+TransformFactory *
+TransformFactory::m_instance = new TransformFactory;
+
+TransformFactory *
+TransformFactory::instance()
+{
+    return m_instance;
+}
+
+TransformFactory::~TransformFactory()
+{
+}
+
+TransformFactory::TransformList
+TransformFactory::getAllTransforms()
+{
+    TransformList list;
+//!!!    list.push_back(BeatDetectTransform::getName());
+//    list.push_back(BeatDetectionFunctionTransform::getName());
+
+    //!!!
+    std::vector<QString> fexplugs =
+	FeatureExtractionPluginFactory::getAllPluginIdentifiers();
+
+    for (size_t i = 0; i < fexplugs.size(); ++i) {
+
+	QString pluginId = fexplugs[i];
+
+	FeatureExtractionPluginFactory *factory =
+	    FeatureExtractionPluginFactory::instanceFor(pluginId);
+
+	if (factory) {
+	    //!!! well, really we want to be able to query this without having to instantiate
+
+	    FeatureExtractionPlugin *plugin = 
+		factory->instantiatePlugin(pluginId, 48000);
+
+	    QString pluginDescription = plugin->getDescription().c_str();
+
+	    if (plugin) {
+
+		FeatureExtractionPlugin::OutputList outputs =
+		    plugin->getOutputDescriptors();
+
+		if (outputs.size() == 1) {
+		    list.push_back
+			(TransformDesc
+			 (QString("%1:%2").arg(pluginId).arg(outputs[0].name.c_str()),
+			  pluginDescription));
+		} else {
+		    for (size_t j = 0; j < outputs.size(); ++j) {
+			list.push_back
+			    (TransformDesc
+			     (QString("%1:%2").arg(pluginId).arg(outputs[j].name.c_str()),
+			      QString("%1: %2").arg(pluginDescription)
+			      .arg(outputs[j].description.c_str())));
+		    }
+		}
+	    }
+	}
+    }
+    
+    return list;
+}
+
+Transform *
+TransformFactory::createTransform(TransformName name, Model *inputModel)
+{
+    return createTransform(name, inputModel, true);
+}
+
+Transform *
+TransformFactory::createTransform(TransformName name, Model *inputModel,
+				  bool start)
+{
+    Transform *transform = 0;
+
+    if (name == BeatDetectTransform::getName()) {
+	transform = new BeatDetectTransform(inputModel);
+    } else if (name == BeatDetectionFunctionTransform::getName()) {
+	transform = new BeatDetectionFunctionTransform(inputModel);
+    } else {
+	QString id = name.section(':', 0, 2);
+	QString output = name.section(':', 3);
+	if (FeatureExtractionPluginFactory::instanceFor(id)) {
+	    transform = new FeatureExtractionPluginTransform(inputModel,
+							     id, output);
+	} else {
+	    std::cerr << "TransformFactory::createTransform: Unknown transform "
+		      << name.toStdString() << std::endl;
+	}
+    }
+
+    if (start && transform) transform->start();
+    return transform;
+}
+
+Model *
+TransformFactory::transform(TransformName name, Model *inputModel)
+{
+    Transform *t = createTransform(name, inputModel, false);
+
+    if (!t) return 0;
+
+    connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
+
+    t->start();
+    return t->detachOutputModel();
+}
+
+void
+TransformFactory::transformFinished()
+{
+    QObject *s = sender();
+    Transform *transform = dynamic_cast<Transform *>(s);
+    
+    if (!transform) {
+	std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
+	return;
+    }
+
+    transform->wait(); // unnecessary but reassuring
+    delete transform;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transform/TransformFactory.h	Tue Jan 10 16:33:16 2006 +0000
@@ -0,0 +1,72 @@
+/* -*- c-basic-offset: 4 -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    A waveform viewer and audio annotation editor.
+    Chris Cannam, Queen Mary University of London, 2005
+   
+    This is experimental software.  Not for distribution.
+*/
+
+#ifndef _TRANSFORM_FACTORY_H_
+#define _TRANSFORM_FACTORY_H_
+
+#include "Transform.h"
+
+class TransformFactory : public QObject
+{
+    Q_OBJECT
+
+public:
+    virtual ~TransformFactory();
+
+    static TransformFactory *instance();
+
+    // The name is intended to be computer-referencable, and unique
+    // within the application.  The description should be
+    // human-readable, and does not have to be unique.
+
+    struct TransformDesc {
+	TransformDesc(TransformName _name, QString _description = "") :
+	    name(_name), description(_description) { }
+	TransformName name;
+	QString description;
+    };
+    typedef std::vector<TransformDesc> TransformList;
+
+    TransformList getAllTransforms();
+
+    /**
+     * Return the output model resulting from applying the named
+     * transform to the given input model.  The transform may still be
+     * working in the background when the model is returned; check the
+     * output model's isReady completion status for more details.
+     *
+     * If the transform is unknown or the input model is not an
+     * appropriate type for the given transform, or if some other
+     * problem occurs, return 0.
+     * 
+     * The returned model is owned by the caller and must be deleted
+     * when no longer needed.
+     */
+    Model *transform(TransformName name, Model *inputModel);
+
+    //!!! Need some way to indicate that the input model has changed /
+    //been deleted so as not to blow up backgrounded transform!  -- Or
+    //indeed, if the output model has been deleted -- could equally
+    //well happen!
+
+    //!!! Need transform category!
+	
+protected slots:
+    void transformFinished();
+
+protected:
+    Transform *createTransform(TransformName name, Model *inputModel);
+    Transform *createTransform(TransformName name, Model *inputModel,
+			       bool start);
+
+    static TransformFactory *m_instance;
+};
+
+
+#endif