Mercurial > hg > svcore
changeset 383:94fc0591ea43 1.2-stable
* merge from trunk (1.2 ended up being tracked from trunk, but we may want
this branch for fixes later)
line wrap: on
line diff
--- a/base/Clipboard.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Clipboard.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -177,6 +177,12 @@ return m_haveReferenceFrame; } +bool +Clipboard::Point::referenceFrameDiffers() const +{ + return m_haveReferenceFrame && (m_referenceFrame != m_frame); +} + long Clipboard::Point::getReferenceFrame() const { @@ -186,7 +192,7 @@ void Clipboard::Point::setReferenceFrame(long f) { - if (f != m_frame) m_haveReferenceFrame = true; + m_haveReferenceFrame = true; m_referenceFrame = f; } @@ -233,3 +239,13 @@ return false; } +bool +Clipboard::referenceFramesDiffer() const +{ + for (PointList::const_iterator i = m_points.begin(); + i != m_points.end(); ++i) { + if (i->referenceFrameDiffers()) return true; + } + return false; +} +
--- a/base/Clipboard.h Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Clipboard.h Wed Feb 27 10:32:45 2008 +0000 @@ -48,6 +48,8 @@ float getLevel() const; bool haveReferenceFrame() const; + bool referenceFrameDiffers() const; // from point frame + long getReferenceFrame() const; void setReferenceFrame(long); @@ -78,6 +80,7 @@ void addPoint(const Point &point); bool haveReferenceFrames() const; + bool referenceFramesDiffer() const; protected: PointList m_points;
--- a/base/CommandHistory.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/CommandHistory.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -216,6 +216,10 @@ CommandHistory::addToCompound(Command *command, bool execute) { // std::cerr << "CommandHistory::addToCompound: " << command->getName().toLocal8Bit().data() << std::endl; + if (!m_currentCompound) { + std::cerr << "CommandHistory::addToCompound: ERROR: no compound operation in progress!" << std::endl; + return; + } if (execute) command->execute(); m_currentCompound->addCommand(command); @@ -227,6 +231,7 @@ if (m_currentCompound) { std::cerr << "CommandHistory::startCompoundOperation: ERROR: compound operation already in progress!" << std::endl; std::cerr << "(name is " << m_currentCompound->getName().toLocal8Bit().data() << ")" << std::endl; + return; } closeBundle(); @@ -240,6 +245,7 @@ { if (!m_currentCompound) { std::cerr << "CommandHistory::endCompoundOperation: ERROR: no compound operation in progress!" << std::endl; + return; } MacroCommand *toAdd = m_currentCompound;
--- a/base/Pitch.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Pitch.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -52,6 +52,37 @@ return midiPitch; } +int +Pitch::getPitchForFrequencyDifference(float frequencyA, + float frequencyB, + float *centsOffsetReturn, + float concertA) +{ + if (concertA <= 0.0) { + concertA = Preferences::getInstance()->getTuningFrequency(); + } + + if (frequencyA > frequencyB) { + std::swap(frequencyA, frequencyB); + } + + float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0; + float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0; + + float p = pB - pA; + + int midiPitch = int(p + 0.00001); + float centsOffset = (p - midiPitch) * 100.0; + + if (centsOffset >= 50.0) { + midiPitch = midiPitch + 1; + centsOffset = -(100.0 - centsOffset); + } + + if (centsOffsetReturn) *centsOffsetReturn = centsOffset; + return midiPitch; +} + static QString notes[] = { "C%1", "C#%1", "D%1", "D#%1", "E%1", "F%1", "F#%1", "G%1", @@ -101,6 +132,34 @@ return getPitchLabel(midiPitch, centsOffset, useFlats); } +QString +Pitch::getLabelForPitchRange(int semis, float cents) +{ + int ic = lrintf(cents); + + if (ic == 0) { + if (semis >= 12) { + return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12)); + } else { + return QString("%1").arg(semis); + } + } else { + if (ic > 0) { + if (semis >= 12) { + return QString("%1'%2+%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic); + } else { + return QString("%1+%3c").arg(semis).arg(ic); + } + } else { + if (semis >= 12) { + return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic); + } else { + return QString("%1%3c").arg(semis).arg(ic); + } + } + } +} + bool Pitch::isFrequencyInMidiRange(float frequency, float concertA)
--- a/base/Pitch.h Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Pitch.h Wed Feb 27 10:32:45 2008 +0000 @@ -51,6 +51,25 @@ float concertA = 0.0); /** + * Return the nearest MIDI pitch range to the given frequency + * range, that is, the difference in MIDI pitch values between the + * higher and lower frequencies. + * + * If centsOffsetReturn is non-NULL, return in *centsOffsetReturn + * the number of cents (1/100ths of a semitone) difference between + * the given frequency difference and the returned MIDI pitch + * range. The cents offset will be in the range [-50,50). + * + * If concertA is non-zero, use that as the reference frequency + * for the A at MIDI pitch 69; otherwise use the tuning frequency + * specified in the application preferences (default 440Hz). + */ + static int getPitchForFrequencyDifference(float frequencyA, + float frequencyB, + float *centsOffsetReturn = 0, + float concertA = 0.0); + + /** * Return a string describing the given MIDI pitch, with optional * cents offset. This consists of the note name, octave number * (with MIDI pitch 0 having octave number -2), and optional @@ -82,6 +101,12 @@ bool useFlats = false); /** + * Return a string describing the given pitch range in octaves, + * semitones and cents. This is in the form e.g. "1'2+4c". + */ + static QString getLabelForPitchRange(int semis, float cents = 0); + + /** * Return true if the given frequency falls within the range of * MIDI note pitches, plus or minus half a semitone. This is * equivalent to testing whether getPitchForFrequency returns a
--- a/base/Preferences.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Preferences.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -23,6 +23,8 @@ #include <QFileInfo> #include <QMutex> #include <QSettings> +#include <QApplication> +#include <QFont> Preferences * Preferences::m_instance = 0; @@ -42,7 +44,10 @@ m_resampleQuality(1), m_omitRecentTemps(true), m_tempDirRoot(""), - m_resampleOnLoad(false) + m_resampleOnLoad(false), + m_viewFontSize(10), + m_backgroundMode(BackgroundFromTheme), + m_showSplash(true) { QSettings settings; settings.beginGroup("Preferences"); @@ -57,6 +62,10 @@ m_resampleOnLoad = settings.value("resample-on-load", false).toBool(); m_backgroundMode = BackgroundMode (settings.value("background-mode", int(BackgroundFromTheme)).toInt()); + m_viewFontSize = settings.value + ("view-font-size", int(QApplication::font().pointSize() * 0.9)) + .toInt(); + m_showSplash = settings.value("show-splash", true).toBool(); settings.endGroup(); settings.beginGroup("TempDirectory"); @@ -81,6 +90,8 @@ props.push_back("Resample On Load"); props.push_back("Temporary Directory Root"); props.push_back("Background Mode"); + props.push_back("View Font Size"); + props.push_back("Show Splash Screen"); return props; } @@ -88,7 +99,7 @@ Preferences::getPropertyLabel(const PropertyName &name) const { if (name == "Spectrogram Smoothing") { - return tr("Spectrogram y-axis smoothing:"); + return tr("Spectrogram y-axis interpolation:"); } if (name == "Tuning Frequency") { return tr("Frequency of concert A"); @@ -114,6 +125,12 @@ if (name == "Background Mode") { return tr("Background colour preference"); } + if (name == "View Font Size") { + return tr("Font size for text overlays"); + } + if (name == "Show Splash Screen") { + return tr("Show splash screen on startup"); + } return name; } @@ -148,6 +165,12 @@ if (name == "Background Mode") { return ValueProperty; } + if (name == "View Font Size") { + return RangeProperty; + } + if (name == "Show Splash Screen") { + return ToggleProperty; + } return InvalidProperty; } @@ -196,6 +219,17 @@ return int(m_backgroundMode); } + if (name == "View Font Size") { + if (min) *min = 3; + if (max) *max = 48; + if (deflt) *deflt = int(QApplication::font().pointSize() * 0.9); + return int(m_viewFontSize); + } + + if (name == "Show Splash Screen") { + if (deflt) *deflt = 1; + } + return 0; } @@ -212,7 +246,7 @@ case RectangularWindow: return tr("Rectangular"); case BartlettWindow: return tr("Triangular"); case HammingWindow: return tr("Hamming"); - case HanningWindow: return tr("Hanning"); + case HanningWindow: return tr("Hann"); case BlackmanWindow: return tr("Blackman"); case GaussianWindow: return tr("Gaussian"); case ParzenWindow: return tr("Parzen"); @@ -230,8 +264,8 @@ if (name == "Spectrogram Smoothing") { switch (value) { case NoSpectrogramSmoothing: return tr("None - blocky but accurate"); - case SpectrogramInterpolated: return tr("Interpolate - fast but fuzzy"); - case SpectrogramZeroPadded: return tr("Zero pad FFT - slow but clear"); + case SpectrogramInterpolated: return tr("Linear - fast but fuzzy"); + case SpectrogramZeroPadded: return tr("4 x Oversampled - slow but clear"); } } if (name == "Background Mode") { @@ -274,6 +308,10 @@ setOmitTempsFromRecentFiles(value ? true : false); } else if (name == "Background Mode") { setBackgroundMode(BackgroundMode(value)); + } else if (name == "View Font Size") { + setViewFontSize(value); + } else if (name == "Show Splash Screen") { + setShowSplash(value ? true : false); } } @@ -403,4 +441,33 @@ } } +void +Preferences::setViewFontSize(int size) +{ + if (m_viewFontSize != size) { + m_viewFontSize = size; + + QSettings settings; + settings.beginGroup("Preferences"); + settings.setValue("view-font-size", size); + settings.endGroup(); + emit propertyChanged("View Font Size"); + } +} + +void +Preferences::setShowSplash(bool show) +{ + if (m_showSplash != show) { + + m_showSplash = show; + + QSettings settings; + settings.beginGroup("Preferences"); + settings.setValue("show-splash", show); + settings.endGroup(); + emit propertyChanged("Show Splash Screen"); + } +} +
--- a/base/Preferences.h Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Preferences.h Wed Feb 27 10:32:45 2008 +0000 @@ -54,6 +54,8 @@ }; PropertyBoxLayout getPropertyBoxLayout() const { return m_propertyBoxLayout; } + int getViewFontSize() const { return m_viewFontSize; } + bool getOmitTempsFromRecentFiles() const { return m_omitRecentTemps; } QString getTemporaryDirectoryRoot() const { return m_tempDirRoot; } @@ -67,6 +69,8 @@ }; BackgroundMode getBackgroundMode() const { return m_backgroundMode; } + bool getShowSplash() const { return m_showSplash; } + public slots: virtual void setProperty(const PropertyName &, int); @@ -79,6 +83,8 @@ void setTemporaryDirectoryRoot(QString tempDirRoot); void setResampleOnLoad(bool); void setBackgroundMode(BackgroundMode mode); + void setViewFontSize(int size); + void setShowSplash(bool); private: Preferences(); // may throw DirectoryCreationFailed @@ -94,7 +100,9 @@ bool m_omitRecentTemps; QString m_tempDirRoot; bool m_resampleOnLoad; + int m_viewFontSize; BackgroundMode m_backgroundMode; + bool m_showSplash; }; #endif
--- a/base/RangeMapper.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/RangeMapper.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -60,27 +60,52 @@ } LogRangeMapper::LogRangeMapper(int minpos, int maxpos, - float ratio, float minlog, + float minval, float maxval, QString unit) : m_minpos(minpos), m_maxpos(maxpos), - m_ratio(ratio), - m_minlog(minlog), m_unit(unit) { + convertMinMax(minpos, maxpos, minval, maxval, m_minlog, m_ratio); + + std::cerr << "LogRangeMapper: minpos " << minpos << ", maxpos " + << maxpos << ", minval " << minval << ", maxval " + << maxval << ", minlog " << m_minlog << ", ratio " << m_ratio + << ", unit " << unit.toStdString() << std::endl; + assert(m_maxpos != m_minpos); m_maxlog = (m_maxpos - m_minpos) / m_ratio + m_minlog; } +void +LogRangeMapper::convertMinMax(int minpos, int maxpos, + float minval, float maxval, + float &minlog, float &ratio) +{ + static float thresh = powf(10, -10); + if (minval < thresh) minval = thresh; + minlog = log10f(minval); + ratio = (maxpos - minpos) / (log10f(maxval) - minlog); +} + +void +LogRangeMapper::convertRatioMinLog(float ratio, float minlog, + int minpos, int maxpos, + float &minval, float &maxval) +{ + minval = powf(10, minlog); + maxval = powf(10, (maxpos - minpos) / ratio + minlog); +} + int LogRangeMapper::getPositionForValue(float value) const { int position = (log10(value) - m_minlog) * m_ratio + m_minpos; if (position < m_minpos) position = m_minpos; if (position > m_maxpos) position = m_maxpos; -// std::cerr << "LogRangeMapper::getPositionForValue: " << value << " -> " -// << position << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << std::endl; + std::cerr << "LogRangeMapper::getPositionForValue: " << value << " -> " + << position << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << std::endl; return position; } @@ -88,8 +113,8 @@ LogRangeMapper::getValueForPosition(int position) const { float value = powf(10, (position - m_minpos) / m_ratio + m_minlog); -// std::cerr << "LogRangeMapper::getValueForPosition: " << position << " -> " -// << value << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << std::endl; + std::cerr << "LogRangeMapper::getValueForPosition: " << position << " -> " + << value << " (minpos " << m_minpos << ", maxpos " << m_maxpos << ", ratio " << m_ratio << ", minlog " << m_minlog << ")" << std::endl; return value; }
--- a/base/RangeMapper.h Fri Nov 30 17:36:14 2007 +0000 +++ b/base/RangeMapper.h Wed Feb 27 10:32:45 2008 +0000 @@ -54,9 +54,17 @@ { public: LogRangeMapper(int minpos, int maxpos, - float ratio, float minlog, + float minval, float maxval, QString m_unit = ""); + static void convertRatioMinLog(float ratio, float minlog, + int minpos, int maxpos, + float &minval, float &maxval); + + static void convertMinMax(int minpos, int maxpos, + float minval, float maxval, + float &ratio, float &minlog); + virtual int getPositionForValue(float value) const; virtual float getValueForPosition(int position) const;
--- a/base/RealTime.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/RealTime.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -122,6 +122,51 @@ return s.substr(0, s.length() - 1); } +RealTime +RealTime::fromString(std::string s) +{ + bool negative = false; + bool faulty = false; + bool section = 0; + std::string ssec, snsec; + + for (size_t i = 0; i < s.length(); ++i) { + + char c = s[i]; + if (isspace(c)) continue; + + if (section == 0) { + + if (c == '-') negative = true; + else if (isdigit(c)) { section = 1; ssec += c; } + else if (c == '.') section = 2; + else break; + + } else if (section == 1) { + + if (c == '.') section = 2; + else if (isdigit(c)) ssec += c; + else break; + + } else if (section == 2) { + + if (isdigit(c)) snsec += c; + else break; + } + } + + while (snsec.length() < 8) snsec += '0'; + + int sec = atoi(ssec.c_str()); + int nsec = atoi(snsec.c_str()); + if (negative) sec = -sec; + + std::cerr << "RealTime::fromString: string " << s << " -> " + << sec << " sec, " << nsec << " nsec" << std::endl; + + return RealTime(sec, nsec); +} + std::string RealTime::toText(bool fixedDp) const { @@ -227,6 +272,22 @@ return RealTime(secdiv, int(nsecdiv + 0.5)); } +RealTime +RealTime::operator*(double m) const +{ + double t = (double(nsec) / ONE_BILLION) * m; + t += sec * m; + return fromSeconds(t); +} + +RealTime +RealTime::operator/(double d) const +{ + double t = (double(nsec) / ONE_BILLION) / d; + t += sec / d; + return fromSeconds(t); +} + double RealTime::operator/(const RealTime &r) const {
--- a/base/RealTime.h Fri Nov 30 17:36:14 2007 +0000 +++ b/base/RealTime.h Wed Feb 27 10:32:45 2008 +0000 @@ -95,30 +95,48 @@ RealTime operator*(int m) const; RealTime operator/(int d) const; - // Find the fractional difference between times - // + RealTime operator*(double m) const; + RealTime operator/(double d) const; + + /** + * Return the ratio of two 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). If align - // is true, prepend " " to the start of positive values so that - // they line up with negative ones (which start with "-"). - // + /** + * Return a human-readable debug-type string to full precision + * (probably not a format to show to a user directly). If align + * is true, prepend " " to the start of positive values so that + * they line up with negative ones (which start with "-"). + */ std::string toString(bool align = false) const; - // Return a user-readable string to the nearest millisecond - // in a form like HH:MM:SS.mmm - // + /** + * Convert a string as obtained from toString back to a RealTime + * object. + */ + static RealTime fromString(std::string); + + /** + * Return a user-readable string to the nearest millisecond, in a + * form like HH:MM:SS.mmm + */ std::string toText(bool fixedDp = false) const; - // Return a user-readable string to the nearest second in a form - // like "6s" (for less than a minute) or "2:21" (for more). - // + /** + * Return a user-readable string to the nearest second, in a form + * like "6s" (for less than a minute) or "2:21" (for more). + */ std::string toSecText() const; - // Convenience functions for handling sample frames - // + /** + * Convert a RealTime into a sample frame at the given sample rate. + */ static long realTime2Frame(const RealTime &r, unsigned int sampleRate); + + /** + * Convert a sample frame at the given sample rate into a RealTime. + */ static RealTime frame2RealTime(long frame, unsigned int sampleRate); static const RealTime zeroTime;
--- a/base/StorageAdviser.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/StorageAdviser.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -22,6 +22,8 @@ #include <iostream> +//#define DEBUG_STORAGE_ADVISER 1 + long StorageAdviser::m_discPlanned = 0; long StorageAdviser::m_memoryPlanned = 0; @@ -30,9 +32,11 @@ int minimumSize, int maximumSize) { +#ifdef DEBUG_STORAGE_ADVISER std::cerr << "StorageAdviser::recommend: Criteria " << criteria << ", minimumSize " << minimumSize << ", maximumSize " << maximumSize << std::endl; +#endif QString path = TempDirectory::getInstance()->getPath(); int discFree = GetDiscSpaceMBAvailable(path.toLocal8Bit()); @@ -51,7 +55,9 @@ memoryFree = 0; } +#ifdef DEBUG_STORAGE_ADVISER std::cerr << "Disc space: " << discFree << ", memory free: " << memoryFree << ", memory total: " << memoryTotal << ", min " << minimumSize << ", max " << maximumSize << std::endl; +#endif //!!! We have a potentially serious problem here if multiple //recommendations are made in advance of any of the resulting @@ -77,6 +83,7 @@ else if (minmb > (memoryFree / 3)) memoryStatus = Marginal; else if (memoryTotal == -1 || minmb > (memoryTotal / 10)) memoryStatus = Marginal; + else if (memoryFree < memoryTotal / 4) memoryStatus = Marginal; else memoryStatus = Sufficient; if (discFree == -1) discStatus = Unknown; @@ -85,8 +92,10 @@ else if (minmb > (discFree / 10)) discStatus = Marginal; else discStatus = Sufficient; +#ifdef DEBUG_STORAGE_ADVISER std::cerr << "Memory status: " << memoryStatus << ", disc status " << discStatus << std::endl; +#endif int recommendation = NoRecommendation;
--- a/base/Window.h Fri Nov 30 17:36:14 2007 +0000 +++ b/base/Window.h Wed Feb 27 10:32:45 2008 +0000 @@ -18,6 +18,7 @@ #include <cmath> #include <iostream> +#include <string> #include <map> enum WindowType { @@ -61,6 +62,12 @@ WindowType getType() const { return m_type; } size_t getSize() const { return m_size; } + // The names used by these functions are un-translated, for use in + // e.g. XML I/O. Use Preferences::getPropertyValueLabel if you + // want translated names for use in the user interface. + static std::string getNameForType(WindowType type); + static WindowType getTypeForName(std::string name); + protected: WindowType m_type; size_t m_size; @@ -159,4 +166,46 @@ } } +template <typename T> +std::string +Window<T>::getNameForType(WindowType type) +{ + switch (type) { + case RectangularWindow: return "rectangular"; + case BartlettWindow: return "bartlett"; + case HammingWindow: return "hamming"; + case HanningWindow: return "hanning"; + case BlackmanWindow: return "blackman"; + case GaussianWindow: return "gaussian"; + case ParzenWindow: return "parzen"; + case NuttallWindow: return "nuttall"; + case BlackmanHarrisWindow: return "blackman-harris"; + } + + std::cerr << "WARNING: Window::getNameForType: unknown type " + << type << std::endl; + + return "unknown"; +} + +template <typename T> +WindowType +Window<T>::getTypeForName(std::string name) +{ + if (name == "rectangular") return RectangularWindow; + if (name == "bartlett") return BartlettWindow; + if (name == "hamming") return HammingWindow; + if (name == "hanning") return HanningWindow; + if (name == "blackman") return BlackmanWindow; + if (name == "gaussian") return GaussianWindow; + if (name == "parzen") return ParzenWindow; + if (name == "nuttall") return NuttallWindow; + if (name == "blackman-harris") return BlackmanHarrisWindow; + + std::cerr << "WARNING: Window::getTypeForName: unknown name \"" + << name << "\", defaulting to \"hanning\"" << std::endl; + + return HanningWindow; +} + #endif
--- a/base/XmlExportable.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/base/XmlExportable.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -25,7 +25,7 @@ XmlExportable::toXmlString(QString indent, QString extraAttributes) const { - std::cerr << "XmlExportable::toXmlString" << std::endl; +// std::cerr << "XmlExportable::toXmlString" << std::endl; QString s;
--- a/data/data.pro Fri Nov 30 17:36:14 2007 +0000 +++ b/data/data.pro Wed Feb 27 10:32:45 2008 +0000 @@ -38,6 +38,7 @@ fileio/MP3FileReader.h \ fileio/OggVorbisFileReader.h \ fileio/PlaylistFileReader.h \ + fileio/ProgressPrinter.h \ fileio/QuickTimeFileReader.h \ fileio/ResamplingWavFileReader.h \ fileio/WavFileReader.h \ @@ -85,6 +86,7 @@ fileio/MP3FileReader.cpp \ fileio/OggVorbisFileReader.cpp \ fileio/PlaylistFileReader.cpp \ + fileio/ProgressPrinter.cpp \ fileio/QuickTimeFileReader.cpp \ fileio/ResamplingWavFileReader.cpp \ fileio/WavFileReader.cpp \
--- a/data/fft/FFTCache.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTCache.h Wed Feb 27 10:32:45 2008 +0000 @@ -49,6 +49,9 @@ virtual void suspend() { } + enum Type { MemoryCache, FileCache }; + virtual Type getType() = 0; + protected: FFTCache() { } };
--- a/data/fft/FFTDataServer.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTDataServer.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -453,7 +453,8 @@ #endif if (i->second.second > 0) { - std::cerr << "ERROR: FFTDataServer::modelAboutToBeDeleted: Model " << model << " (\"" << model->objectName().toStdString() << "\") is about to be deleted, but is still being referred to by FFT server " << server << " with non-zero refcount " << i->second.second << std::endl; + std::cerr << "WARNING: FFTDataServer::modelAboutToBeDeleted: Model " << model << " (\"" << model->objectName().toStdString() << "\") is about to be deleted, but is still being referred to by FFT server " << server << " with non-zero refcount " << i->second.second << std::endl; + return; } for (ServerQueue::iterator j = m_releasedServers.begin(); j != m_releasedServers.end(); ++j) { @@ -496,9 +497,10 @@ m_width(0), m_height(0), m_cacheWidth(0), - m_memoryCache(false), - m_compactCache(false), + m_cacheWidthPower(0), + m_cacheWidthMask(0), m_lastUsedCache(-1), + m_criteria(criteria), m_fftInput(0), m_exiting(false), m_suspended(true), //!!! or false? @@ -526,84 +528,38 @@ if (m_width * columnSize < maxCacheSize * 2) m_cacheWidth = m_width; else m_cacheWidth = maxCacheSize / columnSize; +#ifdef DEBUG_FFT_SERVER + std::cerr << "FFTDataServer(" << this << "): cache width nominal " + << m_cacheWidth << ", actual "; +#endif + int bits = 0; - while (m_cacheWidth) { m_cacheWidth >>= 1; ++bits; } + while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; } + m_cacheWidthPower = bits + 1; m_cacheWidth = 2; while (bits) { m_cacheWidth <<= 1; --bits; } + m_cacheWidthMask = m_cacheWidth - 1; - if (criteria == StorageAdviser::NoCriteria) { +#ifdef DEBUG_FFT_SERVER + std::cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask " + << m_cacheWidthMask << ")" << std::endl; +#endif + + if (m_criteria == StorageAdviser::NoCriteria) { // assume "spectrogram" criteria for polar ffts, and "feature // extraction" criteria for rectangular ones. if (m_polar) { - criteria = StorageAdviser::Criteria + m_criteria = StorageAdviser::Criteria (StorageAdviser::SpeedCritical | StorageAdviser::LongRetentionLikely); } else { - criteria = StorageAdviser::Criteria + m_criteria = StorageAdviser::Criteria (StorageAdviser::PrecisionCritical); } } - int cells = m_width * m_height; - int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb - int maximumSize = (cells / 1024) * sizeof(float); // kb - - // We don't have a compact rectangular representation, and compact - // of course is never precision-critical - bool canCompact = true; - if ((criteria & StorageAdviser::PrecisionCritical) || !m_polar) { - canCompact = false; - minimumSize = maximumSize; // don't use compact - } - - StorageAdviser::Recommendation recommendation; - - try { - - recommendation = - StorageAdviser::recommend(criteria, minimumSize, maximumSize); - - } catch (InsufficientDiscSpace s) { - - // Delete any unused servers we may have been leaving around - // in case we wanted them again - - purgeLimbo(0); - - // This time we don't catch InsufficientDiscSpace -- we - // haven't allocated anything yet and can safely let the - // exception out to indicate to the caller that we can't - // handle it. - - recommendation = - StorageAdviser::recommend(criteria, minimumSize, maximumSize); - } - - std::cerr << "Recommendation was: " << recommendation << std::endl; - - m_memoryCache = false; - - if ((recommendation & StorageAdviser::UseMemory) || - (recommendation & StorageAdviser::PreferMemory)) { - m_memoryCache = true; - } - - m_compactCache = canCompact && - (recommendation & StorageAdviser::ConserveSpace); - - std::cerr << "FFTDataServer: memory cache = " << m_memoryCache << ", compact cache = " << m_compactCache << std::endl; - -#ifdef DEBUG_FFT_SERVER - std::cerr << "Width " << m_width << ", cache width " << m_cacheWidth << " (size " << m_cacheWidth * columnSize << ")" << std::endl; -#endif - - StorageAdviser::notifyPlannedAllocation - (m_memoryCache ? StorageAdviser::MemoryAllocation : - StorageAdviser::DiscAllocation, - m_compactCache ? minimumSize : maximumSize); - for (size_t i = 0; i <= m_width / m_cacheWidth; ++i) { m_caches.push_back(0); } @@ -648,14 +604,9 @@ "FFTDataServer::m_writeMutex[~FFTDataServer]"); for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) { + if (*i) { delete *i; - } else { - StorageAdviser::notifyDoneAllocation - (m_memoryCache ? StorageAdviser::MemoryAllocation : - StorageAdviser::DiscAllocation, - m_cacheWidth * m_height * - (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1); } } @@ -724,6 +675,65 @@ } } +void +FFTDataServer::getStorageAdvice(size_t w, size_t h, + bool &memoryCache, bool &compactCache) +{ + int cells = w * h; + int minimumSize = (cells / 1024) * sizeof(uint16_t); // kb + int maximumSize = (cells / 1024) * sizeof(float); // kb + + // We don't have a compact rectangular representation, and compact + // of course is never precision-critical + + bool canCompact = true; + if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) { + canCompact = false; + minimumSize = maximumSize; // don't use compact + } + + StorageAdviser::Recommendation recommendation; + + try { + + recommendation = + StorageAdviser::recommend(m_criteria, minimumSize, maximumSize); + + } catch (InsufficientDiscSpace s) { + + // Delete any unused servers we may have been leaving around + // in case we wanted them again + + purgeLimbo(0); + + // This time we don't catch InsufficientDiscSpace -- we + // haven't allocated anything yet and can safely let the + // exception out to indicate to the caller that we can't + // handle it. + + recommendation = + StorageAdviser::recommend(m_criteria, minimumSize, maximumSize); + } + + std::cerr << "Recommendation was: " << recommendation << std::endl; + + memoryCache = false; + + if ((recommendation & StorageAdviser::UseMemory) || + (recommendation & StorageAdviser::PreferMemory)) { + memoryCache = true; + } + + compactCache = canCompact && + (recommendation & StorageAdviser::ConserveSpace); + +#ifdef DEBUG_FFT_SERVER + std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl; + + std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::endl; +#endif +} + FFTCache * FFTDataServer::getCacheAux(size_t c) { @@ -784,30 +794,28 @@ width = m_width - c * m_cacheWidth; } + bool memoryCache = false; + bool compactCache = false; + + getStorageAdvice(width, m_height, memoryCache, compactCache); + try { - if (m_memoryCache) { + if (memoryCache) { cache = new FFTMemoryCache - (m_compactCache ? FFTMemoryCache::Compact : -// FFTMemoryCache::Polar); - m_polar ? FFTMemoryCache::Polar : - FFTMemoryCache::Rectangular); - - } else if (m_compactCache) { - - cache = new FFTFileCache - (name, - MatrixFile::ReadWrite, - FFTFileCache::Compact); + (compactCache ? FFTMemoryCache::Compact : + m_polar ? FFTMemoryCache::Polar : + FFTMemoryCache::Rectangular); } else { cache = new FFTFileCache (name, MatrixFile::ReadWrite, - m_polar ? FFTFileCache::Polar : - FFTFileCache::Rectangular); + compactCache ? FFTFileCache::Compact : + m_polar ? FFTFileCache::Polar : + FFTFileCache::Rectangular); } cache->resize(width, m_height); @@ -818,7 +826,7 @@ delete cache; cache = 0; - if (m_memoryCache) { + if (memoryCache) { std::cerr << "WARNING: Memory allocation failed when resizing" << " FFT memory cache no. " << c << " to " << width @@ -827,7 +835,10 @@ try { - cache = new FFTFileCache(name, MatrixFile::ReadWrite, + purgeLimbo(0); + + cache = new FFTFileCache(name, + MatrixFile::ReadWrite, FFTFileCache::Compact); cache->resize(width, m_height); @@ -855,12 +866,6 @@ "There may be insufficient memory or disc space to continue.")); } - StorageAdviser::notifyDoneAllocation - (m_memoryCache ? StorageAdviser::MemoryAllocation : - StorageAdviser::DiscAllocation, - width * m_height * - (m_compactCache ? sizeof(uint16_t) : sizeof(float)) / 1024 + 1); - m_caches[c] = cache; m_lastUsedCache = c; return cache;
--- a/data/fft/FFTDataServer.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTDataServer.h Wed Feb 27 10:32:45 2008 +0000 @@ -135,8 +135,22 @@ size_t m_cacheWidth; size_t m_cacheWidthPower; size_t m_cacheWidthMask; - bool m_memoryCache; - bool m_compactCache; + + int m_lastUsedCache; + FFTCache *getCache(size_t x, size_t &col) { + col = x & m_cacheWidthMask; + int c = x >> m_cacheWidthPower; + // The only use of m_lastUsedCache without a lock is to + // establish whether a cache has been created at all (they're + // created on demand, but not destroyed until the server is). + if (c == m_lastUsedCache) return m_caches[c]; + else return getCacheAux(c); + } + bool haveCache(size_t x) { + int c = x >> m_cacheWidthPower; + if (c == m_lastUsedCache) return true; + else return (m_caches[c] != 0); + } typedef std::vector<FFTCache *> CacheVector; CacheVector m_caches; @@ -144,21 +158,9 @@ typedef std::deque<int> IntQueue; IntQueue m_dormantCaches; - int m_lastUsedCache; - FFTCache *getCache(size_t x, size_t &col) { - col = x % m_cacheWidth; - int c = x / m_cacheWidth; - // The only use of m_lastUsedCache without a lock is to - // establish whether a cache has been created at all (they're - // created on demand, but not destroyed until the server is). - if (c == m_lastUsedCache) return m_caches[c]; - else return getCacheAux(c); - } - bool haveCache(size_t x) { - int c = x / m_cacheWidth; - if (c == m_lastUsedCache) return true; - else return (m_caches[c] != 0); - } + StorageAdviser::Criteria m_criteria; + + void getStorageAdvice(size_t w, size_t h, bool &memory, bool &compact); FFTCache *getCacheAux(size_t c); QMutex m_writeMutex;
--- a/data/fft/FFTFileCache.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTFileCache.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -23,6 +23,7 @@ #include <QMutexLocker> + // The underlying matrix has height (m_height * 2 + 1). In each // column we store magnitude at [0], [2] etc and phase at [1], [3] // etc, and then store the normalization factor (maximum magnitude) at
--- a/data/fft/FFTFileCache.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTFileCache.h Wed Feb 27 10:32:45 2008 +0000 @@ -58,6 +58,8 @@ static size_t getCacheSize(size_t width, size_t height, StorageType type); + virtual Type getType() { return FileCache; } + protected: char *m_writebuf; mutable char *m_readbuf;
--- a/data/fft/FFTMemoryCache.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTMemoryCache.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -18,6 +18,8 @@ #include <iostream> +//#define DEBUG_FFT_MEMORY_CACHE 1 + FFTMemoryCache::FFTMemoryCache(StorageType storageType) : m_width(0), m_height(0), @@ -30,13 +32,17 @@ m_factor(0), m_storageType(storageType) { +#ifdef DEBUG_FFT_MEMORY_CACHE std::cerr << "FFTMemoryCache[" << this << "]::FFTMemoryCache (type " << m_storageType << ")" << std::endl; +#endif } FFTMemoryCache::~FFTMemoryCache() { -// std::cerr << "FFTMemoryCache[" << this << "]::~FFTMemoryCache" << std::endl; +#ifdef DEBUG_FFT_MEMORY_CACHE + std::cerr << "FFTMemoryCache[" << this << "]::~FFTMemoryCache" << std::endl; +#endif for (size_t i = 0; i < m_width; ++i) { if (m_magnitude && m_magnitude[i]) free(m_magnitude[i]); @@ -59,7 +65,9 @@ void FFTMemoryCache::resize(size_t width, size_t height) { -// std::cerr << "FFTMemoryCache[" << this << "]::resize(" << width << "x" << height << " = " << width*height << ")" << std::endl; +#ifdef DEBUG_FFT_MEMORY_CACHE + std::cerr << "FFTMemoryCache[" << this << "]::resize(" << width << "x" << height << " = " << width*height << ")" << std::endl; +#endif if (m_width == width && m_height == height) return; @@ -81,7 +89,9 @@ m_width = width; m_height = height; -// std::cerr << "done, width = " << m_width << " height = " << m_height << std::endl; +#ifdef DEBUG_FFT_MEMORY_CACHE + std::cerr << "done, width = " << m_width << " height = " << m_height << std::endl; +#endif } void
--- a/data/fft/FFTMemoryCache.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fft/FFTMemoryCache.h Wed Feb 27 10:32:45 2008 +0000 @@ -109,6 +109,8 @@ static size_t getCacheSize(size_t width, size_t height, StorageType type); + virtual Type getType() { return MemoryCache; } + private: size_t m_width; size_t m_height;
--- a/data/fileio/CodedAudioFileReader.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -294,7 +294,7 @@ float max = 1.0; size_t count = sz * m_channelCount; - if (m_resampler) { + if (m_resampler && m_fileRate != 0) { float ratio = float(m_sampleRate) / float(m_fileRate);
--- a/data/fileio/CodedAudioFileReader.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.h Wed Feb 27 10:32:45 2008 +0000 @@ -27,6 +27,8 @@ class CodedAudioFileReader : public AudioFileReader { + Q_OBJECT + public: virtual ~CodedAudioFileReader(); @@ -40,6 +42,9 @@ virtual size_t getNativeRate() const { return m_fileRate; } +signals: + void progress(int); + protected: CodedAudioFileReader(CacheMode cacheMode, size_t targetRate);
--- a/data/fileio/FileSource.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/FileSource.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -14,6 +14,8 @@ */ #include "FileSource.h" +#include "ProgressPrinter.h" + #include "base/TempDirectory.h" #include "base/Exceptions.h" @@ -27,7 +29,7 @@ #include <iostream> -//#define DEBUG_FILE_SOURCE 1 +#define DEBUG_FILE_SOURCE 1 int FileSource::m_count = 0; @@ -44,7 +46,7 @@ QMutex FileSource::m_mapMutex; -FileSource::FileSource(QString fileOrUrl, bool showProgress) : +FileSource::FileSource(QString fileOrUrl, ShowProgressType progressType) : m_url(fileOrUrl), m_ftp(0), m_http(0), @@ -54,6 +56,8 @@ m_remote(isRemote(fileOrUrl)), m_done(false), m_leaveLocalFile(false), + m_progressType(progressType), + m_progressPrinter(0), m_progressDialog(0), m_progressShowTimer(this), m_refCounted(false) @@ -68,7 +72,7 @@ return; } - init(showProgress); + init(); if (isRemote() && (fileOrUrl.contains('%') || @@ -99,7 +103,7 @@ m_ok = false; m_done = false; m_lastStatus = 0; - init(showProgress); + init(); } } @@ -109,7 +113,7 @@ } } -FileSource::FileSource(QUrl url, bool showProgress) : +FileSource::FileSource(QUrl url, ShowProgressType progressType) : m_url(url), m_ftp(0), m_http(0), @@ -119,6 +123,8 @@ m_remote(isRemote(url.toString())), m_done(false), m_leaveLocalFile(false), + m_progressType(progressType), + m_progressPrinter(0), m_progressDialog(0), m_progressShowTimer(this), m_refCounted(false) @@ -133,7 +139,7 @@ return; } - init(showProgress); + init(); } FileSource::FileSource(const FileSource &rf) : @@ -147,6 +153,8 @@ m_remote(rf.m_remote), m_done(false), m_leaveLocalFile(false), + m_progressType(rf.m_progressType), + m_progressPrinter(0), m_progressDialog(0), m_progressShowTimer(0), m_refCounted(false) @@ -197,20 +205,40 @@ } void -FileSource::init(bool showProgress) +FileSource::init() { if (!isRemote()) { +#ifdef DEBUG_FILE_SOURCE + std::cerr << "FileSource::init: Not a remote URL" << std::endl; +#endif + bool literal = false; m_localFilename = m_url.toLocalFile(); if (m_localFilename == "") { // QUrl may have mishandled the scheme (e.g. in a DOS path) m_localFilename = m_url.toString(); + literal = true; } +#ifdef DEBUG_FILE_SOURCE + std::cerr << "FileSource::init: URL translates to local filename \"" + << m_localFilename.toStdString() << "\"" << std::endl; +#endif m_ok = true; + m_lastStatus = 200; + if (!QFileInfo(m_localFilename).exists()) { - m_lastStatus = 404; - } else { - m_lastStatus = 200; + if (literal) { + m_lastStatus = 404; + } else { + // Again, QUrl may have been mistreating us -- + // e.g. dropping a part that looks like query data + m_localFilename = m_url.toString(); + literal = true; + if (!QFileInfo(m_localFilename).exists()) { + m_lastStatus = 404; + } + } } + m_done = true; return; } @@ -242,6 +270,7 @@ if (scheme == "http") { initHttp(); + std::cerr << "FileSource: initHttp succeeded" << std::endl; } else if (scheme == "ftp") { initFtp(); } else { @@ -275,14 +304,28 @@ m_refCountMap[m_url]++; m_refCounted = true; - if (showProgress) { - m_progressDialog = new QProgressDialog(tr("Downloading %1...").arg(m_url.toString()), tr("Cancel"), 0, 100); + switch (m_progressType) { + + case ProgressNone: break; + + case ProgressDialog: + m_progressDialog = new QProgressDialog + (tr("Downloading %1...").arg(m_url.toString()), + tr("Cancel"), 0, 100); m_progressDialog->hide(); connect(&m_progressShowTimer, SIGNAL(timeout()), this, SLOT(showProgressDialog())); - connect(m_progressDialog, SIGNAL(canceled()), this, SLOT(cancelled())); + connect(m_progressDialog, SIGNAL(canceled()), + this, SLOT(cancelled())); m_progressShowTimer.setSingleShot(true); m_progressShowTimer.start(2000); + break; + + case ProgressToConsole: + m_progressPrinter = new ProgressPrinter(tr("Downloading...")); + connect(this, SIGNAL(progress(int)), + m_progressPrinter, SLOT(progress(int))); + break; } } } @@ -400,6 +443,8 @@ } delete m_progressDialog; m_progressDialog = 0; + delete m_progressPrinter; + m_progressPrinter = 0; delete m_localFile; // does not actually delete the file m_localFile = 0; } @@ -449,6 +494,7 @@ FileSource::waitForData() { while (m_ok && !m_done) { +// std::cerr << "FileSource::waitForData: calling QApplication::processEvents" << std::endl; QApplication::processEvents(); } } @@ -802,24 +848,3 @@ return false; } -FileSourceProgressPrinter::FileSourceProgressPrinter() : - m_lastProgress(0) -{ -} - -FileSourceProgressPrinter::~FileSourceProgressPrinter() -{ - if (m_lastProgress > 0 && m_lastProgress != 100) { - std::cerr << "\r\n"; - } -} - -void -FileSourceProgressPrinter::progress(int progress) -{ - if (progress == m_lastProgress) return; - if (progress == 100) std::cerr << "\r\n"; - else std::cerr << "\r" << progress << "%"; - m_lastProgress = progress; -} -
--- a/data/fileio/FileSource.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/FileSource.h Wed Feb 27 10:32:45 2008 +0000 @@ -28,6 +28,7 @@ class QFile; class QProgressDialog; class QHttpResponseHeader; +class ProgressPrinter; /** * FileSource is a class used to refer to the contents of a file that @@ -62,19 +63,36 @@ public: + enum ShowProgressType { + ProgressNone, + ProgressDialog, + ProgressToConsole + }; + /** * Construct a FileSource using the given local file path or URL. - * The URL may be raw or encoded. If showProgress is true, a - * progress dialog will be shown for any network transfers. + * The URL may be raw or encoded. + * + * If progressType is ProgressDialog, a progress dialog will be + * shown for any network transfers; if it is ProgressToConsole, a + * progress indication will be sent to the console. + * Note that the progress() signal will also be emitted regularly + * during retrieval, even if progressType is ProgressNone. */ - FileSource(QString fileOrUrl, bool showProgress = false); + FileSource(QString fileOrUrl, + ShowProgressType progressType = ProgressNone); /** - * Construct a FileSource using the given remote URL. If - * showProgress is true, a progress dialog will be shown for any - * network transfers. + * Construct a FileSource using the given remote URL. + * + * If progressType is ProgressDialog, a progress dialog will be + * shown for any network transfers; if it is ProgressToConsole, a + * progress indication will be sent to the console. + * Note that the progress() signal also will be emitted regularly + * during retrieval, even if progressType is ProgressNone. */ - FileSource(QUrl url, bool showProgress = false); + FileSource(QUrl url, + ShowProgressType progressType = ProgressNone); FileSource(const FileSource &); @@ -212,6 +230,8 @@ bool m_remote; bool m_done; bool m_leaveLocalFile; + ShowProgressType m_progressType; + ProgressPrinter *m_progressPrinter; QProgressDialog *m_progressDialog; QTimer m_progressShowTimer; @@ -222,7 +242,7 @@ static QMutex m_mapMutex; bool m_refCounted; - void init(bool showProgress); + void init(); void initHttp(); void initFtp(); @@ -237,19 +257,4 @@ static int m_count; }; -class FileSourceProgressPrinter : public QObject -{ - Q_OBJECT - -public: - FileSourceProgressPrinter(); - virtual ~FileSourceProgressPrinter(); - -public slots: - void progress(int); - -protected: - int m_lastProgress; -}; - #endif
--- a/data/fileio/MP3FileReader.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/MP3FileReader.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -17,6 +17,8 @@ #ifdef HAVE_MAD #include "MP3FileReader.h" +#include "ProgressPrinter.h" + #include "system/System.h" #include <sys/types.h> @@ -111,6 +113,9 @@ (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), QObject::tr("Stop"), 0, 100); m_progress->hide(); + } else { + ProgressPrinter *pp = new ProgressPrinter(tr("Decoding..."), this); + connect(this, SIGNAL(progress(int)), pp, SLOT(progress(int))); } if (!decode(m_filebuffer, m_fileSize)) { @@ -130,7 +135,7 @@ m_decodeThread = new DecodeThread(this); m_decodeThread->start(); - while (m_channelCount == 0 && !m_done) { + while ((m_channelCount == 0 || m_fileRate == 0) && !m_done) { usleep(10); } } @@ -343,7 +348,7 @@ initialiseDecodeCache(); if (m_cacheMode == CacheInTemporaryFile) { - m_completion = 1; +// m_completion = 1; std::cerr << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << std::endl; startSerialised("MP3FileReader::Decode"); } @@ -353,19 +358,23 @@ double bitrate = m_bitrateNum / m_bitrateDenom; double duration = double(m_fileSize * 8) / bitrate; double elapsed = double(m_frameCount) / m_sampleRate; - double percent = ((elapsed * 100.0) / duration); - int progress = int(percent); - if (progress < 1) progress = 1; - if (progress > 99) progress = 99; - m_completion = progress; - if (m_progress) { - if (progress > m_progress->value()) { - m_progress->setValue(progress); - m_progress->show(); - m_progress->raise(); - qApp->processEvents(); - if (m_progress->wasCanceled()) { - m_cancelled = true; + double percent = 100; + if (duration > 0.0) percent = ((elapsed * 100.0) / duration); + int p = int(percent); + if (p < 1) p = 1; + if (p > 99) p = 99; + if (m_completion != p || (m_progress && !m_progress->isVisible())) { + m_completion = p; + emit progress(m_completion); + if (m_progress) { + if (m_completion > m_progress->value()) { + m_progress->setValue(m_completion); + m_progress->show(); + m_progress->raise(); + qApp->processEvents(); + if (m_progress->wasCanceled()) { + m_cancelled = true; + } } } }
--- a/data/fileio/OggVorbisFileReader.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -17,6 +17,8 @@ #ifdef HAVE_FISHSOUND #include "OggVorbisFileReader.h" +#include "ProgressPrinter.h" + #include "base/Profiler.h" #include "system/System.h" @@ -75,6 +77,9 @@ (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), QObject::tr("Stop"), 0, 100); m_progress->hide(); + } else { + ProgressPrinter *pp = new ProgressPrinter(tr("Decoding..."), this); + connect(this, SIGNAL(progress(int)), pp, SLOT(progress(int))); } while (oggz_read(m_oggz, 1024) > 0); @@ -145,14 +150,15 @@ // The number of bytes read by this function is smaller than // the file size because of the packet headers - int progress = lrint(double(reader->m_bytesRead) * 114 / - double(reader->m_fileSize)); - if (progress > 99) progress = 99; - reader->m_completion = progress; - + int p = lrint(double(reader->m_bytesRead) * 114 / + double(reader->m_fileSize)); + if (p > 99) p = 99; + reader->m_completion = p; + reader->progress(p); + if (reader->m_fileSize > 0 && reader->m_progress) { - if (progress > reader->m_progress->value()) { - reader->m_progress->setValue(progress); + if (p > reader->m_progress->value()) { + reader->m_progress->setValue(p); reader->m_progress->show(); reader->m_progress->raise(); qApp->processEvents();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/ProgressPrinter.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -0,0 +1,48 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + 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 "ProgressPrinter.h" + +#include <iostream> + +ProgressPrinter::ProgressPrinter(QString prefix, QObject *parent) : + QObject(parent), + m_prefix(prefix), + m_lastProgress(0) +{ +} + +ProgressPrinter::~ProgressPrinter() +{ + if (m_lastProgress > 0 && m_lastProgress != 100) { + std::cerr << "\r\n"; + } + std::cerr << "(progress printer dtor)" << std::endl; +} + +void +ProgressPrinter::progress(int progress) +{ + if (progress == m_lastProgress) return; + if (progress == 100) std::cerr << "\r\n"; + else { + std::cerr << "\r" + << m_prefix.toStdString() + << (m_prefix == "" ? "" : " ") + << progress << "%"; + } + m_lastProgress = progress; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/ProgressPrinter.h Wed Feb 27 10:32:45 2008 +0000 @@ -0,0 +1,38 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + 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. +*/ + +#ifndef _PROGRESS_PRINTER_H_ +#define _PROGRESS_PRINTER_H_ + +#include <QObject> +#include <QString> + +class ProgressPrinter : public QObject +{ + Q_OBJECT + +public: + ProgressPrinter(QString prefix = "", QObject *parent = 0); + virtual ~ProgressPrinter(); + +public slots: + void progress(int); + +protected: + QString m_prefix; + int m_lastProgress; +}; + +#endif
--- a/data/fileio/QuickTimeFileReader.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/QuickTimeFileReader.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -236,6 +236,8 @@ break; } + //!!! progress? + // std::cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << std::endl; // QuickTime buffers are interleaved unless specified otherwise
--- a/data/fileio/WavFileReader.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/fileio/WavFileReader.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -111,6 +111,7 @@ { if (count == 0) return; results.clear(); + results.reserve(count * m_fileInfo.channels); QMutexLocker locker(&m_mutex);
--- a/data/model/AggregateWaveModel.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/AggregateWaveModel.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -175,6 +175,28 @@ if (mixing) delete[] readbuf; return sz; } + +size_t +AggregateWaveModel::getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffer) const +{ + size_t min = count; + + for (size_t c = fromchannel; c <= tochannel; ++c) { + size_t here = getData(c, start, count, buffer[c - fromchannel]); + if (here < min) min = here; + } + + return min; +} + +size_t +AggregateWaveModel::getSummaryBlockSize(size_t desired) const +{ + //!!! complete + return desired; +} void AggregateWaveModel::getSummaries(size_t channel, size_t start, size_t count,
--- a/data/model/AggregateWaveModel.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/AggregateWaveModel.h Wed Feb 27 10:32:45 2008 +0000 @@ -67,6 +67,12 @@ virtual size_t getData(int channel, size_t start, size_t count, double *buffer) const; + virtual size_t getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffer) const; + + virtual size_t getSummaryBlockSize(size_t desired) const; + virtual void getSummaries(size_t channel, size_t start, size_t count, RangeBlock &ranges, size_t &blockSize) const;
--- a/data/model/AlignmentModel.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/AlignmentModel.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -17,6 +17,8 @@ #include "SparseTimeValueModel.h" +//#define DEBUG_ALIGNMENT_MODEL 1 + AlignmentModel::AlignmentModel(Model *reference, Model *aligned, Model *inputModel, @@ -30,14 +32,17 @@ m_pathBegun(false), m_pathComplete(false) { - connect(m_rawPath, SIGNAL(modelChanged()), - this, SLOT(pathChanged())); + if (m_rawPath) { - connect(m_rawPath, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(pathChanged(size_t, size_t))); + connect(m_rawPath, SIGNAL(modelChanged()), + this, SLOT(pathChanged())); - connect(m_rawPath, SIGNAL(completionChanged()), - this, SLOT(pathCompletionChanged())); + connect(m_rawPath, SIGNAL(modelChanged(size_t, size_t)), + this, SLOT(pathChanged(size_t, size_t))); + + connect(m_rawPath, SIGNAL(completionChanged()), + this, SLOT(pathCompletionChanged())); + } constructPath(); constructReversePath(); @@ -124,16 +129,26 @@ size_t AlignmentModel::toReference(size_t frame) const { -// std::cerr << "AlignmentModel::toReference(" << frame << ")" << std::endl; - if (!m_path) constructPath(); +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::toReference(" << frame << ")" << std::endl; +#endif + if (!m_path) { + if (!m_rawPath) return frame; + constructPath(); + } return align(m_path, frame); } size_t AlignmentModel::fromReference(size_t frame) const { -// std::cerr << "AlignmentModel::fromReference(" << frame << ")" << std::endl; - if (!m_reversePath) constructReversePath(); +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::fromReference(" << frame << ")" << std::endl; +#endif + if (!m_reversePath) { + if (!m_rawPath) return frame; + constructReversePath(); + } return align(m_reversePath, frame); } @@ -166,8 +181,10 @@ int completion = 0; m_rawPath->isReady(&completion); -// std::cerr << "AlignmentModel::pathCompletionChanged: completion = " -// << completion << std::endl; +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::pathCompletionChanged: completion = " + << completion << std::endl; +#endif m_pathComplete = (completion == 100); @@ -211,7 +228,9 @@ m_path->addPoint(PathPoint(frame, rframe)); } -// std::cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << std::endl; +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::constructPath: " << m_path->getPointCount() << " points, at least " << (2 * m_path->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << std::endl; +#endif } void @@ -241,7 +260,9 @@ m_reversePath->addPoint(PathPoint(rframe, frame)); } -// std::cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << std::endl; +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(PathPoint))) << " bytes" << std::endl; +#endif } size_t @@ -257,13 +278,24 @@ const PathModel::PointList &points = path->getPoints(); if (points.empty()) { -// std::cerr << "AlignmentModel::align: No points" << std::endl; +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::align: No points" << std::endl; +#endif return frame; } +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::align: frame " << frame << " requested" << std::endl; +#endif + PathModel::Point point(frame); PathModel::PointList::const_iterator i = points.lower_bound(point); - if (i == points.end()) --i; + if (i == points.end()) { +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "Note: i == points.end()" << std::endl; +#endif + --i; + } while (i != points.begin() && i->frame > long(frame)) --i; long foundFrame = i->frame; @@ -273,9 +305,16 @@ long followingMapFrame = foundMapFrame; if (++i != points.end()) { +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "another point available" << std::endl; +#endif followingFrame = i->frame; followingMapFrame = i->mapframe; - } + } else { +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "no other point available" << std::endl; +#endif + } if (foundMapFrame < 0) return 0; @@ -288,7 +327,9 @@ resultFrame += lrintf((followingMapFrame - foundMapFrame) * interp); } -// std::cerr << "AlignmentModel::align: resultFrame = " << resultFrame << std::endl; +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::align: resultFrame = " << resultFrame << std::endl; +#endif return resultFrame; }
--- a/data/model/DenseTimeValueModel.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/DenseTimeValueModel.h Wed Feb 27 10:32:45 2008 +0000 @@ -71,6 +71,15 @@ virtual size_t getData(int channel, size_t start, size_t count, double *buffer) const = 0; + /** + * Get the specified set of samples from given contiguous range + * of channels of the model in single-precision floating-point + * format. Return the number of sample frames actually retrieved. + */ + virtual size_t getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffers) const = 0; + QString getTypeName() const { return tr("Dense Time-Value"); } };
--- a/data/model/FFTModel.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/FFTModel.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -82,6 +82,19 @@ if (m_server) FFTDataServer::releaseInstance(m_server); } +void +FFTModel::sourceModelAboutToBeDeleted() +{ + if (m_sourceModel) { + std::cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_sourceModel << ")" << std::endl; + if (m_server) { + FFTDataServer::releaseInstance(m_server); + m_server = 0; + } + FFTDataServer::modelAboutToBeDeleted(m_sourceModel); + } +} + FFTDataServer * FFTModel::getServer(const DenseTimeValueModel *model, int channel,
--- a/data/model/FFTModel.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/FFTModel.h Wed Feb 27 10:32:45 2008 +0000 @@ -167,6 +167,9 @@ QString getTypeName() const { return tr("FFT"); } +public slots: + void sourceModelAboutToBeDeleted(); + private: FFTModel(const FFTModel &); // not implemented FFTModel &operator=(const FFTModel &); // not implemented
--- a/data/model/Labeller.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/Labeller.h Wed Feb 27 10:32:45 2008 +0000 @@ -36,8 +36,10 @@ ValueFromTwoLevelCounter, ValueFromFrameNumber, ValueFromRealTime, - ValueFromRealTimeDifference, - ValueFromTempo, + ValueFromDurationFromPrevious, + ValueFromDurationToNext, + ValueFromTempoFromPrevious, + ValueFromTempoToNext, ValueFromExistingNeighbour, ValueFromLabel }; @@ -59,7 +61,7 @@ // 4. re-label a set of points that have already been added to a // model - Labeller(ValueType type) : + Labeller(ValueType type = ValueNone) : m_type(type), m_counter(1), m_counter2(1), @@ -70,8 +72,8 @@ Labeller(const Labeller &l) : QObject(), m_type(l.m_type), - m_counter(1), - m_counter2(1), + m_counter(l.m_counter), + m_counter2(l.m_counter2), m_cycle(l.m_cycle), m_dp(l.m_dp), m_rate(l.m_rate) { } @@ -81,16 +83,30 @@ typedef std::map<ValueType, QString> TypeNameMap; TypeNameMap getTypeNames() const { TypeNameMap m; - m[ValueNone] = tr("No numbering"); - m[ValueFromSimpleCounter] = tr("Simple counter"); - m[ValueFromCyclicalCounter] = tr("Cyclical counter"); - m[ValueFromTwoLevelCounter] = tr("Cyclical two-level counter (bar/beat)"); - m[ValueFromFrameNumber] = tr("Audio sample frame number"); - m[ValueFromRealTime] = tr("Time in seconds"); - m[ValueFromRealTimeDifference] = tr("Duration to the following item"); - m[ValueFromTempo] = tr("Tempo (bpm) based on duration to following item"); - m[ValueFromExistingNeighbour] = tr("Same as the nearest previous item"); - m[ValueFromLabel] = tr("Value extracted from the item's label (where possible)"); + m[ValueNone] + = tr("No numbering"); + m[ValueFromSimpleCounter] + = tr("Simple counter"); + m[ValueFromCyclicalCounter] + = tr("Cyclical counter"); + m[ValueFromTwoLevelCounter] + = tr("Cyclical two-level counter (bar/beat)"); + m[ValueFromFrameNumber] + = tr("Audio sample frame number"); + m[ValueFromRealTime] + = tr("Time in seconds"); + m[ValueFromDurationToNext] + = tr("Duration to the following item"); + m[ValueFromTempoToNext] + = tr("Tempo (bpm) based on duration to following item"); + m[ValueFromDurationFromPrevious] + = tr("Duration since the previous item"); + m[ValueFromTempoFromPrevious] + = tr("Tempo (bpm) based on duration since previous item"); + m[ValueFromExistingNeighbour] + = tr("Same as the nearest previous item"); + m[ValueFromLabel] + = tr("Value extracted from the item's label (where possible)"); return m; } @@ -212,9 +228,16 @@ } } + bool requiresPrevPoint() const { + return (m_type == ValueFromDurationFromPrevious || + m_type == ValueFromDurationToNext || + m_type == ValueFromTempoFromPrevious || + m_type == ValueFromDurationToNext); + } + bool actingOnPrevPoint() const { - return (m_type == ValueFromRealTimeDifference || - m_type == ValueFromTempo); + return (m_type == ValueFromDurationToNext || + m_type == ValueFromTempoToNext); } protected: @@ -252,15 +275,18 @@ } break; - case ValueFromRealTimeDifference: - case ValueFromTempo: + case ValueFromDurationToNext: + case ValueFromTempoToNext: + case ValueFromDurationFromPrevious: + case ValueFromTempoFromPrevious: if (m_rate == 0.f) { std::cerr << "ERROR: Labeller::getValueFor: Real-time conversion required, but no sample rate set" << std::endl; } else if (!prevPoint) { std::cerr << "ERROR: Labeller::getValueFor: Time difference required, but only one point provided" << std::endl; } else { size_t f0 = prevPoint->frame, f1 = newPoint.frame; - if (m_type == ValueFromRealTimeDifference) { + if (m_type == ValueFromDurationToNext || + m_type == ValueFromDurationFromPrevious) { value = float(f1 - f0) / m_rate; } else { if (f1 > f0) {
--- a/data/model/Model.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/Model.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -25,7 +25,7 @@ Model::~Model() { -// std::cerr << "Model::~Model(" << this << ")" << std::endl; + std::cerr << "Model::~Model(" << this << ")" << std::endl; if (!m_aboutToDelete) { std::cerr << "NOTE: Model::~Model(" << this << ", \"" @@ -66,6 +66,8 @@ void Model::aboutToDelete() { + std::cerr << "Model(" << this << ")::aboutToDelete()" << std::endl; + if (m_aboutToDelete) { std::cerr << "WARNING: Model(" << this << ", \"" << objectName().toStdString() << "\")::aboutToDelete: " @@ -100,7 +102,7 @@ { if (!m_alignment) { if (m_sourceModel) return m_sourceModel->getAlignmentReference(); - return this; + return 0; } return m_alignment->getReferenceModel(); }
--- a/data/model/PowerOfSqrtTwoZoomConstraint.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -75,7 +75,13 @@ } // std::cerr << "Testing base " << base << std::endl; - if (base >= blockSize) { + + if (base == blockSize) { + result = base; + break; + } + + if (base > blockSize) { if (dir == RoundNearest) { if (base - blockSize < blockSize - prevBase) { dir = RoundUp;
--- a/data/model/RangeSummarisableTimeValueModel.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/RangeSummarisableTimeValueModel.h Wed Feb 27 10:32:45 2008 +0000 @@ -72,6 +72,8 @@ */ virtual Range getSummary(size_t channel, size_t start, size_t count) const = 0; + virtual size_t getSummaryBlockSize(size_t desired) const = 0; + QString getTypeName() const { return tr("Range-Summarisable Time-Value"); } };
--- a/data/model/WaveFileModel.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/WaveFileModel.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -189,7 +189,7 @@ // This is used for e.g. audio playback. // Could be much more efficient (although compiler optimisation will help) - if (start > m_startFrame) { + if (start >= m_startFrame) { start -= m_startFrame; } else { for (size_t i = 0; i < count; ++i) buffer[i] = 0.f; @@ -211,12 +211,14 @@ // << start << ", " << end << "): calling reader" << std::endl; #endif - SampleBlock frames; + int channels = getChannelCount(); + + SampleBlock frames(count * channels); m_reader->getInterleavedFrames(start, count, frames); size_t i = 0; - int ch0 = channel, ch1 = channel, channels = getChannelCount(); + int ch0 = channel, ch1 = channel; if (channel == -1) { ch0 = 0; ch1 = channels - 1; @@ -262,12 +264,14 @@ return 0; } - SampleBlock frames; + int channels = getChannelCount(); + + SampleBlock frames(count * channels); m_reader->getInterleavedFrames(start, count, frames); size_t i = 0; - int ch0 = channel, ch1 = channel, channels = getChannelCount(); + int ch0 = channel, ch1 = channel; if (channel == -1) { ch0 = 0; ch1 = channels - 1; @@ -292,12 +296,112 @@ return i; } +size_t +WaveFileModel::getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffer) const +{ + size_t channels = getChannelCount(); + + if (fromchannel > tochannel) { + std::cerr << "ERROR: WaveFileModel::getData: fromchannel (" + << fromchannel << ") > tochannel (" << tochannel << ")" + << std::endl; + return 0; + } + + if (tochannel >= channels) { + std::cerr << "ERROR: WaveFileModel::getData: tochannel (" + << tochannel << ") >= channel count (" << channels << ")" + << std::endl; + return 0; + } + + if (fromchannel == tochannel) { + return getData(fromchannel, start, count, buffer[0]); + } + + size_t reqchannels = (tochannel - fromchannel) + 1; + + // Always read these directly from the file. + // This is used for e.g. audio playback. + // Could be much more efficient (although compiler optimisation will help) + + if (start >= m_startFrame) { + start -= m_startFrame; + } else { + for (size_t c = 0; c < reqchannels; ++c) { + for (size_t i = 0; i < count; ++i) buffer[c][i] = 0.f; + } + if (count <= m_startFrame - start) { + return 0; + } else { + count -= (m_startFrame - start); + start = 0; + } + } + + if (!m_reader || !m_reader->isOK() || count == 0) { + for (size_t c = 0; c < reqchannels; ++c) { + for (size_t i = 0; i < count; ++i) buffer[c][i] = 0.f; + } + return 0; + } + + SampleBlock frames(count * channels); + m_reader->getInterleavedFrames(start, count, frames); + + size_t i = 0; + + int ch0 = fromchannel, ch1 = tochannel; + + size_t index = 0, available = frames.size(); + + while (i < count) { + + if (index >= available) break; + + size_t destc = 0; + + for (size_t c = 0; c < channels; ++c) { + + if (c >= fromchannel && c <= tochannel) { + buffer[destc][i] = frames[index]; + ++destc; + } + + ++index; + } + + ++i; + } + + return i; +} + +size_t +WaveFileModel::getSummaryBlockSize(size_t desired) const +{ + int cacheType = 0; + int power = m_zoomConstraint.getMinCachePower(); + size_t roundedBlockSize = m_zoomConstraint.getNearestBlockSize + (desired, cacheType, power, ZoomConstraint::RoundDown); + if (cacheType != 0 && cacheType != 1) { + // We will be reading directly from file, so can satisfy any + // blocksize requirement + return desired; + } else { + return roundedBlockSize; + } +} + void WaveFileModel::getSummaries(size_t channel, size_t start, size_t count, RangeBlock &ranges, size_t &blockSize) const { ranges.clear(); if (!isOK()) return; + ranges.reserve((count / blockSize) + 1); if (start > m_startFrame) start -= m_startFrame; else if (count <= m_startFrame - start) return; @@ -308,8 +412,8 @@ int cacheType = 0; int power = m_zoomConstraint.getMinCachePower(); - blockSize = m_zoomConstraint.getNearestBlockSize - (blockSize, cacheType, power, ZoomConstraint::RoundUp); + size_t roundedBlockSize = m_zoomConstraint.getNearestBlockSize + (blockSize, cacheType, power, ZoomConstraint::RoundDown); size_t channels = getChannelCount(); @@ -323,17 +427,26 @@ // matter by putting a single cache in getInterleavedFrames // for short queries. - SampleBlock frames; - m_reader->getInterleavedFrames(start, count, frames); + m_directReadMutex.lock(); + + if (m_lastDirectReadStart != start || + m_lastDirectReadCount != count || + m_directRead.empty()) { + + m_reader->getInterleavedFrames(start, count, m_directRead); + m_lastDirectReadStart = start; + m_lastDirectReadCount = count; + } + float max = 0.0, min = 0.0, total = 0.0; size_t i = 0, got = 0; while (i < count) { size_t index = i * channels + channel; - if (index >= frames.size()) break; + if (index >= m_directRead.size()) break; - float sample = frames[index]; + float sample = m_directRead[index]; if (sample > max || got == 0) max = sample; if (sample < min || got == 0) min = sample; total += fabsf(sample); @@ -348,6 +461,8 @@ } } + m_directReadMutex.unlock(); + if (got > 0) { ranges.push_back(Range(min, max, total / got)); } @@ -360,6 +475,8 @@ const RangeBlock &cache = m_cache[cacheType]; + blockSize = roundedBlockSize; + size_t cacheBlock, div; if (cacheType == 0) {
--- a/data/model/WaveFileModel.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/WaveFileModel.h Wed Feb 27 10:32:45 2008 +0000 @@ -68,6 +68,12 @@ virtual size_t getData(int channel, size_t start, size_t count, double *buffer) const; + virtual size_t getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffers) const; + + virtual size_t getSummaryBlockSize(size_t desired) const; + virtual void getSummaries(size_t channel, size_t start, size_t count, RangeBlock &ranges, size_t &blockSize) const; @@ -124,6 +130,11 @@ size_t m_lastFillExtent; bool m_exiting; static PowerOfSqrtTwoZoomConstraint m_zoomConstraint; + + mutable SampleBlock m_directRead; + mutable size_t m_lastDirectReadStart; + mutable size_t m_lastDirectReadCount; + mutable QMutex m_directReadMutex; }; #endif
--- a/data/model/WritableWaveFileModel.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/WritableWaveFileModel.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -185,11 +185,25 @@ double *buffer) const { if (!m_model || m_model->getChannelCount() == 0) return 0; -// std::cerr << "WritableWaveFileModel::getValues(" << channel << ", " -// << start << ", " << end << "): calling model" << std::endl; return m_model->getData(channel, start, count, buffer); } +size_t +WritableWaveFileModel::getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffers) const +{ + if (!m_model || m_model->getChannelCount() == 0) return 0; + return m_model->getData(fromchannel, tochannel, start, count, buffers); +} + +size_t +WritableWaveFileModel::getSummaryBlockSize(size_t desired) const +{ + if (!m_model) return desired; + return m_model->getSummaryBlockSize(desired); +} + void WritableWaveFileModel::getSummaries(size_t channel, size_t start, size_t count, RangeBlock &ranges,
--- a/data/model/WritableWaveFileModel.h Fri Nov 30 17:36:14 2007 +0000 +++ b/data/model/WritableWaveFileModel.h Wed Feb 27 10:32:45 2008 +0000 @@ -68,6 +68,12 @@ virtual size_t getData(int channel, size_t start, size_t count, double *buffer) const; + virtual size_t getData(size_t fromchannel, size_t tochannel, + size_t start, size_t count, + float **buffer) const; + + virtual size_t getSummaryBlockSize(size_t desired) const; + virtual void getSummaries(size_t channel, size_t start, size_t count, RangeBlock &ranges, size_t &blockSize) const;
--- a/plugin/DSSIPluginInstance.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/DSSIPluginInstance.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -831,6 +831,20 @@ } } +int +DSSIPluginInstance::getParameterDisplayHint(unsigned int parameter) const +{ + if (parameter >= m_controlPortsIn.size()) return 0.0; + + LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory); + if (f) { + return f->getPortDisplayHint(m_descriptor->LADSPA_Plugin, + m_controlPortsIn[parameter].first); + } else { + return PortHint::NoHint; + } +} + std::string DSSIPluginInstance::configure(std::string key, std::string value)
--- a/plugin/DSSIPluginInstance.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/DSSIPluginInstance.h Wed Feb 27 10:32:45 2008 +0000 @@ -60,6 +60,7 @@ virtual void setParameterValue(unsigned int parameter, float value); virtual float getParameterValue(unsigned int parameter) const; virtual float getParameterDefault(unsigned int parameter) const; + virtual int getParameterDisplayHint(unsigned int parameter) const; virtual ParameterList getParameterDescriptors() const; virtual float getParameter(std::string) const;
--- a/plugin/FeatureExtractionPluginFactory.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/FeatureExtractionPluginFactory.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -95,6 +95,7 @@ if (factory) { std::vector<QString> tmp = factory->getPluginIdentifiers(); for (size_t i = 0; i < tmp.size(); ++i) { +// std::cerr << "identifier: " << tmp[i].toStdString() << std::endl; rv.push_back(tmp[i]); } }
--- a/plugin/LADSPAPluginFactory.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/LADSPAPluginFactory.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -161,6 +161,10 @@ minimum *= m_sampleRate; } + if (LADSPA_IS_HINT_LOGARITHMIC(d)) { + if (minimum == 0.f) minimum = 1.f; + } + return minimum; } @@ -211,6 +215,17 @@ bool logarithmic = LADSPA_IS_HINT_LOGARITHMIC(d); + float logmin = 0, logmax = 0; + if (logarithmic) { + float thresh = powf(10, -10); + if (minimum < thresh) logmin = -10; + else logmin = log10f(minimum); + if (maximum < thresh) logmax = -10; + else logmax = log10f(maximum); + } + +// std::cerr << "LADSPAPluginFactory::getPortDefault: hint = " << d << std::endl; + if (!LADSPA_IS_HINT_HAS_DEFAULT(d)) { deft = minimum; @@ -222,8 +237,7 @@ } else if (LADSPA_IS_HINT_DEFAULT_LOW(d)) { if (logarithmic) { - deft = powf(10, log10(minimum) * 0.75 + - log10(maximum) * 0.25); + deft = powf(10, logmin * 0.75 + logmax * 0.25); } else { deft = minimum * 0.75 + maximum * 0.25; } @@ -231,8 +245,7 @@ } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(d)) { if (logarithmic) { - deft = powf(10, log10(minimum) * 0.5 + - log10(maximum) * 0.5); + deft = powf(10, logmin * 0.5 + logmax * 0.5); } else { deft = minimum * 0.5 + maximum * 0.5; } @@ -240,8 +253,7 @@ } else if (LADSPA_IS_HINT_DEFAULT_HIGH(d)) { if (logarithmic) { - deft = powf(10, log10(minimum) * 0.25 + - log10(maximum) * 0.75); + deft = powf(10, logmin * 0.25 + logmax * 0.75); } else { deft = minimum * 0.25 + maximum * 0.75; } @@ -271,10 +283,14 @@ deft = minimum; } + +//!!! No -- the min and max have already been multiplied by the rate, +//so it would happen twice if we did it here -- and e.g. DEFAULT_440 +//doesn't want to be multiplied by the rate either - if (LADSPA_IS_HINT_SAMPLE_RATE(d)) { - deft *= m_sampleRate; - } +// if (LADSPA_IS_HINT_SAMPLE_RATE(d)) { +// deft *= m_sampleRate; +// } return deft; }
--- a/plugin/LADSPAPluginInstance.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/LADSPAPluginInstance.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -515,6 +515,19 @@ } } +int +LADSPAPluginInstance::getParameterDisplayHint(unsigned int parameter) const +{ + if (parameter >= m_controlPortsIn.size()) return 0.0; + + LADSPAPluginFactory *f = dynamic_cast<LADSPAPluginFactory *>(m_factory); + if (f) { + return f->getPortDisplayHint(m_descriptor, m_controlPortsIn[parameter].first); + } else { + return PortHint::NoHint; + } +} + void LADSPAPluginInstance::run(const Vamp::RealTime &) {
--- a/plugin/LADSPAPluginInstance.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/LADSPAPluginInstance.h Wed Feb 27 10:32:45 2008 +0000 @@ -56,6 +56,7 @@ virtual void setParameterValue(unsigned int parameter, float value); virtual float getParameterValue(unsigned int parameter) const; virtual float getParameterDefault(unsigned int parameter) const; + virtual int getParameterDisplayHint(unsigned int parameter) const; virtual ParameterList getParameterDescriptors() const; virtual float getParameter(std::string) const;
--- a/plugin/RealTimePluginInstance.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/RealTimePluginInstance.h Wed Feb 27 10:32:45 2008 +0000 @@ -112,6 +112,7 @@ 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 int getParameterDisplayHint(unsigned int parameter) const = 0; virtual std::string configure(std::string /* key */, std::string /* value */) { return std::string(); }
--- a/plugin/plugin.pro Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/plugin.pro Wed Feb 27 10:32:45 2008 +0000 @@ -36,7 +36,6 @@ api/alsa/seq_midi_event.h \ api/alsa/sound/asequencer.h \ transform/FeatureExtractionModelTransformer.h \ - transform/PluginTransformer.h \ transform/RealTimeEffectModelTransformer.h \ transform/Transform.h \ transform/TransformDescription.h \ @@ -55,7 +54,6 @@ api/dssi_alsa_compat.c \ plugins/SamplePlayer.cpp \ transform/FeatureExtractionModelTransformer.cpp \ - transform/PluginTransformer.cpp \ transform/RealTimeEffectModelTransformer.cpp \ transform/Transform.cpp \ transform/TransformFactory.cpp \
--- a/plugin/transform/FeatureExtractionModelTransformer.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/FeatureExtractionModelTransformer.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -29,83 +29,130 @@ #include "data/model/FFTModel.h" #include "data/model/WaveFileModel.h" +#include "TransformFactory.h" + #include <QMessageBox> #include <iostream> -FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString outputName) : - PluginTransformer(inputModel, context), +FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in, + const Transform &transform) : + ModelTransformer(in, transform), m_plugin(0), m_descriptor(0), m_outputFeatureNo(0) { -// std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl; +// std::cerr << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << pluginId.toStdString() << ", outputName " << m_transform.getOutput().toStdString() << std::endl; + + QString pluginId = transform.getPluginIdentifier(); FeatureExtractionPluginFactory *factory = FeatureExtractionPluginFactory::instanceFor(pluginId); if (!factory) { - std::cerr << "FeatureExtractionModelTransformer: No factory available for plugin id \"" - << pluginId.toStdString() << "\"" << std::endl; + m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId); return; } - m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate()); + DenseTimeValueModel *input = getConformingInput(); + if (!input) { + m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId); + return; + } + m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate()); if (!m_plugin) { - std::cerr << "FeatureExtractionModelTransformer: Failed to instantiate plugin \"" - << pluginId.toStdString() << "\"" << std::endl; + m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId); return; } - m_context.makeConsistentWithPlugin(m_plugin); + TransformFactory::getInstance()->makeContextConsistentWithPlugin + (m_transform, m_plugin); - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } - - DenseTimeValueModel *input = getInput(); - if (!input) return; + TransformFactory::getInstance()->setPluginParameters + (m_transform, m_plugin); size_t channelCount = input->getChannelCount(); if (m_plugin->getMaxChannelCount() < channelCount) { channelCount = 1; } if (m_plugin->getMinChannelCount() > channelCount) { - std::cerr << "FeatureExtractionModelTransformer:: " - << "Can't provide enough channels to plugin (plugin min " - << m_plugin->getMinChannelCount() << ", max " - << m_plugin->getMaxChannelCount() << ", input model has " - << input->getChannelCount() << ")" << std::endl; + m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)") + .arg(pluginId) + .arg(m_plugin->getMinChannelCount()) + .arg(m_plugin->getMaxChannelCount()) + .arg(input->getChannelCount()); return; } std::cerr << "Initialising feature extraction plugin with channels = " - << channelCount << ", step = " << m_context.stepSize - << ", block = " << m_context.blockSize << std::endl; + << channelCount << ", step = " << m_transform.getStepSize() + << ", block = " << m_transform.getBlockSize() << std::endl; if (!m_plugin->initialise(channelCount, - m_context.stepSize, - m_context.blockSize)) { - std::cerr << "FeatureExtractionModelTransformer: Plugin " - << m_plugin->getIdentifier() << " failed to initialise!" << std::endl; - return; + m_transform.getStepSize(), + m_transform.getBlockSize())) { + + size_t pstep = m_transform.getStepSize(); + size_t pblock = m_transform.getBlockSize(); + + m_transform.setStepSize(0); + m_transform.setBlockSize(0); + TransformFactory::getInstance()->makeContextConsistentWithPlugin + (m_transform, m_plugin); + + if (m_transform.getStepSize() != pstep || + m_transform.getBlockSize() != pblock) { + + if (!m_plugin->initialise(channelCount, + m_transform.getStepSize(), + m_transform.getBlockSize())) { + + m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); + return; + + } else { + + m_message = tr("Feature extraction plugin \"%1\" rejected the given step and block sizes (%2 and %3); using plugin defaults (%4 and %5) instead") + .arg(pluginId) + .arg(pstep) + .arg(pblock) + .arg(m_transform.getStepSize()) + .arg(m_transform.getBlockSize()); + } + + } else { + + m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); + return; + } + } + + if (m_transform.getPluginVersion() != "") { + QString pv = QString("%1").arg(m_plugin->getPluginVersion()); + if (pv != m_transform.getPluginVersion()) { + QString vm = tr("Transform was configured for version %1 of plugin \"%2\", but the plugin being used is version %3") + .arg(m_transform.getPluginVersion()) + .arg(pluginId) + .arg(pv); + if (m_message != "") { + m_message = QString("%1; %2").arg(vm).arg(m_message); + } else { + m_message = vm; + } + } } Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors(); if (outputs.empty()) { - std::cerr << "FeatureExtractionModelTransformer: Plugin \"" - << pluginId.toStdString() << "\" has no outputs" << std::endl; + m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId); return; } for (size_t i = 0; i < outputs.size(); ++i) { - if (outputName == "" || outputs[i].identifier == outputName.toStdString()) { + if (m_transform.getOutput() == "" || + outputs[i].identifier == m_transform.getOutput().toStdString()) { m_outputFeatureNo = i; m_descriptor = new Vamp::Plugin::OutputDescriptor (outputs[i]); @@ -114,9 +161,9 @@ } if (!m_descriptor) { - std::cerr << "FeatureExtractionModelTransformer: Plugin \"" - << pluginId.toStdString() << "\" has no output named \"" - << outputName.toStdString() << "\"" << std::endl; + m_message = tr("Plugin \"%1\" has no output named \"%2\"") + .arg(pluginId) + .arg(m_transform.getOutput()); return; } @@ -140,7 +187,7 @@ haveExtents = true; } - size_t modelRate = m_input->getSampleRate(); + size_t modelRate = input->getSampleRate(); size_t modelResolution = 1; switch (m_descriptor->sampleType) { @@ -152,7 +199,7 @@ break; case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: - modelResolution = m_context.stepSize; + modelResolution = m_transform.getStepSize(); break; case Vamp::Plugin::OutputDescriptor::FixedSampleRate: @@ -219,7 +266,7 @@ m_output = model; } - if (m_output) m_output->setSourceModel(m_input); + if (m_output) m_output->setSourceModel(input); } FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() @@ -230,12 +277,12 @@ } DenseTimeValueModel * -FeatureExtractionModelTransformer::getInput() +FeatureExtractionModelTransformer::getConformingInput() { DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(getInputModel()); if (!dtvm) { - std::cerr << "FeatureExtractionModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; + std::cerr << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; } return dtvm; } @@ -243,7 +290,7 @@ void FeatureExtractionModelTransformer::run() { - DenseTimeValueModel *input = getInput(); + DenseTimeValueModel *input = getConformingInput(); if (!input) return; if (!m_output) return; @@ -260,7 +307,7 @@ sleep(1); } - size_t sampleRate = m_input->getSampleRate(); + size_t sampleRate = input->getSampleRate(); size_t channelCount = input->getChannelCount(); if (m_plugin->getMaxChannelCount() < channelCount) { @@ -269,9 +316,12 @@ float **buffers = new float*[channelCount]; for (size_t ch = 0; ch < channelCount; ++ch) { - buffers[ch] = new float[m_context.blockSize + 2]; + buffers[ch] = new float[m_transform.getBlockSize() + 2]; } + size_t stepSize = m_transform.getStepSize(); + size_t blockSize = m_transform.getBlockSize(); + bool frequencyDomain = (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain); std::vector<FFTModel *> fftModels; @@ -279,12 +329,12 @@ if (frequencyDomain) { for (size_t ch = 0; ch < channelCount; ++ch) { FFTModel *model = new FFTModel - (getInput(), - channelCount == 1 ? m_context.channel : ch, - m_context.windowType, - m_context.blockSize, - m_context.stepSize, - m_context.blockSize, + (getConformingInput(), + channelCount == 1 ? m_input.getChannel() : ch, + m_transform.getWindowType(), + blockSize, + stepSize, + blockSize, false, StorageAdviser::PrecisionCritical); if (!model->isOK()) { @@ -301,11 +351,17 @@ } } - long startFrame = m_input->getStartFrame(); - long endFrame = m_input->getEndFrame(); + long startFrame = m_input.getModel()->getStartFrame(); + long endFrame = m_input.getModel()->getEndFrame(); - long contextStart = m_context.startFrame; - long contextDuration = m_context.duration; + RealTime contextStartRT = m_transform.getStartTime(); + RealTime contextDurationRT = m_transform.getDuration(); + + long contextStart = + RealTime::realTime2Frame(contextStartRT, sampleRate); + + long contextDuration = + RealTime::realTime2Frame(contextDurationRT, sampleRate); if (contextStart == 0 || contextStart < startFrame) { contextStart = startFrame; @@ -327,7 +383,7 @@ while (!m_abandoned) { if (frequencyDomain) { - if (blockFrame - int(m_context.blockSize)/2 > + if (blockFrame - int(blockSize)/2 > contextStart + contextDuration) break; } else { if (blockFrame >= @@ -336,25 +392,24 @@ // std::cerr << "FeatureExtractionModelTransformer::run: blockFrame " // << blockFrame << ", endFrame " << endFrame << ", blockSize " -// << m_context.blockSize << std::endl; +// << blockSize << std::endl; long completion = - (((blockFrame - contextStart) / m_context.stepSize) * 99) / - (contextDuration / m_context.stepSize); + (((blockFrame - contextStart) / stepSize) * 99) / + (contextDuration / stepSize); - // channelCount is either m_input->channelCount or 1 + // channelCount is either m_input.getModel()->channelCount or 1 - for (size_t ch = 0; ch < channelCount; ++ch) { - if (frequencyDomain) { - int column = (blockFrame - startFrame) / m_context.stepSize; - for (size_t i = 0; i <= m_context.blockSize/2; ++i) { + if (frequencyDomain) { + for (size_t ch = 0; ch < channelCount; ++ch) { + int column = (blockFrame - startFrame) / stepSize; + for (size_t i = 0; i <= blockSize/2; ++i) { fftModels[ch]->getValuesAt (column, i, buffers[ch][i*2], buffers[ch][i*2+1]); } - } else { - getFrames(ch, channelCount, - blockFrame, m_context.blockSize, buffers[ch]); - } + } + } else { + getFrames(channelCount, blockFrame, blockSize, buffers); } Vamp::Plugin::FeatureSet features = m_plugin->process @@ -371,7 +426,7 @@ prevCompletion = completion; } - blockFrame += m_context.stepSize; + blockFrame += stepSize; } if (m_abandoned) return; @@ -394,15 +449,17 @@ } void -FeatureExtractionModelTransformer::getFrames(int channel, int channelCount, - long startFrame, long size, - float *buffer) +FeatureExtractionModelTransformer::getFrames(int channelCount, + long startFrame, long size, + float **buffers) { long offset = 0; if (startFrame < 0) { - for (int i = 0; i < size && startFrame + i < 0; ++i) { - buffer[i] = 0.0f; + for (int c = 0; c < channelCount; ++c) { + for (int i = 0; i < size && startFrame + i < 0; ++i) { + buffers[c][i] = 0.0f; + } } offset = -startFrame; size -= offset; @@ -410,30 +467,52 @@ startFrame = 0; } - long got = getInput()->getData - ((channelCount == 1 ? m_context.channel : channel), - startFrame, size, buffer + offset); + DenseTimeValueModel *input = getConformingInput(); + if (!input) return; + + long got = 0; + + if (channelCount == 1) { + + got = input->getData(m_input.getChannel(), startFrame, size, + buffers[0] + offset); + + if (m_input.getChannel() == -1 && input->getChannelCount() > 1) { + // use mean instead of sum, as plugin input + float cc = float(input->getChannelCount()); + for (long i = 0; i < size; ++i) { + buffers[0][i + offset] /= cc; + } + } + + } else { + + float **writebuf = buffers; + if (offset > 0) { + writebuf = new float *[channelCount]; + for (int i = 0; i < channelCount; ++i) { + writebuf[i] = buffers[i] + offset; + } + } + + got = input->getData(0, channelCount-1, startFrame, size, writebuf); + + if (writebuf != buffers) delete[] writebuf; + } while (got < size) { - buffer[offset + got] = 0.0; + for (int c = 0; c < channelCount; ++c) { + buffers[c][got + offset] = 0.0; + } ++got; } - - if (m_context.channel == -1 && channelCount == 1 && - getInput()->getChannelCount() > 1) { - // use mean instead of sum, as plugin input - int cc = getInput()->getChannelCount(); - for (long i = 0; i < size; ++i) { - buffer[i] /= cc; - } - } } void FeatureExtractionModelTransformer::addFeature(size_t blockFrame, const Vamp::Plugin::Feature &feature) { - size_t inputRate = m_input->getSampleRate(); + size_t inputRate = m_input.getModel()->getSampleRate(); // std::cerr << "FeatureExtractionModelTransformer::addFeature(" // << blockFrame << ")" << std::endl; @@ -472,8 +551,10 @@ if (binCount == 0) { - SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>(); + SparseOneDimensionalModel *model = + getConformingOutput<SparseOneDimensionalModel>(); if (!model) return; + model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); } else if (binCount == 1) { @@ -481,8 +562,10 @@ float value = 0.0; if (feature.values.size() > 0) value = feature.values[0]; - SparseTimeValueModel *model = getOutput<SparseTimeValueModel>(); + SparseTimeValueModel *model = + getConformingOutput<SparseTimeValueModel>(); if (!model) return; + model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str())); // std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl; @@ -500,7 +583,7 @@ if (velocity < 0) velocity = 127; if (velocity > 127) velocity = 127; - NoteModel *model = getOutput<NoteModel>(); + NoteModel *model = getConformingOutput<NoteModel>(); if (!model) return; model->addPoint(NoteModel::Point(frame, pitch, @@ -513,7 +596,7 @@ DenseThreeDimensionalModel::Column values = feature.values; EditableDenseThreeDimensionalModel *model = - getOutput<EditableDenseThreeDimensionalModel>(); + getConformingOutput<EditableDenseThreeDimensionalModel>(); if (!model) return; model->setColumn(frame / model->getResolution(), values); @@ -533,29 +616,32 @@ if (binCount == 0) { - SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>(); + SparseOneDimensionalModel *model = + getConformingOutput<SparseOneDimensionalModel>(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } else if (binCount == 1) { - SparseTimeValueModel *model = getOutput<SparseTimeValueModel>(); + SparseTimeValueModel *model = + getConformingOutput<SparseTimeValueModel>(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } else if (m_descriptor->sampleType == Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - NoteModel *model = getOutput<NoteModel>(); + NoteModel *model = + getConformingOutput<NoteModel>(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } else { EditableDenseThreeDimensionalModel *model = - getOutput<EditableDenseThreeDimensionalModel>(); + getConformingOutput<EditableDenseThreeDimensionalModel>(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } }
--- a/plugin/transform/FeatureExtractionModelTransformer.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/FeatureExtractionModelTransformer.h Wed Feb 27 10:32:45 2008 +0000 @@ -16,20 +16,23 @@ #ifndef _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ #define _FEATURE_EXTRACTION_PLUGIN_TRANSFORMER_H_ -#include "PluginTransformer.h" +#include "ModelTransformer.h" + +#include <QString> + +#include <vamp-sdk/Plugin.h> + +#include <iostream> class DenseTimeValueModel; -class FeatureExtractionModelTransformer : public PluginTransformer +class FeatureExtractionModelTransformer : public ModelTransformer { Q_OBJECT public: - FeatureExtractionModelTransformer(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString outputName = ""); + FeatureExtractionModelTransformer(Input input, + const Transform &transform); virtual ~FeatureExtractionModelTransformer(); protected: @@ -44,12 +47,12 @@ void setCompletion(int); - void getFrames(int channel, int channelCount, - long startFrame, long size, float *buffer); + void getFrames(int channelCount, long startFrame, long size, + float **buffer); // just casts - DenseTimeValueModel *getInput(); - template <typename ModelClass> ModelClass *getOutput() { + DenseTimeValueModel *getConformingInput(); + template <typename ModelClass> ModelClass *getConformingOutput() { ModelClass *mc = dynamic_cast<ModelClass *>(m_output); if (!mc) { std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl;
--- a/plugin/transform/ModelTransformer.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/ModelTransformer.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -15,8 +15,9 @@ #include "ModelTransformer.h" -ModelTransformer::ModelTransformer(Model *m) : - m_input(m), +ModelTransformer::ModelTransformer(Input input, const Transform &transform) : + m_transform(transform), + m_input(input), m_output(0), m_detached(false), m_abandoned(false)
--- a/plugin/transform/ModelTransformer.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/ModelTransformer.h Wed Feb 27 10:32:45 2008 +0000 @@ -20,6 +20,8 @@ #include "data/model/Model.h" +#include "Transform.h" + /** * A ModelTransformer turns one data model into another. * @@ -38,22 +40,72 @@ public: virtual ~ModelTransformer(); - // Just a hint to the processing thread that it should give up. - // Caller should still wait() and/or delete the transform before - // assuming its input and output models are no longer required. + class Input { + public: + Input(Model *m) : m_model(m), m_channel(-1) { } + Input(Model *m, int c) : m_model(m), m_channel(c) { } + + Model *getModel() const { return m_model; } + void setModel(Model *m) { m_model = m; } + + int getChannel() const { return m_channel; } + void setChannel(int c) { m_channel = c; } + + protected: + Model *m_model; + int m_channel; + }; + + /** + * Hint to the processing thread that it should give up, for + * example because the process is going to exit or we want to get + * rid of the input model. Caller should still wait() and/or + * delete the transform before assuming its input and output + * models are no longer required. + */ void abandon() { m_abandoned = true; } - Model *getInputModel() { return m_input; } + /** + * Return the input model for the transform. + */ + Model *getInputModel() { return m_input.getModel(); } + + /** + * Return the input channel spec for the transform. + */ + int getInputChannel() { return m_input.getChannel(); } + + /** + * Return the output model created by the transform. Returns a + * null model if the transform could not be initialised; an error + * message may be available via getMessage() in this situation. + */ Model *getOutputModel() { return m_output; } + + /** + * Return the output model, also detaching it from the transformer + * so that it will not be deleted when the transformer is. The + * caller takes ownership of the model. + */ Model *detachOutputModel() { m_detached = true; return m_output; } + /** + * Return a warning or error message. If getOutputModel returned + * a null pointer, this should contain a fatal error message for + * the transformer; otherwise it may contain a warning to show to + * the user about e.g. suboptimal block size or whatever. + */ + QString getMessage() const { return m_message; } + protected: - ModelTransformer(Model *m); + ModelTransformer(Input input, const Transform &transform); - Model *m_input; // I don't own this + Transform m_transform; + Input m_input; // I don't own the model in this Model *m_output; // I own this, unless... bool m_detached; // ... this is true. bool m_abandoned; + QString m_message; }; #endif
--- a/plugin/transform/ModelTransformerFactory.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/ModelTransformerFactory.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -51,7 +51,8 @@ } bool -ModelTransformerFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin, +ModelTransformerFactory::getChannelRange(TransformId identifier, + Vamp::PluginBase *plugin, int &minChannels, int &maxChannels) { Vamp::Plugin *vp = 0; @@ -66,17 +67,17 @@ } } -Model * -ModelTransformerFactory::getConfigurationForTransformer(TransformId identifier, - const std::vector<Model *> &candidateInputModels, - Model *defaultInputModel, - PluginTransformer::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source, - size_t startFrame, - size_t duration) +ModelTransformer::Input +ModelTransformerFactory::getConfigurationForTransform(Transform &transform, + const std::vector<Model *> &candidateInputModels, + Model *defaultInputModel, + AudioCallbackPlaySource *source, + size_t startFrame, + size_t duration) { - if (candidateInputModels.empty()) return 0; + ModelTransformer::Input input(0); + + if (candidateInputModels.empty()) return input; //!!! This will need revision -- we'll have to have a callback //from the dialog for when the candidate input model is changed, @@ -99,15 +100,15 @@ } } - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); + QString id = transform.getPluginIdentifier(); + QString output = transform.getOutput(); QString outputLabel = ""; QString outputDescription = ""; bool ok = false; - configurationXml = m_lastConfigurations[identifier]; + QString configurationXml = m_lastConfigurations[transform.getIdentifier()]; -// std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; + std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl; Vamp::PluginBase *plugin = 0; @@ -117,7 +118,7 @@ if (FeatureExtractionPluginFactory::instanceFor(id)) { - std::cerr << "getConfigurationForTransformer: instantiating Vamp plugin" << std::endl; + std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl; Vamp::Plugin *vp = FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin @@ -184,11 +185,18 @@ if (plugin) { - context = PluginTransformer::ExecutionContext(context.channel, plugin); + // Ensure block size etc are valid + TransformFactory::getInstance()-> + makeContextConsistentWithPlugin(transform, plugin); - if (configurationXml != "") { - PluginXml(plugin).setParametersFromXml(configurationXml); - } + // Prepare the plugin with any existing parameters already + // found in the transform + TransformFactory::getInstance()-> + setPluginParameters(transform, plugin); + + // For this interactive usage, we want to override those with + // whatever the user chose last time around + PluginXml(plugin).setParametersFromXml(configurationXml); int sourceChannels = 1; if (dynamic_cast<DenseTimeValueModel *>(inputModel)) { @@ -197,7 +205,8 @@ } int minChannels = 1, maxChannels = sourceChannels; - getChannelRange(identifier, plugin, minChannels, maxChannels); + getChannelRange(transform.getIdentifier(), plugin, + minChannels, maxChannels); int targetChannels = sourceChannels; if (!effect) { @@ -205,7 +214,7 @@ if (sourceChannels > maxChannels) targetChannels = maxChannels; } - int defaultChannel = context.channel; + int defaultChannel = -1; //!!! no longer saved! [was context.channel] PluginParameterDialog *dialog = new PluginParameterDialog(plugin); @@ -242,22 +251,42 @@ } else { std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl; } + + // Write parameters back to transform object + TransformFactory::getInstance()-> + setParametersFromPlugin(transform, plugin); - configurationXml = PluginXml(plugin).toXmlString(); - context.channel = dialog->getChannel(); + input.setChannel(dialog->getChannel()); + //!!! The dialog ought to be taking & returning transform + //objects and input objects and stuff rather than passing + //around all this misc stuff, but that's for tomorrow + //(whenever that may be) + if (startFrame != 0 || duration != 0) { if (dialog->getSelectionOnly()) { - context.startFrame = startFrame; - context.duration = duration; + transform.setStartTime(RealTime::frame2RealTime + (startFrame, inputModel->getSampleRate())); + transform.setDuration(RealTime::frame2RealTime + (duration, inputModel->getSampleRate())); } } - dialog->getProcessingParameters(context.stepSize, - context.blockSize, - context.windowType); + size_t stepSize = 0, blockSize = 0; + WindowType windowType = HanningWindow; - context.makeConsistentWithPlugin(plugin); + dialog->getProcessingParameters(stepSize, + blockSize, + windowType); + + transform.setStepSize(stepSize); + transform.setBlockSize(blockSize); + transform.setWindowType(windowType); + + TransformFactory::getInstance()-> + makeContextConsistentWithPlugin(transform, plugin); + + configurationXml = PluginXml(plugin).toXmlString(); delete dialog; @@ -268,11 +297,14 @@ } } - if (ok) m_lastConfigurations[identifier] = configurationXml; + if (ok) { + m_lastConfigurations[transform.getIdentifier()] = configurationXml; + input.setModel(inputModel); + } - return ok ? inputModel : 0; + return input; } - +/*!!! PluginTransformer::ExecutionContext ModelTransformerFactory::getDefaultContextForTransformer(TransformId identifier, Model *inputModel) @@ -295,43 +327,41 @@ return context; } - +*/ ModelTransformer * -ModelTransformerFactory::createTransformer(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml) +ModelTransformerFactory::createTransformer(const Transform &transform, + const ModelTransformer::Input &input) { ModelTransformer *transformer = 0; - QString id = identifier.section(':', 0, 2); - QString output = identifier.section(':', 3); + QString id = transform.getPluginIdentifier(); if (FeatureExtractionPluginFactory::instanceFor(id)) { - transformer = new FeatureExtractionModelTransformer - (inputModel, id, context, configurationXml, output); + + transformer = + new FeatureExtractionModelTransformer(input, transform); + } else if (RealTimePluginFactory::instanceFor(id)) { - transformer = new RealTimeEffectModelTransformer - (inputModel, id, context, configurationXml, - TransformFactory::getInstance()->getTransformUnits(identifier), - output == "A" ? -1 : output.toInt()); + + transformer = + new RealTimeEffectModelTransformer(input, transform); + } else { std::cerr << "ModelTransformerFactory::createTransformer: Unknown transform \"" - << identifier.toStdString() << "\"" << std::endl; + << transform.getIdentifier().toStdString() << "\"" << std::endl; return transformer; } - if (transformer) transformer->setObjectName(identifier); + if (transformer) transformer->setObjectName(transform.getIdentifier()); return transformer; } Model * -ModelTransformerFactory::transform(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml) +ModelTransformerFactory::transform(const Transform &transform, + const ModelTransformer::Input &input, + QString &message) { - ModelTransformer *t = createTransformer(identifier, inputModel, context, - configurationXml); - + ModelTransformer *t = createTransformer(transform, input); if (!t) return 0; connect(t, SIGNAL(finished()), this, SLOT(transformerFinished())); @@ -342,10 +372,10 @@ Model *model = t->detachOutputModel(); if (model) { - QString imn = inputModel->objectName(); + QString imn = input.getModel()->objectName(); QString trn = TransformFactory::getInstance()->getTransformFriendlyName - (identifier); + (transform.getIdentifier()); if (imn != "") { if (trn != "") { model->setObjectName(tr("%1: %2").arg(imn).arg(trn)); @@ -359,6 +389,8 @@ t->wait(); } + message = t->getMessage(); + return model; }
--- a/plugin/transform/ModelTransformerFactory.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/ModelTransformerFactory.h Wed Feb 27 10:32:45 2008 +0000 @@ -21,8 +21,6 @@ #include "ModelTransformer.h" -#include "PluginTransformer.h" - #include <map> #include <set> @@ -40,28 +38,21 @@ static ModelTransformerFactory *getInstance(); /** - * Get a configuration XML string for the given transform (by - * asking the user, most likely). Returns the selected input - * model if the transform is acceptable, 0 if the operation should - * be cancelled. Audio callback play source may be used to - * audition effects plugins, if provided. + * Fill out the configuration for the given transform (by asking + * the user, most likely). Returns the selected input model and + * channel if the transform is acceptable, or an input with a null + * model if the operation should be cancelled. Audio callback + * play source may be used to audition effects plugins, if + * provided. */ - Model *getConfigurationForTransformer(TransformId identifier, - const std::vector<Model *> &candidateInputModels, - Model *defaultInputModel, - PluginTransformer::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source = 0, - size_t startFrame = 0, - size_t duration = 0); - - /** - * Get the default execution context for the given transform - * and input model (if known). - */ - PluginTransformer::ExecutionContext getDefaultContextForTransformer(TransformId identifier, - Model *inputModel = 0); - + ModelTransformer::Input + getConfigurationForTransform(Transform &transform, + const std::vector<Model *> &candidateInputModels, + Model *defaultInputModel, + AudioCallbackPlaySource *source = 0, + size_t startFrame = 0, + size_t duration = 0); + /** * Return the output model resulting from applying the named * transform to the given input model. The transform may still be @@ -70,14 +61,15 @@ * * 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. + * problem occurs, return 0. Set message if there is any error or + * warning to report. * * The returned model is owned by the caller and must be deleted * when no longer needed. */ - Model *transform(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml = ""); + Model *transform(const Transform &transform, + const ModelTransformer::Input &input, + QString &message); protected slots: void transformerFinished(); @@ -85,9 +77,8 @@ void modelAboutToBeDeleted(Model *); protected: - ModelTransformer *createTransformer(TransformId identifier, Model *inputModel, - const PluginTransformer::ExecutionContext &context, - QString configurationXml); + ModelTransformer *createTransformer(const Transform &transform, + const ModelTransformer::Input &input); typedef std::map<TransformId, QString> TransformerConfigurationMap; TransformerConfigurationMap m_lastConfigurations;
--- a/plugin/transform/PluginTransformer.cpp Fri Nov 30 17:36:14 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 QMUL. - - 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 "PluginTransformer.h" - -#include "vamp-sdk/PluginHostAdapter.h" -#include "vamp-sdk/hostext/PluginWrapper.h" - -PluginTransformer::PluginTransformer(Model *inputModel, - const ExecutionContext &context) : - ModelTransformer(inputModel), - m_context(context) -{ -} - -PluginTransformer::ExecutionContext::ExecutionContext(int _c, size_t _bs) : - channel(_c), - domain(Vamp::Plugin::TimeDomain), - stepSize(_bs ? _bs : 1024), - blockSize(_bs ? _bs : 1024), - windowType(HanningWindow), - startFrame(0), - duration(0), - sampleRate(0), - updates(true) -{ -} - -PluginTransformer::ExecutionContext::ExecutionContext(int _c, size_t _ss, - size_t _bs, WindowType _wt) : - channel(_c), - domain(Vamp::Plugin::FrequencyDomain), - stepSize(_ss ? _ss : (_bs ? _bs / 2 : 512)), - blockSize(_bs ? _bs : 1024), - windowType(_wt), - startFrame(0), - duration(0), - sampleRate(0), - updates(true) -{ -} - -PluginTransformer::ExecutionContext::ExecutionContext(int _c, - const Vamp::PluginBase *_plugin) : - channel(_c), - domain(Vamp::Plugin::TimeDomain), - stepSize(0), - blockSize(0), - windowType(HanningWindow), - startFrame(0), - duration(0), - sampleRate(0) -{ - makeConsistentWithPlugin(_plugin); -} - -bool -PluginTransformer::ExecutionContext::operator==(const ExecutionContext &c) -{ - return (c.channel == channel && - c.domain == domain && - c.stepSize == stepSize && - c.blockSize == blockSize && - c.windowType == windowType && - c.startFrame == startFrame && - c.duration == duration && - c.sampleRate == sampleRate); -} - -void -PluginTransformer::ExecutionContext::makeConsistentWithPlugin(const Vamp::PluginBase *_plugin) -{ - const Vamp::Plugin *vp = dynamic_cast<const Vamp::Plugin *>(_plugin); - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; - vp = dynamic_cast<const Vamp::PluginHostAdapter *>(_plugin); //!!! why? -} - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; - vp = dynamic_cast<const Vamp::HostExt::PluginWrapper *>(_plugin); //!!! no, I mean really why? - } - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; - } - -// std::cerr << "makeConsistentWithPlugin: stepSize = " << stepSize << ", blockSize = " << blockSize << std::endl; - - if (!vp) { - domain = Vamp::Plugin::TimeDomain; -// std::cerr << "time domain RT plugin" << std::endl; - if (!stepSize) { - if (!blockSize) blockSize = 1024; - stepSize = blockSize; - } else { - if (!blockSize) blockSize = stepSize; - } - } else { - domain = vp->getInputDomain(); -// std::cerr << "feature extraction plugin" << std::endl; - if (!stepSize) stepSize = vp->getPreferredStepSize(); - if (!blockSize) blockSize = vp->getPreferredBlockSize(); - if (!blockSize) blockSize = 1024; - if (!stepSize) { - if (domain == Vamp::Plugin::FrequencyDomain) { -// std::cerr << "frequency domain, step = " << blockSize/2 << std::endl; - stepSize = blockSize/2; - } else { -// std::cerr << "time domain, step = " << blockSize/2 << std::endl; - stepSize = blockSize; - } - } - } -} - -
--- a/plugin/transform/PluginTransformer.h Fri Nov 30 17:36:14 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 QMUL. - - 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. -*/ - -#ifndef _PLUGIN_TRANSFORMER_H_ -#define _PLUGIN_TRANSFORMER_H_ - -#include "ModelTransformer.h" - -#include "base/Window.h" - -#include "vamp-sdk/Plugin.h" - -//!!! should this just move back up to Transformer? It is after all used -//directly in all sorts of generic places, like Document - -class PluginTransformer : public ModelTransformer -{ -public: - class ExecutionContext { - public: - // Time domain: - ExecutionContext(int _c = -1, size_t _bs = 0); - - // Frequency domain: - ExecutionContext(int _c, size_t _ss, size_t _bs, WindowType _wt); - - // From plugin defaults: - ExecutionContext(int _c, const Vamp::PluginBase *_plugin); - - bool operator==(const ExecutionContext &); - - void makeConsistentWithPlugin(const Vamp::PluginBase *_plugin); - - int channel; - Vamp::Plugin::InputDomain domain; - size_t stepSize; - size_t blockSize; - WindowType windowType; - size_t startFrame; - size_t duration; // 0 -> whole thing - float sampleRate; // 0 -> model's rate - bool updates; - }; - -protected: - PluginTransformer(Model *inputModel, - const ExecutionContext &context); - - ExecutionContext m_context; -}; - -#endif
--- a/plugin/transform/RealTimeEffectModelTransformer.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/RealTimeEffectModelTransformer.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -25,22 +25,23 @@ #include "data/model/WritableWaveFileModel.h" #include "data/model/WaveFileModel.h" +#include "TransformFactory.h" + #include <iostream> -RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Model *inputModel, - QString pluginId, - const ExecutionContext &context, - QString configurationXml, - QString units, - int output) : - PluginTransformer(inputModel, context), - m_pluginId(pluginId), - m_configurationXml(configurationXml), - m_units(units), - m_plugin(0), - m_outputNo(output) +RealTimeEffectModelTransformer::RealTimeEffectModelTransformer(Input in, + const Transform &transform) : + ModelTransformer(in, transform), + m_plugin(0) { - if (!m_context.blockSize) m_context.blockSize = 1024; + m_units = TransformFactory::getInstance()->getTransformUnits + (transform.getIdentifier()); + m_outputNo = + (transform.getOutput() == "A") ? -1 : transform.getOutput().toInt(); + + QString pluginId = transform.getPluginIdentifier(); + + if (!m_transform.getBlockSize()) m_transform.setBlockSize(1024); // std::cerr << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId.toStdString() << ", output " << output << std::endl; @@ -53,12 +54,12 @@ return; } - DenseTimeValueModel *input = getInput(); + DenseTimeValueModel *input = getConformingInput(); if (!input) return; m_plugin = factory->instantiatePlugin(pluginId, 0, 0, - m_input->getSampleRate(), - m_context.blockSize, + input->getSampleRate(), + m_transform.getBlockSize(), input->getChannelCount()); if (!m_plugin) { @@ -67,9 +68,7 @@ return; } - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } + TransformFactory::getInstance()->setPluginParameters(m_transform, m_plugin); if (m_outputNo >= 0 && m_outputNo >= int(m_plugin->getControlOutputCount())) { @@ -92,9 +91,9 @@ } else { SparseTimeValueModel *model = new SparseTimeValueModel - (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false); + (input->getSampleRate(), m_transform.getBlockSize(), 0.0, 0.0, false); - if (units != "") model->setScaleUnits(units); + if (m_units != "") model->setScaleUnits(m_units); m_output = model; } @@ -106,12 +105,12 @@ } DenseTimeValueModel * -RealTimeEffectModelTransformer::getInput() +RealTimeEffectModelTransformer::getConformingInput() { DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(getInputModel()); if (!dtvm) { - std::cerr << "RealTimeEffectModelTransformer::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; + std::cerr << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl; } return dtvm; } @@ -119,7 +118,7 @@ void RealTimeEffectModelTransformer::run() { - DenseTimeValueModel *input = getInput(); + DenseTimeValueModel *input = getConformingInput(); if (!input) return; while (!input->isReady()) { @@ -136,17 +135,23 @@ size_t sampleRate = input->getSampleRate(); size_t channelCount = input->getChannelCount(); - if (!wwfm && m_context.channel != -1) channelCount = 1; + if (!wwfm && m_input.getChannel() != -1) channelCount = 1; long blockSize = m_plugin->getBufferSize(); float **inbufs = m_plugin->getAudioInputBuffers(); - long startFrame = m_input->getStartFrame(); - long endFrame = m_input->getEndFrame(); + long startFrame = m_input.getModel()->getStartFrame(); + long endFrame = m_input.getModel()->getEndFrame(); - long contextStart = m_context.startFrame; - long contextDuration = m_context.duration; + RealTime contextStartRT = m_transform.getStartTime(); + RealTime contextDurationRT = m_transform.getDuration(); + + long contextStart = + RealTime::realTime2Frame(contextStartRT, sampleRate); + + long contextDuration = + RealTime::realTime2Frame(contextDurationRT, sampleRate); if (contextStart == 0 || contextStart < startFrame) { contextStart = startFrame; @@ -179,7 +184,7 @@ if (channelCount == 1) { if (inbufs && inbufs[0]) { got = input->getData - (m_context.channel, blockFrame, blockSize, inbufs[0]); + (m_input.getChannel(), blockFrame, blockSize, inbufs[0]); while (got < blockSize) { inbufs[0][got++] = 0.0; } @@ -190,14 +195,14 @@ } } } else { - for (size_t ch = 0; ch < channelCount; ++ch) { - if (inbufs && inbufs[ch]) { - got = input->getData - (ch, blockFrame, blockSize, inbufs[ch]); - while (got < blockSize) { - inbufs[ch][got++] = 0.0; - } + got = input->getData(0, channelCount - 1, + blockFrame, blockSize, + inbufs); + while (got < blockSize) { + for (size_t ch = 0; ch < channelCount; ++ch) { + inbufs[ch][got] = 0.0; } + ++got; } for (size_t ch = channelCount; ch < m_plugin->getAudioInputCount(); ++ch) { for (long i = 0; i < blockSize; ++i) {
--- a/plugin/transform/RealTimeEffectModelTransformer.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/RealTimeEffectModelTransformer.h Wed Feb 27 10:32:45 2008 +0000 @@ -16,34 +16,27 @@ #ifndef _REAL_TIME_PLUGIN_TRANSFORMER_H_ #define _REAL_TIME_PLUGIN_TRANSFORMER_H_ -#include "PluginTransformer.h" +#include "ModelTransformer.h" #include "plugin/RealTimePluginInstance.h" class DenseTimeValueModel; -class RealTimeEffectModelTransformer : public PluginTransformer +class RealTimeEffectModelTransformer : public ModelTransformer { public: - RealTimeEffectModelTransformer(Model *inputModel, - QString plugin, - const ExecutionContext &context, - QString configurationXml = "", - QString units = "", - int output = -1); // -1 -> audio, 0+ -> data + RealTimeEffectModelTransformer(Input input, + const Transform &transform); virtual ~RealTimeEffectModelTransformer(); protected: virtual void run(); - QString m_pluginId; - QString m_configurationXml; QString m_units; - RealTimePluginInstance *m_plugin; int m_outputNo; // just casts - DenseTimeValueModel *getInput(); + DenseTimeValueModel *getConformingInput(); }; #endif
--- a/plugin/transform/Transform.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/Transform.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -19,6 +19,17 @@ #include "plugin/FeatureExtractionPluginFactory.h" +#include <QXmlAttributes> + +#include <QDomDocument> +#include <QDomElement> +#include <QDomNamedNodeMap> +#include <QDomAttr> + +#include <QTextStream> + +#include <iostream> + Transform::Transform() : m_stepSize(0), m_blockSize(0), @@ -27,10 +38,102 @@ { } +Transform::Transform(QString xml) : + m_stepSize(0), + m_blockSize(0), + m_windowType(HanningWindow), + m_sampleRate(0) +{ + QDomDocument doc; + + QString error; + int errorLine; + int errorColumn; + + if (!doc.setContent(xml, false, &error, &errorLine, &errorColumn)) { + std::cerr << "Transform::Transform: Error in parsing XML: " + << error.toStdString() << " at line " << errorLine + << ", column " << errorColumn << std::endl; + std::cerr << "Input follows:" << std::endl; + std::cerr << xml.toStdString() << std::endl; + std::cerr << "Input ends." << std::endl; + return; + } + + QDomElement transformElt = doc.firstChildElement("transform"); + QDomNamedNodeMap attrNodes = transformElt.attributes(); + QXmlAttributes attrs; + + for (unsigned int i = 0; i < attrNodes.length(); ++i) { + QDomAttr attr = attrNodes.item(i).toAttr(); + if (!attr.isNull()) attrs.append(attr.name(), "", "", attr.value()); + } + + setFromXmlAttributes(attrs); + + for (QDomElement paramElt = transformElt.firstChildElement("parameter"); + !paramElt.isNull(); + paramElt = paramElt.nextSiblingElement("parameter")) { + + QDomNamedNodeMap paramAttrs = paramElt.attributes(); + + QDomAttr nameAttr = paramAttrs.namedItem("name").toAttr(); + if (nameAttr.isNull() || nameAttr.value() == "") continue; + + QDomAttr valueAttr = paramAttrs.namedItem("value").toAttr(); + if (valueAttr.isNull() || valueAttr.value() == "") continue; + + setParameter(nameAttr.value(), valueAttr.value().toFloat()); + } + + for (QDomElement configElt = transformElt.firstChildElement("configuration"); + !configElt.isNull(); + configElt = configElt.nextSiblingElement("configuration")) { + + QDomNamedNodeMap configAttrs = configElt.attributes(); + + QDomAttr nameAttr = configAttrs.namedItem("name").toAttr(); + if (nameAttr.isNull() || nameAttr.value() == "") continue; + + QDomAttr valueAttr = configAttrs.namedItem("value").toAttr(); + if (valueAttr.isNull() || valueAttr.value() == "") continue; + + setConfigurationValue(nameAttr.value(), valueAttr.value()); + } +} + Transform::~Transform() { } +bool +Transform::operator==(const Transform &t) +{ + return + m_id == t.m_id && + m_parameters == t.m_parameters && + m_configuration == t.m_configuration && + m_program == t.m_program && + m_stepSize == t.m_stepSize && + m_blockSize == t.m_blockSize && + m_windowType == t.m_windowType && + m_startTime == t.m_startTime && + m_duration == t.m_duration && + m_sampleRate == t.m_sampleRate; +} + +void +Transform::setIdentifier(TransformId id) +{ + m_id = id; +} + +TransformId +Transform::getIdentifier() const +{ + return m_id; +} + QString Transform::createIdentifier(QString type, QString soName, QString label, QString output) @@ -74,7 +177,250 @@ } void -Transform::toXml(QTextStream &stream, QString indent, QString extraAttributes) const +Transform::setPluginIdentifier(QString pluginIdentifier) { + m_id = pluginIdentifier + ':' + getOutput(); +} + +void +Transform::setOutput(QString output) +{ + m_id = getPluginIdentifier() + ':' + output; +} + +TransformId +Transform::getIdentifierForPluginOutput(QString pluginIdentifier, + QString output) +{ + return pluginIdentifier + ':' + output; +} + +const Transform::ParameterMap & +Transform::getParameters() const +{ + return m_parameters; +} + +void +Transform::setParameters(const ParameterMap &pm) +{ + m_parameters = pm; +} + +void +Transform::setParameter(QString name, float value) +{ + std::cerr << "Transform::setParameter(" << name.toStdString() + << ") -> " << value << std::endl; + m_parameters[name] = value; +} + +const Transform::ConfigurationMap & +Transform::getConfiguration() const +{ + return m_configuration; +} + +void +Transform::setConfiguration(const ConfigurationMap &cm) +{ + m_configuration = cm; +} + +void +Transform::setConfigurationValue(QString name, QString value) +{ + std::cerr << "Transform::setConfigurationValue(" << name.toStdString() + << ") -> " << value.toStdString() << std::endl; + m_configuration[name] = value; +} + +QString +Transform::getPluginVersion() const +{ + return m_pluginVersion; +} + +void +Transform::setPluginVersion(QString version) +{ + m_pluginVersion = version; +} + +QString +Transform::getProgram() const +{ + return m_program; +} + +void +Transform::setProgram(QString program) +{ + m_program = program; +} + +size_t +Transform::getStepSize() const +{ + return m_stepSize; } + +void +Transform::setStepSize(size_t s) +{ + m_stepSize = s; +} + +size_t +Transform::getBlockSize() const +{ + return m_blockSize; +} + +void +Transform::setBlockSize(size_t s) +{ + m_blockSize = s; +} + +WindowType +Transform::getWindowType() const +{ + return m_windowType; +} + +void +Transform::setWindowType(WindowType type) +{ + m_windowType = type; +} + +RealTime +Transform::getStartTime() const +{ + return m_startTime; +} + +void +Transform::setStartTime(RealTime t) +{ + m_startTime = t; +} + +RealTime +Transform::getDuration() const +{ + return m_duration; +} + +void +Transform::setDuration(RealTime d) +{ + m_duration = d; +} + +float +Transform::getSampleRate() const +{ + return m_sampleRate; +} + +void +Transform::setSampleRate(float rate) +{ + m_sampleRate = rate; +} + +void +Transform::toXml(QTextStream &out, QString indent, QString extraAttributes) const +{ + out << indent; + + bool haveContent = true; + if (m_parameters.empty() && m_configuration.empty()) haveContent = false; + + out << QString("<transform id=\"%1\" pluginVersion=\"%2\" program=\"%3\" stepSize=\"%4\" blockSize=\"%5\" windowType=\"%6\" startTime=\"%7\" duration=\"%8\" sampleRate=\"%9\"") + .arg(encodeEntities(m_id)) + .arg(encodeEntities(m_pluginVersion)) + .arg(encodeEntities(m_program)) + .arg(m_stepSize) + .arg(m_blockSize) + .arg(encodeEntities(Window<float>::getNameForType(m_windowType).c_str())) + .arg(encodeEntities(m_startTime.toString().c_str())) + .arg(encodeEntities(m_duration.toString().c_str())) + .arg(m_sampleRate); + + if (extraAttributes != "") { + out << " " << extraAttributes; + } + + if (haveContent) { + + out << ">\n"; + + for (ParameterMap::const_iterator i = m_parameters.begin(); + i != m_parameters.end(); ++i) { + out << indent << " " + << QString("<parameter name=\"%1\" value=\"%2\"/>\n") + .arg(encodeEntities(i->first)) + .arg(i->second); + } + + for (ConfigurationMap::const_iterator i = m_configuration.begin(); + i != m_configuration.end(); ++i) { + out << indent << " " + << QString("<configuration name=\"%1\" value=\"%2\"/>\n") + .arg(encodeEntities(i->first)) + .arg(encodeEntities(i->second)); + } + + out << indent << "</transform>\n"; + + } else { + + out << "/>\n"; + } +} + +void +Transform::setFromXmlAttributes(const QXmlAttributes &attrs) +{ + if (attrs.value("id") != "") { + setIdentifier(attrs.value("id")); + } + + if (attrs.value("pluginVersion") != "") { + setPluginVersion(attrs.value("pluginVersion")); + } + + if (attrs.value("program") != "") { + setProgram(attrs.value("program")); + } + + if (attrs.value("stepSize") != "") { + setStepSize(attrs.value("stepSize").toInt()); + } + + if (attrs.value("blockSize") != "") { + setBlockSize(attrs.value("blockSize").toInt()); + } + + if (attrs.value("windowType") != "") { + setWindowType(Window<float>::getTypeForName + (attrs.value("windowType").toStdString())); + } + + if (attrs.value("startTime") != "") { + setStartTime(RealTime::fromString(attrs.value("startTime").toStdString())); + } + + if (attrs.value("duration") != "") { + setStartTime(RealTime::fromString(attrs.value("duration").toStdString())); + } + + if (attrs.value("sampleRate") != "") { + setSampleRate(attrs.value("sampleRate").toFloat()); + } +} +
--- a/plugin/transform/Transform.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/Transform.h Wed Feb 27 10:32:45 2008 +0000 @@ -18,13 +18,14 @@ #include "base/XmlExportable.h" #include "base/Window.h" - -#include <vamp-sdk/RealTime.h> +#include "base/RealTime.h" #include <QString> typedef QString TransformId; +class QXmlAttributes; + namespace Vamp { class PluginBase; } @@ -32,56 +33,101 @@ class Transform : public XmlExportable { public: + /** + * Construct a new Transform with default data and no identifier. + * The Transform object will be meaningless until some data and an + * identifier have been set on it. + * + * To construct a Transform for use with a particular transform + * identifier, use TransformFactory::getDefaultTransformFor. + */ Transform(); + + /** + * Construct a Transform by parsing the given XML data string. + * This is the inverse of toXml. + */ + Transform(QString xml); + virtual ~Transform(); - void setIdentifier(TransformId id) { m_id = id; } - TransformId getIdentifier() const { return m_id; } - - void setPlugin(QString pluginIdentifier); - void setOutput(QString output); + /** + * Compare two Transforms. They only compare equal if every data + * element matches. + */ + bool operator==(const Transform &); + + void setIdentifier(TransformId id); + TransformId getIdentifier() const; enum Type { FeatureExtraction, RealTimeEffect }; Type getType() const; QString getPluginIdentifier() const; QString getOutput() const; + + void setPluginIdentifier(QString pluginIdentifier); + void setOutput(QString output); + + // Turn a plugin ID and output name into a transform ID. Note + // that our pluginIdentifier is the same thing as the Vamp SDK's + // PluginLoader::PluginKey. + static TransformId getIdentifierForPluginOutput(QString pluginIdentifier, + QString output = ""); typedef std::map<QString, float> ParameterMap; - ParameterMap getParameters() const { return m_parameters; } - void setParameters(const ParameterMap &pm) { m_parameters = pm; } + const ParameterMap &getParameters() const; + void setParameters(const ParameterMap &pm); + void setParameter(QString name, float value); typedef std::map<QString, QString> ConfigurationMap; - ConfigurationMap getConfiguration() const { return m_configuration; } - void setConfiguration(const ConfigurationMap &cm) { m_configuration = cm; } + const ConfigurationMap &getConfiguration() const; + void setConfiguration(const ConfigurationMap &cm); + void setConfigurationValue(QString name, QString value); - QString getProgram() const { return m_program; } - void setProgram(QString program) { m_program = program; } + QString getPluginVersion() const; + void setPluginVersion(QString version); + + QString getProgram() const; + void setProgram(QString program); - size_t getStepSize() const { return m_stepSize; } - void setStepSize(size_t s) { m_stepSize = s; } + size_t getStepSize() const; + void setStepSize(size_t s); - size_t getBlockSize() const { return m_blockSize; } - void setBlockSize(size_t s) { m_blockSize = s; } - - WindowType getWindowType() const { return m_windowType; } - void setWindowType(WindowType type) { m_windowType = type; } - - Vamp::RealTime getStartTime() const { return m_startTime; } - void setStartTime(Vamp::RealTime t) { m_startTime = t; } - - Vamp::RealTime getDuration() const { return m_duration; } // 0 -> all - void setDuration(Vamp::RealTime d) { m_duration = d; } + size_t getBlockSize() const; + void setBlockSize(size_t s); - float getSampleRate() const { return m_sampleRate; } // 0 -> as input - void setSampleRate(float rate) { m_sampleRate = rate; } + WindowType getWindowType() const; + void setWindowType(WindowType type); + + RealTime getStartTime() const; + void setStartTime(RealTime t); + + RealTime getDuration() const; // 0 -> all + void setDuration(RealTime d); + + float getSampleRate() const; // 0 -> as input + void setSampleRate(float rate); void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const; - static Transform fromXmlString(QString xml); + /** + * Set the main transform data from the given XML attributes. + * This does not set the parameters or configuration, which are + * exported to separate XML elements rather than attributes of the + * transform element. + * + * Note that this only sets those attributes which are actually + * present in the argument. Any attributes not defined in the + * attribute will remain unchanged in the Transform. If your aim + * is to create a transform exactly matching the given attributes, + * ensure you start from an empty transform rather than one that + * has already been configured. + */ + void setFromXmlAttributes(const QXmlAttributes &); protected: TransformId m_id; // pluginid:output, that is type:soname:label:output @@ -95,12 +141,13 @@ ParameterMap m_parameters; ConfigurationMap m_configuration; + QString m_pluginVersion; QString m_program; size_t m_stepSize; size_t m_blockSize; WindowType m_windowType; - Vamp::RealTime m_startTime; - Vamp::RealTime m_duration; + RealTime m_startTime; + RealTime m_duration; float m_sampleRate; };
--- a/plugin/transform/TransformDescription.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/TransformDescription.h Wed Feb 27 10:32:45 2008 +0000 @@ -37,6 +37,10 @@ * The friendly name is a shorter version of the name. * * The type is also intended to be user-readable, for use in menus. + * + * To obtain these objects, use + * TransformFactory::getAllTransformDescriptions and + * TransformFactory::getTransformDescription. */ struct TransformDescription @@ -62,7 +66,9 @@ bool configurable; bool operator<(const TransformDescription &od) const { - return (name < od.name); + return + (name < od.name) || + (name == od.name && identifier < od.identifier); }; };
--- a/plugin/transform/TransformFactory.cpp Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/TransformFactory.cpp Wed Feb 27 10:32:45 2008 +0000 @@ -28,6 +28,7 @@ #include <set> #include <QRegExp> +#include <QTextStream> TransformFactory * TransformFactory::m_instance = new TransformFactory; @@ -43,25 +44,39 @@ } TransformList -TransformFactory::getAllTransforms() +TransformFactory::getAllTransformDescriptions() { if (m_transforms.empty()) populateTransforms(); std::set<TransformDescription> dset; for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); i != m_transforms.end(); ++i) { +// std::cerr << "inserting transform into set: id = " << i->second.identifier.toStdString() << std::endl; dset.insert(i->second); } TransformList list; for (std::set<TransformDescription>::const_iterator i = dset.begin(); i != dset.end(); ++i) { +// std::cerr << "inserting transform into list: id = " << i->identifier.toStdString() << std::endl; list.push_back(*i); } return list; } +TransformDescription +TransformFactory::getTransformDescription(TransformId id) +{ + if (m_transforms.empty()) populateTransforms(); + + if (m_transforms.find(id) == m_transforms.end()) { + return TransformDescription(); + } + + return m_transforms[id]; +} + std::vector<QString> TransformFactory::getAllTransformTypes() { @@ -217,7 +232,7 @@ } Vamp::Plugin *plugin = - factory->instantiatePlugin(pluginId, 48000); + factory->instantiatePlugin(pluginId, 44100); if (!plugin) { std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl; @@ -273,7 +288,7 @@ bool configurable = (!plugin->getPrograms().empty() || !plugin->getParameterDescriptors().empty()); -// std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl; +// std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << " friendly name: " << friendlyName.toStdString() << std::endl; transforms[transformId] = TransformDescription(tr("Analysis"), @@ -423,6 +438,83 @@ } } + +Transform +TransformFactory::getDefaultTransformFor(TransformId id, size_t rate) +{ + Transform t; + t.setIdentifier(id); + if (rate != 0) t.setSampleRate(rate); + + Vamp::PluginBase *plugin = instantiateDefaultPluginFor(id, rate); + + if (plugin) { + t.setPluginVersion(QString("%1").arg(plugin->getPluginVersion())); + setParametersFromPlugin(t, plugin); + makeContextConsistentWithPlugin(t, plugin); + delete plugin; + } + + return t; +} + +Vamp::PluginBase * +TransformFactory::instantiatePluginFor(const Transform &transform) +{ + Vamp::PluginBase *plugin = instantiateDefaultPluginFor + (transform.getIdentifier(), transform.getSampleRate()); + if (plugin) { + setPluginParameters(transform, plugin); + } + return plugin; +} + +Vamp::PluginBase * +TransformFactory::instantiateDefaultPluginFor(TransformId identifier, size_t rate) +{ + Transform t; + t.setIdentifier(identifier); + if (rate == 0) rate = 44100; + QString pluginId = t.getPluginIdentifier(); + + Vamp::PluginBase *plugin = 0; + + if (t.getType() == Transform::FeatureExtraction) { + + FeatureExtractionPluginFactory *factory = + FeatureExtractionPluginFactory::instanceFor(pluginId); + + plugin = factory->instantiatePlugin(pluginId, rate); + + } else { + + RealTimePluginFactory *factory = + RealTimePluginFactory::instanceFor(pluginId); + + plugin = factory->instantiatePlugin(pluginId, 0, 0, rate, 1024, 1); + } + + return plugin; +} + +Vamp::Plugin * +TransformFactory::downcastVampPlugin(Vamp::PluginBase *plugin) +{ + Vamp::Plugin *vp = dynamic_cast<Vamp::Plugin *>(plugin); + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; + vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin); //!!! why? +} + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; + vp = dynamic_cast<Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why? + } + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; + } + return vp; +} + bool TransformFactory::haveTransform(TransformId identifier) { @@ -454,6 +546,28 @@ } else return ""; } +Vamp::Plugin::InputDomain +TransformFactory::getTransformInputDomain(TransformId identifier) +{ + Transform transform; + transform.setIdentifier(identifier); + + if (transform.getType() != Transform::FeatureExtraction) { + return Vamp::Plugin::TimeDomain; + } + + Vamp::Plugin *plugin = + downcastVampPlugin(instantiateDefaultPluginFor(identifier, 0)); + + if (plugin) { + Vamp::Plugin::InputDomain d = plugin->getInputDomain(); + delete plugin; + return d; + } + + return Vamp::Plugin::TimeDomain; +} + bool TransformFactory::isTransformConfigurable(TransformId identifier) { @@ -472,7 +586,7 @@ Vamp::Plugin *plugin = FeatureExtractionPluginFactory::instanceFor(id)-> - instantiatePlugin(id, 48000); + instantiatePlugin(id, 44100); if (!plugin) return false; min = plugin->getMinChannelCount(); @@ -483,6 +597,8 @@ } else if (RealTimePluginFactory::instanceFor(id)) { + // don't need to instantiate + const RealTimePluginDescriptor *descriptor = RealTimePluginFactory::instanceFor(id)-> getPluginDescriptor(id); @@ -503,6 +619,10 @@ { Transform::ParameterMap pmap; + //!!! record plugin & API version + + //!!! check that this is the right plugin! + Vamp::PluginBase::ParameterList parameters = plugin->getParameterDescriptors(); @@ -539,21 +659,48 @@ } void +TransformFactory::setPluginParameters(const Transform &transform, + Vamp::PluginBase *plugin) +{ + //!!! check plugin & API version (see e.g. PluginXml::setParameters) + + //!!! check that this is the right plugin! + + RealTimePluginInstance *rtpi = + dynamic_cast<RealTimePluginInstance *>(plugin); + + if (rtpi) { + const Transform::ConfigurationMap &cmap = transform.getConfiguration(); + for (Transform::ConfigurationMap::const_iterator i = cmap.begin(); + i != cmap.end(); ++i) { + rtpi->configure(i->first.toStdString(), i->second.toStdString()); + } + } + + if (transform.getProgram() != "") { + plugin->selectProgram(transform.getProgram().toStdString()); + } + + const Transform::ParameterMap &pmap = transform.getParameters(); + + Vamp::PluginBase::ParameterList parameters = + plugin->getParameterDescriptors(); + + for (Vamp::PluginBase::ParameterList::const_iterator i = parameters.begin(); + i != parameters.end(); ++i) { + QString key = i->identifier.c_str(); + Transform::ParameterMap::const_iterator pmi = pmap.find(key); + if (pmi != pmap.end()) { + plugin->setParameter(i->identifier, pmi->second); + } + } +} + +void TransformFactory::makeContextConsistentWithPlugin(Transform &transform, Vamp::PluginBase *plugin) { - const Vamp::Plugin *vp = dynamic_cast<const Vamp::Plugin *>(plugin); - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; - vp = dynamic_cast<const Vamp::PluginHostAdapter *>(plugin); //!!! why? -} - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; - vp = dynamic_cast<const Vamp::HostExt::PluginWrapper *>(plugin); //!!! no, I mean really why? - } - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; - } + const Vamp::Plugin *vp = downcastVampPlugin(plugin); if (!vp) { // time domain input for real-time effects plugin @@ -586,49 +733,44 @@ } } -Transform -TransformFactory::getDefaultTransformFor(TransformId id, size_t rate) +QString +TransformFactory::getPluginConfigurationXml(const Transform &t) { - Transform t; - t.setIdentifier(id); - - if (rate == 0) { - rate = 44100; - } else { - t.setSampleRate(rate); + QString xml; + + Vamp::PluginBase *plugin = instantiateDefaultPluginFor + (t.getIdentifier(), 0); + if (!plugin) { + std::cerr << "TransformFactory::getPluginConfigurationXml: " + << "Unable to instantiate plugin for transform \"" + << t.getIdentifier().toStdString() << "\"" << std::endl; + return xml; } - QString pluginId = t.getPluginIdentifier(); + setPluginParameters(t, plugin); - if (t.getType() == Transform::FeatureExtraction) { + QTextStream out(&xml); + PluginXml(plugin).toXml(out); + delete plugin; - FeatureExtractionPluginFactory *factory = - FeatureExtractionPluginFactory::instanceFor(pluginId); + return xml; +} - Vamp::Plugin *plugin = factory->instantiatePlugin - (pluginId, rate); - - if (plugin) { - setParametersFromPlugin(t, plugin); - makeContextConsistentWithPlugin(t, plugin); - delete plugin; - } - - } else { - - RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); - - RealTimePluginInstance *plugin = factory->instantiatePlugin - (pluginId, 0, 0, rate, 1024, 1); - - if (plugin) { - setParametersFromPlugin(t, plugin); - makeContextConsistentWithPlugin(t, plugin); - delete plugin; - } +void +TransformFactory::setParametersFromPluginConfigurationXml(Transform &t, + QString xml) +{ + Vamp::PluginBase *plugin = instantiateDefaultPluginFor + (t.getIdentifier(), 0); + if (!plugin) { + std::cerr << "TransformFactory::setParametersFromPluginConfigurationXml: " + << "Unable to instantiate plugin for transform \"" + << t.getIdentifier().toStdString() << "\"" << std::endl; + return; } - return t; + PluginXml(plugin).setParametersFromXml(xml); + setParametersFromPlugin(t, plugin); + delete plugin; }
--- a/plugin/transform/TransformFactory.h Fri Nov 30 17:36:14 2007 +0000 +++ b/plugin/transform/TransformFactory.h Wed Feb 27 10:32:45 2008 +0000 @@ -18,11 +18,11 @@ #include "TransformDescription.h" +#include <vamp-sdk/Plugin.h> + #include <map> #include <set> -namespace Vamp { class PluginBase; } - class TransformFactory : public QObject { Q_OBJECT @@ -32,10 +32,10 @@ static TransformFactory *getInstance(); - TransformList getAllTransforms(); + TransformList getAllTransformDescriptions(); + TransformDescription getTransformDescription(TransformId id); std::vector<QString> getAllTransformTypes(); - std::vector<QString> getTransformCategories(QString transformType); std::vector<QString> getTransformMakers(QString transformType); @@ -45,6 +45,13 @@ bool haveTransform(TransformId identifier); /** + * A single transform ID can lead to many possible Transforms, + * with different parameters and execution context settings. + * Return the default one for the given transform. + */ + Transform getDefaultTransformFor(TransformId identifier, size_t rate = 0); + + /** * Full name of a transform, suitable for putting on a menu. */ QString getTransformName(TransformId identifier); @@ -57,6 +64,8 @@ QString getTransformUnits(TransformId identifier); + Vamp::Plugin::InputDomain getTransformInputDomain(TransformId identifier); + /** * Return true if the transform has any configurable parameters, * i.e. if getConfigurationForTransform can ever return a non-trivial @@ -74,6 +83,35 @@ int &minChannels, int &maxChannels); /** + * Load an appropriate plugin for the given transform and set the + * parameters, program and configuration strings on that plugin + * from the Transform object. + * + * Note that this requires that the transform has a meaningful + * sample rate set, as that is used as the rate for the plugin. A + * Transform can legitimately have rate set at zero (= "use the + * rate of the input source"), so the caller will need to test for + * this case. + * + * Returns the plugin thus loaded. This will be a + * Vamp::PluginBase, but not necessarily a Vamp::Plugin (only if + * the transform was a feature-extraction type -- call + * downcastVampPlugin if you only want Vamp::Plugins). Returns + * NULL if no suitable plugin was available. + * + * The returned plugin is owned by the caller, and should be + * deleted (using "delete") when no longer needed. + */ + Vamp::PluginBase *instantiatePluginFor(const Transform &transform); + + /** + * Convert a Vamp::PluginBase to a Vamp::Plugin, if it is one. + * Return NULL otherwise. This ill-fitting convenience function + * is really just a dynamic_cast wrapper. + */ + Vamp::Plugin *downcastVampPlugin(Vamp::PluginBase *); + + /** * Set the plugin parameters, program and configuration strings on * the given Transform object from the given plugin instance. * Note that no check is made whether the plugin is actually the @@ -82,6 +120,12 @@ void setParametersFromPlugin(Transform &transform, Vamp::PluginBase *plugin); /** + * Set the parameters, program and configuration strings on the + * given plugin from the given Transform object. + */ + void setPluginParameters(const Transform &transform, Vamp::PluginBase *plugin); + + /** * If the given Transform object has no processing step and block * sizes set, set them to appropriate defaults for the given * plugin. @@ -89,11 +133,27 @@ void makeContextConsistentWithPlugin(Transform &transform, Vamp::PluginBase *plugin); /** - * A single transform ID can lead to many possible Transforms, - * with different parameters and execution context settings. - * Return the default one for the given transform. + * Retrieve a <plugin ... /> XML fragment that describes the + * plugin parameters, program and configuration data for the given + * transform. + * + * This function is provided for backward compatibility only. Use + * Transform::toXml where compatibility with PluginXml + * descriptions of transforms is not required. */ - Transform getDefaultTransformFor(TransformId identifier, size_t rate = 0); + QString getPluginConfigurationXml(const Transform &transform); + + /** + * Set the plugin parameters, program and configuration strings on + * the given Transform object from the given <plugin ... /> XML + * fragment. + * + * This function is provided for backward compatibility only. Use + * Transform(QString) where compatibility with PluginXml + * descriptions of transforms is not required. + */ + void setParametersFromPluginConfigurationXml(Transform &transform, + QString xml); protected: typedef std::map<TransformId, TransformDescription> TransformDescriptionMap; @@ -103,6 +163,8 @@ void populateFeatureExtractionPlugins(TransformDescriptionMap &); void populateRealTimePlugins(TransformDescriptionMap &); + Vamp::PluginBase *instantiateDefaultPluginFor(TransformId id, size_t rate); + static TransformFactory *m_instance; };
--- a/system/System.h Fri Nov 30 17:36:14 2007 +0000 +++ b/system/System.h Wed Feb 27 10:32:45 2008 +0000 @@ -25,6 +25,7 @@ #define MLOCK(a,b) 1 #define MUNLOCK(a,b) 1 #define MUNLOCK_SAMPLEBLOCK(a) 1 +#define MUNLOCKALL() 1 #define DLOPEN(a,b) LoadLibrary((a).toStdWString().c_str()) #define DLSYM(a,b) GetProcAddress((HINSTANCE)(a),(b)) @@ -82,6 +83,8 @@ #define DEFAULT_LADSPA_PATH "$HOME/Library/Audio/Plug-Ins/LADSPA:/Library/Audio/Plug-Ins/LADSPA" #define DEFAULT_DSSI_PATH "$HOME/Library/Audio/Plug-Ins/DSSI:/Library/Audio/Plug-Ins/DSSI" +#define MUNLOCKALL() 1 + #else #define PLUGIN_GLOB "*.so" @@ -90,6 +93,8 @@ #define DEFAULT_LADSPA_PATH "$HOME/ladspa:$HOME/.ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa" #define DEFAULT_DSSI_PATH "$HOME/dssi:$HOME/.dssi:/usr/local/lib/dssi:/usr/lib/dssi" +#define MUNLOCKALL() ::munlockall() + #endif /* __APPLE__ */ #endif /* ! _WIN32 */