# HG changeset patch # User Chris Cannam # Date 1136910796 0 # Node ID da6937383da8f3fb85762baa8a8772228aaa7daa initial import diff -r 000000000000 -r da6937383da8 base/AudioLevel.cpp --- /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 +#include +#include +#include +#include + +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 LevelList; +static std::map 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]; +} + + diff -r 000000000000 -r da6937383da8 base/AudioLevel.h --- /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 + diff -r 000000000000 -r da6937383da8 base/AudioPlaySource.h --- /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 diff -r 000000000000 -r da6937383da8 base/Layer.cpp --- /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 + +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 + diff -r 000000000000 -r da6937383da8 base/Layer.h --- /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 +#include + +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 + diff -r 000000000000 -r da6937383da8 base/Model.cpp --- /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 + diff -r 000000000000 -r da6937383da8 base/Model.h --- /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 +#include + +typedef std::vector 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 diff -r 000000000000 -r da6937383da8 base/PlayParameters.cpp --- /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(); +} + + diff -r 000000000000 -r da6937383da8 base/PlayParameters.h --- /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 + +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 + + + + diff -r 000000000000 -r da6937383da8 base/Profiler.cpp --- /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 +#include "Profiler.h" + +#include +#include + +//#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 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::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 +} + diff -r 000000000000 -r da6937383da8 base/Profiler.h --- /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 +#include +#include + +#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 TimePair; + typedef std::pair ProfilePair; + typedef std::map ProfileMap; + typedef std::map 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 diff -r 000000000000 -r da6937383da8 base/PropertyContainer.cpp --- /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) +{ +} + diff -r 000000000000 -r da6937383da8 base/PropertyContainer.h --- /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 +#include +#include + +class Playable; + +class PropertyContainer +{ +public: + typedef QString PropertyName; + typedef std::vector 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 diff -r 000000000000 -r da6937383da8 base/RealTime.cpp --- /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 + +#if (__GNUC__ < 3) +#include +#define stringstream strstream +#else +#include +#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); + diff -r 000000000000 -r da6937383da8 base/RealTime.h --- /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 +#include + +/** + * 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 diff -r 000000000000 -r da6937383da8 base/RingBuffer.h --- /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 + +#include "System.h" +#include "Scavenger.h" + +//#define DEBUG_RINGBUFFER 1 + +#ifdef DEBUG_RINGBUFFER +#include +#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 +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 > m_scavenger; + +private: + RingBuffer(const RingBuffer &); // not provided + RingBuffer &operator=(const RingBuffer &); // not provided +}; + +template +Scavenger > RingBuffer::m_scavenger; + +template +RingBuffer::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[" << this << "]::RingBuffer(" << n << ")" << std::endl; +#endif + + for (int i = 0; i < N; ++i) m_readers[i] = 0; + + m_scavenger.scavenge(); +} + +template +RingBuffer::~RingBuffer() +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << this << "]::~RingBuffer" << std::endl; +#endif + + if (m_mlocked) { + MUNLOCK((void *)m_buffer, m_size * sizeof(T)); + } + delete[] m_buffer; + + m_scavenger.scavenge(); +} + +template +size_t +RingBuffer::getSize() const +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << this << "]::getSize(): " << m_size-1 << std::endl; +#endif + + return m_size - 1; +} + +template +void +RingBuffer::resize(size_t newSize) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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(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 +bool +RingBuffer::mlock() +{ + if (MLOCK((void *)m_buffer, m_size * sizeof(T))) return false; + m_mlocked = true; + return true; +} + +template +void +RingBuffer::reset() +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << this << "]::reset" << std::endl; +#endif + + m_writer = 0; + for (int i = 0; i < N; ++i) m_readers[i] = 0; +} + +template +size_t +RingBuffer::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[" << this << "]::getReadSpace(" << R << "): " << space << std::endl; +#endif + + return space; +} + +template +size_t +RingBuffer::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[" << this << "]::getWriteSpace(): " << space << std::endl; +#endif + + return space; +} + +template +size_t +RingBuffer::read(T *destination, size_t n, int R) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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[" << this << "]::read: read " << n << ", reader now " << m_readers[R] << std::endl; +#endif + + return n; +} + +template +size_t +RingBuffer::readAdding(T *destination, size_t n, int R) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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 +T +RingBuffer::readOne(int R) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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 +size_t +RingBuffer::peek(T *destination, size_t n, int R) const +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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[" << this << "]::peek: read " << n << std::endl; +#endif + + return n; +} + +template +T +RingBuffer::peekOne(int R) const +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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 +size_t +RingBuffer::skip(size_t n, int R) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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 +size_t +RingBuffer::write(const T *source, size_t n) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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[" << this << "]::write: wrote " << n << ", writer now " << m_writer << std::endl; +#endif + + return n; +} + +template +size_t +RingBuffer::zero(size_t n) +{ +#ifdef DEBUG_RINGBUFFER + std::cerr << "RingBuffer[" << 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_ diff -r 000000000000 -r da6937383da8 base/Scavenger.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 +#include + +/** + * 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 +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 ObjectTimePair; + typedef std::vector ObjectTimeList; + ObjectTimeList m_objects; + int m_sec; + + unsigned int m_claimed; + unsigned int m_scavenged; +}; + +/** + * A wrapper to permit arrays to be scavenged. + */ + +template +class ScavengerArrayWrapper +{ +public: + ScavengerArrayWrapper(T *array) : m_array(array) { } + ~ScavengerArrayWrapper() { delete[] m_array; } + +private: + T *m_array; +}; + + +template +Scavenger::Scavenger(int sec, int defaultObjectListSize) : + m_objects(ObjectTimeList(defaultObjectListSize)), + m_sec(sec), + m_claimed(0), + m_scavenged(0) +{ +} + +template +void +Scavenger::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 +void +Scavenger::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 diff -r 000000000000 -r da6937383da8 base/System.cpp --- /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 diff -r 000000000000 -r da6937383da8 base/System.h --- /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 +#include + +#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 +#include + +#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 + diff -r 000000000000 -r da6937383da8 base/View.cpp --- /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 +#include +#include +#include + +#include + +//#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(""); +} + +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(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(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(*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 + diff -r 000000000000 -r da6937383da8 base/View.h --- /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 +#include + +#include "base/ZoomConstraint.h" +#include "base/PropertyContainer.h" + +class Layer; +class ViewManager; + +#include + +/** + * 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 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 ProgressMap; + ProgressMap m_progressBars; // I own the ProgressBars + + ViewManager *m_manager; // I don't own this +}; + +#endif + diff -r 000000000000 -r da6937383da8 base/ViewManager.cpp --- /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 + +//#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 + diff -r 000000000000 -r da6937383da8 base/ViewManager.h --- /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 +#include + +#include + +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 m_playParameters; +}; + +#endif + diff -r 000000000000 -r da6937383da8 base/Window.h --- /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 +#include +#include + +enum WindowType { + RectangularWindow, + BartlettWindow, + HammingWindow, + HanningWindow, + BlackmanWindow, + GaussianWindow, + ParzenWindow +}; + +template +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 +void Window::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 diff -r 000000000000 -r da6937383da8 base/ZoomConstraint.h --- /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 + +/** + * 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 + diff -r 000000000000 -r da6937383da8 plugin/DSSIPluginFactory.cpp --- /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 + +#include + +#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 &list) +{ + for (std::vector::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 +DSSIPluginFactory::getPluginPath() +{ + std::vector 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 +DSSIPluginFactory::getLRDFPath(QString &baseUri) +{ + std::vector pathList = getPluginPath(); + std::vector 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::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; + } +} + + + diff -r 000000000000 -r da6937383da8 plugin/DSSIPluginFactory.h --- /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 + +class DSSIPluginInstance; + +class DSSIPluginFactory : public LADSPAPluginFactory +{ +public: + virtual ~DSSIPluginFactory(); + + virtual void enumeratePlugins(std::vector &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 getPluginPath(); + +#ifdef HAVE_LIBLRDF + virtual std::vector 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 + diff -r 000000000000 -r da6937383da8 plugin/DSSIPluginInstance.cpp --- /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 +#include + +#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 > DSSIPluginInstance::m_bufferScavenger(2, 10); +std::map > 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 + (i, data)); + + m_backupControlPortsIn.push_back(0.0); + + } else { + LADSPA_Data *data = new LADSPA_Data(0.0); + m_controlPortsOut.push_back( + std::pair(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 + (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::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::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::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::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::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(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(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 +} + diff -r 000000000000 -r da6937383da8 plugin/DSSIPluginInstance.h --- /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 +#include +#include +#include +#include +#include + +#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 > m_controlPortsIn; + std::vector > m_controlPortsOut; + + std::vector m_backupControlPortsIn; + + std::map m_controllerMap; + + std::vector m_audioPortsIn; + std::vector m_audioPortsOut; + + struct ProgramControl { + int msb; + int lsb; + int program; + }; + ProgramControl m_pending; + + struct ProgramDescriptor { + int bank; + int program; + QString name; + }; + mutable std::vector m_cachedPrograms; + mutable bool m_programCacheValid; + + RingBuffer 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 PluginSet; + typedef std::map GroupMap; + static GroupMap m_groupMap; + static snd_seq_event_t **m_groupLocalEventBuffers; + static size_t m_groupLocalEventBufferCount; + + static Scavenger > 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 > m_threads; +}; + +#endif // _DSSIPLUGININSTANCE_H_ + diff -r 000000000000 -r da6937383da8 plugin/FeatureExtractionPlugin.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 +#include +#include + +#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 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 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 values; + + /** + * Label for the sample of this feature. + */ + std::string label; + }; + + typedef std::vector FeatureList; + typedef std::map 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 + + + diff -r 000000000000 -r da6937383da8 plugin/FeatureExtractionPluginFactory.cpp --- /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 + +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 +FeatureExtractionPluginFactory::getAllPluginIdentifiers() +{ + FeatureExtractionPluginFactory *factory; + std::vector rv; + + factory = instance("sv"); + if (factory) { + std::vector 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 +FeatureExtractionPluginFactory::getPluginIdentifiers() +{ + std::vector 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; +} + diff -r 000000000000 -r da6937383da8 plugin/FeatureExtractionPluginFactory.h --- /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 +#include + +class FeatureExtractionPlugin; + +class FeatureExtractionPluginFactory +{ +public: + static FeatureExtractionPluginFactory *instance(QString pluginType); + static FeatureExtractionPluginFactory *instanceFor(QString identifier); + static std::vector getAllPluginIdentifiers(); + + std::vector 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 diff -r 000000000000 -r da6937383da8 plugin/LADSPAPluginFactory.cpp --- /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 + +#include +#include +#include + +#include + +#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::iterator i = m_instances.begin(); + i != m_instances.end(); ++i) { + (*i)->setFactory(0); + delete *i; + } + m_instances.clear(); + unloadUnusedLibraries(); +} + +const std::vector & +LADSPAPluginFactory::getPluginIdentifiers() const +{ + return m_identifiers; +} + +void +LADSPAPluginFactory::enumeratePlugins(std::vector &list) +{ + for (std::vector::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::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 toUnload; + + for (LibraryHandleMap::iterator i = m_libraryHandles.begin(); + i != m_libraryHandles.end(); ++i) { + + bool stillInUse = false; + + for (std::set::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::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 +LADSPAPluginFactory::getPluginPath() +{ + std::vector 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 +LADSPAPluginFactory::getLRDFPath(QString &baseUri) +{ + std::vector pathList = getPluginPath(); + std::vector lrdfPaths; + + lrdfPaths.push_back("/usr/local/share/ladspa/rdf"); + lrdfPaths.push_back("/usr/share/ladspa/rdf"); + + for (std::vector::iterator i = pathList.begin(); + i != pathList.end(); ++i) { + lrdfPaths.push_back(*i + "/rdf"); + } + + baseUri = LADSPA_BASE; + return lrdfPaths; +} +#endif + +void +LADSPAPluginFactory::discoverPlugins() +{ + std::vector pathList = getPluginPath(); + +// std::cerr << "LADSPAPluginFactory::discoverPlugins - " +// << "discovering plugins; path is "; + for (std::vector::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 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::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 pluginPath = getPluginPath(); + std::vector 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 +} + + diff -r 000000000000 -r da6937383da8 plugin/LADSPAPluginFactory.h --- /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 +#include +#include +#include + +class LADSPAPluginInstance; + +class LADSPAPluginFactory : public RealTimePluginFactory +{ +public: + virtual ~LADSPAPluginFactory(); + + virtual void discoverPlugins(); + + virtual const std::vector &getPluginIdentifiers() const; + + virtual void enumeratePlugins(std::vector &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 getPluginPath(); + +#ifdef HAVE_LIBLRDF + virtual std::vector 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 m_identifiers; + + std::map m_taxonomy; + std::map m_fallbackCategories; + std::map > m_portDefaults; + + std::set m_instances; + + typedef std::map LibraryHandleMap; + LibraryHandleMap m_libraryHandles; +}; + +#endif + diff -r 000000000000 -r da6937383da8 plugin/LADSPAPluginInstance.cpp --- /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 +#include + +#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(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(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::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::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(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(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::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::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::iterator hi = m_instanceHandles.begin(); + hi != m_instanceHandles.end(); ++hi) { + m_descriptor->cleanup(*hi); + } + + m_instanceHandles.clear(); +} + + diff -r 000000000000 -r da6937383da8 plugin/LADSPAPluginInstance.h --- /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 +#include +#include + +#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 m_instanceHandles; + size_t m_instanceCount; + const LADSPA_Descriptor *m_descriptor; + + std::vector > m_controlPortsIn; + std::vector > m_controlPortsOut; + + std::vector m_audioPortsIn; + std::vector 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_ + diff -r 000000000000 -r da6937383da8 plugin/PluginDescription.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 +{ + diff -r 000000000000 -r da6937383da8 plugin/PluginIdentifier.cpp --- /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 + +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"; + diff -r 000000000000 -r da6937383da8 plugin/PluginIdentifier.h --- /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 + + +// 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 diff -r 000000000000 -r da6937383da8 plugin/RealTimePluginFactory.cpp --- /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 , + Chris Cannam , + Richard Bown + + 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 + +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 +RealTimePluginFactory::getAllPluginIdentifiers() +{ + RealTimePluginFactory *factory; + std::vector 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 &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 &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 &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"); +} + diff -r 000000000000 -r da6937383da8 plugin/RealTimePluginFactory.h --- /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 +#include + +class RealTimePluginInstance; + +class RealTimePluginFactory +{ +public: + static RealTimePluginFactory *instance(QString pluginType); + static RealTimePluginFactory *instanceFor(QString identifier); + static std::vector getAllPluginIdentifiers(); + static void enumerateAllPlugins(std::vector &); + + 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 &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 &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 diff -r 000000000000 -r da6937383da8 plugin/RealTimePluginInstance.cpp --- /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 + + +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); + } +} + + diff -r 000000000000 -r da6937383da8 plugin/RealTimePluginInstance.h --- /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 +#include +#include + +#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 diff -r 000000000000 -r da6937383da8 plugin/api/FEAPI.h --- /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. +//

+// In the following, context function pointers typedefs are referred to as +// functions.
+// +// +// Create a new instance of the plugin with the function +// ::FEAPI_CreatePluginInstance. The call of this function is mandatory. +//

+// +// Initialize the plugin with the call of function +// ::FEAPI_InitializePlugin.

+// +// Information about the plugin can be requested via the following +// functions:
+// ::FEAPI_GetPluginAPIVersion,
+// ::FEAPI_GetPluginName,
+// ::FEAPI_GetPluginVendor,
+// ::FEAPI_GetPluginVendorVersion,
+// ::FEAPI_GetPluginCopyright,
+// ::FEAPI_GetPluginDescription.
+// The call of these functions is optional. Combined with a call to +// ::FEAPI_GetPluginId, the plugin can be uniquely identified.

+// +// The technical capabilities of the plugin can be requested via the call +// of the function +// ::FEAPI_GetPluginProperty.

+// +// To get the number of the features resp. results that are computed +// by the plugin, call the function
+// ::FEAPI_GetPluginNumOfResults;
+// the function
+// ::FEAPI_GetPluginResultDescription gives you detailed information about the +// meaning and usage of every result (see structure ::_ResultDescription +// for details).

+// +// To get the number of the options/ parameter settings that can be +// done before processing, call the function
+// ::FEAPI_GetPluginNumOfParameters;
+// the function
+// ::FEAPI_GetPluginParamDescription gives you detailed information about +// the meaning and usage of every single parameter (see structure +// ::FEAPI_ParameterDescription_t for details).

+// +// To get or set a specific parameter value, call the function
+// ::FEAPI_GetPluginParameter resp.
+// ::FEAPI_SetPluginParameter.

+// +// After the plugin is initialized, the actual processing can begin. +// The ::FEAPI_ProcessPlugin
+// 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.
+// +// 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
+// ::FEAPI_GetPluginSizeOfResult,
+// which returns the number of values for this +// result. Afterwards, the result values for each result can +// be requested via
+// ::FEAPI_GetPluginResult. Note that the memory for the results has to be +// allocated by the host.

+// +// To signal that no more audio data is available at the end (e.g. of +// the audio file), call
+// ::FEAPI_ProcessPluginDone and get the last results with +// ::FEAPI_GetPluginResult if available.

+// +// To flush the internal buffers, the function
+// ::FEAPI_ResetPlugin
+// may be called. +// +// After all processing has been done, destroy the instance of the +// plugin with the function
+// ::FEAPI_DestroyPluginInstance.
+// The call of this function is mandatory.

+// +//


+// The Feature Extraction plugin API is released under a BSD style +// license. Please make all changes available to the authors.
+// Contact information: lerch 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).
+ 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) diff -r 000000000000 -r da6937383da8 plugin/api/alsa/README --- /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 /lib; for example, on Mac +OS X, the following files are installed: + + /lib/libdssialsacompat.0.0.0.dylib + /lib/libdssialsacompat.0.dylib + /lib/libdssialsacompat.a + /lib/libdssialsacompat.dylib + /lib/libdssialsacompat.la + +The header files are installed to /include/dssi/alsa: + + /include/dssi/alsa/asoundef.h + /include/dssi/alsa/asoundlib.h + /include/dssi/alsa/seq.h + /include/dssi/alsa/seq_event.h + /include/dssi/alsa/seq_midi_event.h + /include/dssi/alsa/sound/asequencer.h + +Note that they are NOT installed to /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: + + /lib/pkgconfig/libdssialsacompat.pc + +Use +=== +At its most basic, compiling with gcc and libdssialsacompat consists +of adding '-I/include/dssi -L/lib -ldssialsacompat' +to your gcc command line. Note that the '-I' directive will cause +code like to following: + + #include + #include + +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. + diff -r 000000000000 -r da6937383da8 plugin/api/alsa/asoundef.h --- /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 + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \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 */ diff -r 000000000000 -r da6937383da8 plugin/api/alsa/asoundlib.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 + +#include +#include +#include + diff -r 000000000000 -r da6937383da8 plugin/api/alsa/seq.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 + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \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 */ + diff -r 000000000000 -r da6937383da8 plugin/api/alsa/seq_event.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 + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \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 */ + diff -r 000000000000 -r da6937383da8 plugin/api/alsa/seq_midi_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 + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \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 */ + diff -r 000000000000 -r da6937383da8 plugin/api/alsa/sound/asequencer.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 + * (c) 1998-1999 by Jaroslav Kysela + * + * + * 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 */ diff -r 000000000000 -r da6937383da8 plugin/api/dssi.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 */ diff -r 000000000000 -r da6937383da8 plugin/api/dssi_alsa_compat.c --- /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 +#include + +#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 + * \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 + * \author Jaroslav Kysela + * \date 2000-2001 + */ + +/* + * MIDI byte <-> sequencer event coder + * + * Copyright (C) 1998,99,2000 Takashi Iwai , + * Jaroslav Kysela + * + * + * 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 */ diff -r 000000000000 -r da6937383da8 plugin/api/ladspa.h --- /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 */ diff -r 000000000000 -r da6937383da8 plugin/api/new_dssi_transport_patch --- /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 +> */ +> }; diff -r 000000000000 -r da6937383da8 plugin/plugins/SamplePlayer.cpp --- /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 +#include + +#include +#include +#include + +#include +#include +#include + +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 + (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; + } +} diff -r 000000000000 -r da6937383da8 plugin/plugins/SamplePlayer.h --- /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 +#include +#include + +#include +#include +#include + +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 > m_samples; // program name, path + bool m_sampleSearchComplete; + int m_pendingProgramChange; + + QMutex m_mutex; +}; + + +#endif diff -r 000000000000 -r da6937383da8 transform/BeatDetectTransform.cpp --- /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 +#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 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(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(getOutputModel()); +} + diff -r 000000000000 -r da6937383da8 transform/BeatDetectTransform.h --- /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 + diff -r 000000000000 -r da6937383da8 transform/BeatDetectionFunctionTransform.cpp --- /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 +#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(getInputModel()); +} + +SparseTimeValueModel * +BeatDetectionFunctionTransform::getOutput() +{ + return static_cast(getOutputModel()); +} + diff -r 000000000000 -r da6937383da8 transform/BeatDetectionFunctionTransform.h --- /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 + diff -r 000000000000 -r da6937383da8 transform/FeatureExtractionPluginTransform.cpp --- /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 + +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(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(); + 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(); + if (!model) return; + model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); + + } else { + + DenseThreeDimensionalModel::BinValueSet values = feature.values; + + DenseThreeDimensionalModel *model = getOutput(); + 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(); + if (!model) return; + model->setCompletion(completion); + + } else if (valueCount == 1 || + m_descriptor->sampleType == + FeatureExtractionPlugin::OutputDescriptor::VariableSampleRate) { + + SparseTimeValueModel *model = getOutput(); + if (!model) return; + model->setCompletion(completion); + + } else { + + //!!! Can't actually do this with the 3D model (yet?) + } +} + diff -r 000000000000 -r da6937383da8 transform/FeatureExtractionPluginTransform.h --- /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 ModelClass *getOutput() { + ModelClass *mc = dynamic_cast(m_output); + if (!mc) { + std::cerr << "FeatureExtractionPluginTransform::getOutput: Output model not conformable" << std::endl; + } + return mc; + } +}; + +#endif + diff -r 000000000000 -r da6937383da8 transform/Transform.cpp --- /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; +} + diff -r 000000000000 -r da6937383da8 transform/Transform.h --- /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 + +#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 diff -r 000000000000 -r da6937383da8 transform/TransformFactory.cpp --- /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 + +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 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(s); + + if (!transform) { + std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl; + return; + } + + transform->wait(); // unnecessary but reassuring + delete transform; +} + diff -r 000000000000 -r da6937383da8 transform/TransformFactory.h --- /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 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