# HG changeset patch # User Chris Cannam # Date 1204113582 0 # Node ID 6f6ab834449d6c0ce9a0794e638e6e0c06f4231f # Parent a6fab10ff9e6bcf304fed57e29f188f4368b52d8 * Merge from trunk diff -r a6fab10ff9e6 -r 6f6ab834449d base/Clipboard.cpp --- a/base/Clipboard.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Clipboard.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -21,7 +21,11 @@ m_haveValue(false), m_haveDuration(false), m_haveLabel(true), - m_label(label) + m_label(label), + m_haveLevel(false), + m_level(0.f), + m_haveReferenceFrame(false), + m_referenceFrame(frame) { } @@ -32,7 +36,11 @@ m_value(value), m_haveDuration(false), m_haveLabel(true), - m_label(label) + m_label(label), + m_haveLevel(false), + m_level(0.f), + m_haveReferenceFrame(false), + m_referenceFrame(frame) { } @@ -44,7 +52,27 @@ m_haveDuration(true), m_duration(duration), m_haveLabel(true), - m_label(label) + m_label(label), + m_haveLevel(false), + m_level(0.f), + m_haveReferenceFrame(false), + m_referenceFrame(frame) +{ +} + +Clipboard::Point::Point(long frame, float value, size_t duration, float level, QString label) : + m_haveFrame(true), + m_frame(frame), + m_haveValue(true), + m_value(value), + m_haveDuration(true), + m_duration(duration), + m_haveLabel(true), + m_label(label), + m_haveLevel(true), + m_level(level), + m_haveReferenceFrame(false), + m_referenceFrame(frame) { } @@ -56,7 +84,11 @@ m_haveDuration(point.m_haveDuration), m_duration(point.m_duration), m_haveLabel(point.m_haveLabel), - m_label(point.m_label) + m_label(point.m_label), + m_haveLevel(point.m_haveLevel), + m_level(point.m_level), + m_haveReferenceFrame(point.m_haveReferenceFrame), + m_referenceFrame(point.m_referenceFrame) { } @@ -72,6 +104,10 @@ m_duration = point.m_duration; m_haveLabel = point.m_haveLabel; m_label = point.m_label; + m_haveLevel = point.m_haveLevel; + m_level = point.m_level; + m_haveReferenceFrame = point.m_haveReferenceFrame; + m_referenceFrame = point.m_referenceFrame; return *this; } @@ -123,6 +159,43 @@ return m_label; } +bool +Clipboard::Point::haveLevel() const +{ + return m_haveLevel; +} + +float +Clipboard::Point::getLevel() const +{ + return m_level; +} + +bool +Clipboard::Point::haveReferenceFrame() const +{ + return m_haveReferenceFrame; +} + +bool +Clipboard::Point::referenceFrameDiffers() const +{ + return m_haveReferenceFrame && (m_referenceFrame != m_frame); +} + +long +Clipboard::Point::getReferenceFrame() const +{ + return m_referenceFrame; +} + +void +Clipboard::Point::setReferenceFrame(long f) +{ + m_haveReferenceFrame = true; + m_referenceFrame = f; +} + Clipboard::Clipboard() { } Clipboard::~Clipboard() { } @@ -156,3 +229,23 @@ m_points.push_back(point); } +bool +Clipboard::haveReferenceFrames() const +{ + for (PointList::const_iterator i = m_points.begin(); + i != m_points.end(); ++i) { + if (i->haveReferenceFrame()) return true; + } + 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; +} + diff -r a6fab10ff9e6 -r 6f6ab834449d base/Clipboard.h --- a/base/Clipboard.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Clipboard.h Wed Feb 27 11:59:42 2008 +0000 @@ -28,6 +28,7 @@ Point(long frame, QString label); Point(long frame, float value, QString label); Point(long frame, float value, size_t duration, QString label); + Point(long frame, float value, size_t duration, float level, QString label); Point(const Point &point); Point &operator=(const Point &point); @@ -43,6 +44,15 @@ bool haveLabel() const; QString getLabel() const; + bool haveLevel() const; + float getLevel() const; + + bool haveReferenceFrame() const; + bool referenceFrameDiffers() const; // from point frame + + long getReferenceFrame() const; + void setReferenceFrame(long); + private: bool m_haveFrame; long m_frame; @@ -52,6 +62,10 @@ size_t m_duration; bool m_haveLabel; QString m_label; + bool m_haveLevel; + float m_level; + bool m_haveReferenceFrame; + long m_referenceFrame; }; Clipboard(); @@ -65,6 +79,9 @@ void setPoints(const PointList &points); void addPoint(const Point &point); + bool haveReferenceFrames() const; + bool referenceFramesDiffer() const; + protected: PointList m_points; }; diff -r a6fab10ff9e6 -r 6f6ab834449d base/CommandHistory.cpp --- a/base/CommandHistory.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/CommandHistory.cpp Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d base/Pitch.cpp --- a/base/Pitch.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Pitch.cpp Wed Feb 27 11:59:42 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) diff -r a6fab10ff9e6 -r 6f6ab834449d base/Pitch.h --- a/base/Pitch.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Pitch.h Wed Feb 27 11:59:42 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 diff -r a6fab10ff9e6 -r 6f6ab834449d base/Preferences.cpp --- a/base/Preferences.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Preferences.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -23,6 +23,8 @@ #include #include #include +#include +#include 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"); + } +} + diff -r a6fab10ff9e6 -r 6f6ab834449d base/Preferences.h --- a/base/Preferences.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Preferences.h Wed Feb 27 11:59:42 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 diff -r a6fab10ff9e6 -r 6f6ab834449d base/PropertyContainer.cpp --- a/base/PropertyContainer.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/PropertyContainer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -29,12 +29,6 @@ return PropertyList(); } -//QString -//PropertyContainer::getPropertyLabel(const PropertyName &) const -//{ -// return ""; -//} - PropertyContainer::PropertyType PropertyContainer::getPropertyType(const PropertyName &) const { @@ -42,6 +36,12 @@ } QString +PropertyContainer::getPropertyIconName(const PropertyName &) const +{ + return QString(); +} + +QString PropertyContainer::getPropertyGroupName(const PropertyName &) const { return QString(); diff -r a6fab10ff9e6 -r 6f6ab834449d base/PropertyContainer.h --- a/base/PropertyContainer.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/PropertyContainer.h Wed Feb 27 11:59:42 2008 +0000 @@ -62,6 +62,11 @@ virtual PropertyType getPropertyType(const PropertyName &) const; /** + * Return an icon for the property, if any. + */ + virtual QString getPropertyIconName(const PropertyName &) const; + + /** * If this property has something in common with other properties * on this container, return a name that can be used to group them * (in order to save screen space, for example). e.g. "Window diff -r a6fab10ff9e6 -r 6f6ab834449d base/RangeMapper.cpp --- a/base/RangeMapper.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/RangeMapper.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -60,29 +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 { - float mapped = m_ratio * log10(value); - int position = lrintf(((mapped - m_minlog) / (m_maxlog - m_minlog)) - * (m_maxpos - m_minpos)); + 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; } @@ -90,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; } diff -r a6fab10ff9e6 -r 6f6ab834449d base/RangeMapper.h --- a/base/RangeMapper.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/RangeMapper.h Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d base/RealTime.cpp --- a/base/RealTime.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/RealTime.cpp Wed Feb 27 11:59:42 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 { diff -r a6fab10ff9e6 -r 6f6ab834449d base/RealTime.h --- a/base/RealTime.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/RealTime.h Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d base/StorageAdviser.cpp --- a/base/StorageAdviser.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/StorageAdviser.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -22,6 +22,8 @@ #include +//#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 @@ -86,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; diff -r a6fab10ff9e6 -r 6f6ab834449d base/Window.h --- a/base/Window.h Thu Nov 15 14:03:56 2007 +0000 +++ b/base/Window.h Wed Feb 27 11:59:42 2008 +0000 @@ -18,6 +18,7 @@ #include #include +#include #include 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 +std::string +Window::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 +WindowType +Window::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 diff -r a6fab10ff9e6 -r 6f6ab834449d base/XmlExportable.cpp --- a/base/XmlExportable.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/base/XmlExportable.cpp Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d data/data.pro --- a/data/data.pro Thu Nov 15 14:03:56 2007 +0000 +++ b/data/data.pro Wed Feb 27 11:59:42 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 \ diff -r a6fab10ff9e6 -r 6f6ab834449d data/fft/FFTDataServer.cpp --- a/data/fft/FFTDataServer.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fft/FFTDataServer.cpp Wed Feb 27 11:59:42 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) { @@ -726,9 +727,9 @@ compactCache = canCompact && (recommendation & StorageAdviser::ConserveSpace); +#ifdef DEBUG_FFT_SERVER std::cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << std::endl; -#ifdef DEBUG_FFT_SERVER std::cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << std::endl; #endif } diff -r a6fab10ff9e6 -r 6f6ab834449d data/fft/FFTFileCache.cpp --- a/data/fft/FFTFileCache.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fft/FFTFileCache.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -23,6 +23,7 @@ #include + // 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 diff -r a6fab10ff9e6 -r 6f6ab834449d data/fft/FFTMemoryCache.cpp --- a/data/fft/FFTMemoryCache.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fft/FFTMemoryCache.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -18,6 +18,8 @@ #include +//#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) { +#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 diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/AudioFileReader.h --- a/data/fileio/AudioFileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/AudioFileReader.h Wed Feb 27 11:59:42 2008 +0000 @@ -39,6 +39,12 @@ size_t getChannelCount() const { return m_channelCount; } size_t getSampleRate() const { return m_sampleRate; } size_t getNativeRate() const { return m_sampleRate; } // if resampled + + /** + * Return the location of the audio data in the reader (as passed + * in to the FileSource constructor, for example). + */ + virtual QString getLocation() const { return ""; } /** * Return the title of the work in the audio file, if known. This diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/CodedAudioFileReader.cpp --- a/data/fileio/CodedAudioFileReader.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.cpp Wed Feb 27 11:59:42 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); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/CodedAudioFileReader.h --- a/data/fileio/CodedAudioFileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.h Wed Feb 27 11:59:42 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); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/FileFinder.cpp --- a/data/fileio/FileFinder.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/FileFinder.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -378,15 +378,15 @@ QString FileFinder::find(FileType type, QString location, QString lastKnownLocation) { - if (QFileInfo(location).exists()) return location; - - if (FileSource::isRemote(location)) { + if (FileSource::canHandleScheme(location)) { if (FileSource(location).isAvailable()) { std::cerr << "FileFinder::find: ok, it's available... returning" << std::endl; return location; } } + if (QFileInfo(location).exists()) return location; + QString foundAt = ""; if ((foundAt = findRelative(location, lastKnownLocation)) != "") { @@ -415,6 +415,9 @@ fileName = QUrl(location).path().section('/', -1, -1, QString::SectionSkipEmpty); } else { + if (QUrl(location).scheme() == "file") { + location = QUrl(location).toLocalFile(); + } fileName = QFileInfo(location).fileName(); } @@ -423,6 +426,9 @@ if (!FileSource(resolved).isAvailable()) resolved = ""; std::cerr << "resolved: " << resolved.toStdString() << std::endl; } else { + if (QUrl(relativeTo).scheme() == "file") { + relativeTo = QUrl(relativeTo).toLocalFile(); + } resolved = QFileInfo(relativeTo).dir().filePath(fileName); if (!QFileInfo(resolved).exists() || !QFileInfo(resolved).isFile() || diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/FileSource.cpp --- a/data/fileio/FileSource.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/FileSource.cpp Wed Feb 27 11:59:42 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 -//#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,16 +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; } @@ -238,6 +270,7 @@ if (scheme == "http") { initHttp(); + std::cerr << "FileSource: initHttp succeeded" << std::endl; } else if (scheme == "ftp") { initFtp(); } else { @@ -271,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; } } } @@ -396,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; } @@ -403,16 +452,19 @@ bool FileSource::isRemote(QString fileOrUrl) { + // Note that a "scheme" with length 1 is probably a DOS drive letter QString scheme = QUrl(fileOrUrl).scheme().toLower(); - return (scheme == "http" || scheme == "ftp"); + if (scheme == "" || scheme == "file" || scheme.length() == 1) return false; + return true; } bool FileSource::canHandleScheme(QUrl url) { + // Note that a "scheme" with length 1 is probably a DOS drive letter QString scheme = url.scheme().toLower(); return (scheme == "http" || scheme == "ftp" || - scheme == "file" || scheme == ""); + scheme == "file" || scheme == "" || scheme.length() == 1); } bool @@ -442,6 +494,7 @@ FileSource::waitForData() { while (m_ok && !m_done) { +// std::cerr << "FileSource::waitForData: calling QApplication::processEvents" << std::endl; QApplication::processEvents(); } } @@ -795,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; -} - diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/FileSource.h --- a/data/fileio/FileSource.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/FileSource.h Wed Feb 27 11:59:42 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 diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/MIDIFileReader.cpp --- a/data/fileio/MIDIFileReader.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/MIDIFileReader.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -1021,8 +1021,10 @@ QString noteLabel = tr("%1 - vel %2") .arg(pitchLabel).arg(int((*i)->getVelocity())); + float level = float((*i)->getVelocity()) / 128.f; + Note note(startFrame, (*i)->getPitch(), - endFrame - startFrame, noteLabel); + endFrame - startFrame, level, noteLabel); // std::cerr << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << std::endl; diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/MIDIFileWriter.cpp --- a/data/fileio/MIDIFileWriter.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/MIDIFileWriter.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -369,8 +369,10 @@ double quarters = (seconds * m_tempo) / 60.0; unsigned long midiTime = lrint(quarters * m_timingDivision); - // We don't support velocity in note models yet int velocity = 100; + if (i->level > 0.f && i->level <= 1.f) { + velocity = lrintf(i->level * 127.f); + } // Get the sounding time for the matching NOTE_OFF seconds = double(frame + duration) / double(m_model->getSampleRate()); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/MP3FileReader.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -17,6 +17,8 @@ #ifdef HAVE_MAD #include "MP3FileReader.h" +#include "ProgressPrinter.h" + #include "system/System.h" #include @@ -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); } } @@ -206,7 +211,7 @@ unsigned int nstrings = id3_field_getnstrings(&frame->fields[1]); if (nstrings == 0) { #ifdef DEBUG_ID3TAG - std::cerr << "MP3FileReader::loadTags: No data for \"" << name << "\" in ID3 tag" << std::endl; + std::cerr << "MP3FileReader::loadTags: No strings for \"" << name << "\" in ID3 tag" << std::endl; #endif return ""; } @@ -291,7 +296,22 @@ DecoderData *data = (DecoderData *)dp; if (!data->length) return MAD_FLOW_STOP; - mad_stream_buffer(stream, data->start, data->length); + + unsigned char const *start = data->start; + unsigned long length = data->length; + +#ifdef HAVE_ID3TAG + if (length > ID3_TAG_QUERYSIZE) { + int taglen = id3_tag_query(start, ID3_TAG_QUERYSIZE); + if (taglen > 0) { +// std::cerr << "ID3 tag length to skip: " << taglen << std::endl; + start += taglen; + length -= taglen; + } + } +#endif + + mad_stream_buffer(stream, start, length); data->length = 0; return MAD_FLOW_CONTINUE; @@ -328,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"); } @@ -338,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; + } } } } diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/MP3FileReader.h --- a/data/fileio/MP3FileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/MP3FileReader.h Wed Feb 27 11:59:42 2008 +0000 @@ -43,6 +43,7 @@ virtual QString getError() const { return m_error; } + virtual QString getLocation() const { return m_source.getLocation(); } virtual QString getTitle() const { return m_title; } virtual QString getMaker() const { return m_maker; } diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/OggVorbisFileReader.cpp --- a/data/fileio/OggVorbisFileReader.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Wed Feb 27 11:59:42 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(); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/OggVorbisFileReader.h --- a/data/fileio/OggVorbisFileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.h Wed Feb 27 11:59:42 2008 +0000 @@ -45,6 +45,7 @@ virtual QString getError() const { return m_error; } + virtual QString getLocation() const { return m_source.getLocation(); } virtual QString getTitle() const { return m_title; } virtual QString getMaker() const { return m_maker; } diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/ProgressPrinter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/ProgressPrinter.cpp Wed Feb 27 11:59:42 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 + +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; +} + diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/ProgressPrinter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/ProgressPrinter.h Wed Feb 27 11:59:42 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 +#include + +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 diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/QuickTimeFileReader.cpp --- a/data/fileio/QuickTimeFileReader.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/QuickTimeFileReader.cpp Wed Feb 27 11:59:42 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 diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/QuickTimeFileReader.h --- a/data/fileio/QuickTimeFileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/QuickTimeFileReader.h Wed Feb 27 11:59:42 2008 +0000 @@ -44,6 +44,7 @@ virtual ~QuickTimeFileReader(); virtual QString getError() const { return m_error; } + virtual QString getLocation() const { return m_source.getLocation(); } virtual QString getTitle() const { return m_title; } static void getSupportedExtensions(std::set &extensions); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/ResamplingWavFileReader.h --- a/data/fileio/ResamplingWavFileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/ResamplingWavFileReader.h Wed Feb 27 11:59:42 2008 +0000 @@ -40,6 +40,7 @@ virtual ~ResamplingWavFileReader(); virtual QString getError() const { return m_error; } + virtual QString getLocation() const { return m_source.getLocation(); } static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); static bool supportsContentType(QString type); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/WavFileReader.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -111,6 +111,7 @@ { if (count == 0) return; results.clear(); + results.reserve(count * m_fileInfo.channels); QMutexLocker locker(&m_mutex); diff -r a6fab10ff9e6 -r 6f6ab834449d data/fileio/WavFileReader.h --- a/data/fileio/WavFileReader.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/fileio/WavFileReader.h Wed Feb 27 11:59:42 2008 +0000 @@ -29,6 +29,7 @@ WavFileReader(FileSource source, bool fileUpdating = false); virtual ~WavFileReader(); + virtual QString getLocation() const { return m_source.getLocation(); } virtual QString getError() const { return m_error; } /** diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/AggregateWaveModel.cpp --- a/data/model/AggregateWaveModel.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/AggregateWaveModel.cpp Wed Feb 27 11:59:42 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, diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/AggregateWaveModel.h --- a/data/model/AggregateWaveModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/AggregateWaveModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -42,6 +42,8 @@ bool isOK() const; bool isReady(int *) const; + QString getTypeName() const { return tr("Aggregate Wave"); } + size_t getComponentCount() const; ModelChannelSpec getComponent(size_t c) const; @@ -65,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; diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/AlignmentModel.cpp --- a/data/model/AlignmentModel.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/AlignmentModel.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -17,6 +17,8 @@ #include "SparseTimeValueModel.h" +//#define DEBUG_ALIGNMENT_MODEL 1 + AlignmentModel::AlignmentModel(Model *reference, Model *aligned, Model *inputModel, @@ -24,26 +26,32 @@ m_reference(reference), m_aligned(aligned), m_inputModel(inputModel), - m_path(path), + m_rawPath(path), + m_path(0), m_reversePath(0), m_pathBegun(false), m_pathComplete(false) { - connect(m_path, SIGNAL(modelChanged()), - this, SLOT(pathChanged())); + if (m_rawPath) { - connect(m_path, SIGNAL(modelChanged(size_t, size_t)), - this, SLOT(pathChanged(size_t, size_t))); + connect(m_rawPath, SIGNAL(modelChanged()), + this, SLOT(pathChanged())); - connect(m_path, 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(); } AlignmentModel::~AlignmentModel() { delete m_inputModel; + delete m_rawPath; delete m_path; delete m_reversePath; } @@ -51,13 +59,13 @@ bool AlignmentModel::isOK() const { - return m_path->isOK(); + if (m_rawPath) return m_rawPath->isOK(); + else return true; } size_t AlignmentModel::getStartFrame() const { - //!!! do we care about distinct rates? size_t a = m_reference->getStartFrame(); size_t b = m_aligned->getStartFrame(); return std::min(a, b); @@ -66,7 +74,6 @@ size_t AlignmentModel::getEndFrame() const { - //!!! do we care about distinct rates? size_t a = m_reference->getEndFrame(); size_t b = m_aligned->getEndFrame(); return std::max(a, b); @@ -84,23 +91,27 @@ return new AlignmentModel (m_reference, m_aligned, m_inputModel ? m_inputModel->clone() : 0, - m_path ? static_cast(m_path->clone()) : 0); + m_rawPath ? static_cast(m_rawPath->clone()) : 0); } bool AlignmentModel::isReady(int *completion) const { if (!m_pathBegun) { - completion = 0; + if (completion) *completion = 0; return false; } - return m_path->isReady(completion); + if (m_pathComplete || !m_rawPath) { + if (completion) *completion = 100; + return true; + } + return m_rawPath->isReady(completion); } const ZoomConstraint * AlignmentModel::getZoomConstraint() const { - return m_path->getZoomConstraint(); + return 0; } const Model * @@ -118,43 +129,70 @@ size_t AlignmentModel::toReference(size_t frame) const { -// std::cerr << "AlignmentModel::toReference(" << frame << ")" << std::endl; - if (!m_reversePath) constructReversePath(); - return align(m_reversePath, frame); +#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; - return align(m_path, frame); +#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); } void AlignmentModel::pathChanged() { + if (m_pathComplete) { + std::cerr << "AlignmentModel: deleting raw path model" << std::endl; + delete m_rawPath; + m_rawPath = 0; + } } void AlignmentModel::pathChanged(size_t, size_t) { if (!m_pathComplete) return; + constructPath(); constructReversePath(); } void AlignmentModel::pathCompletionChanged() { + if (!m_rawPath) return; m_pathBegun = true; if (!m_pathComplete) { + int completion = 0; - m_path->isReady(&completion); -// std::cerr << "AlignmentModel::pathCompletionChanged: completion = " -// << completion << std::endl; + m_rawPath->isReady(&completion); + +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::pathCompletionChanged: completion = " + << completion << std::endl; +#endif + m_pathComplete = (completion == 100); + if (m_pathComplete) { + + constructPath(); constructReversePath(); + delete m_inputModel; m_inputModel = 0; } @@ -164,78 +202,134 @@ } void -AlignmentModel::constructReversePath() const +AlignmentModel::constructPath() const { - if (!m_reversePath) { - m_reversePath = new SparseTimeValueModel - (m_path->getSampleRate(), m_path->getResolution(), false); + if (!m_path) { + if (!m_rawPath) { + std::cerr << "ERROR: AlignmentModel::constructPath: " + << "No raw path available" << std::endl; + return; + } + m_path = new PathModel + (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false); + } else { + if (!m_rawPath) return; } - m_reversePath->clear(); + m_path->clear(); - SparseTimeValueModel::PointList points = m_path->getPoints(); + SparseTimeValueModel::PointList points = m_rawPath->getPoints(); for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); i != points.end(); ++i) { long frame = i->frame; float value = i->value; long rframe = lrintf(value * m_aligned->getSampleRate()); - float rvalue = (float)frame / (float)m_reference->getSampleRate(); - m_reversePath->addPoint - (SparseTimeValueModel::Point(rframe, rvalue, "")); + m_path->addPoint(PathPoint(frame, rframe)); } - std::cerr << "AlignmentModel::constructReversePath: " << m_reversePath->getPointCount() << " points, at least " << (2 * m_reversePath->getPointCount() * (3 * sizeof(void *) + sizeof(int) + sizeof(SparseTimeValueModel::Point))) << " 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 +AlignmentModel::constructReversePath() const +{ + if (!m_reversePath) { + if (!m_rawPath) { + std::cerr << "ERROR: AlignmentModel::constructReversePath: " + << "No raw path available" << std::endl; + return; + } + m_reversePath = new PathModel + (m_rawPath->getSampleRate(), m_rawPath->getResolution(), false); + } else { + if (!m_rawPath) return; + } + + m_reversePath->clear(); + + SparseTimeValueModel::PointList points = m_rawPath->getPoints(); + + for (SparseTimeValueModel::PointList::const_iterator i = points.begin(); + i != points.end(); ++i) { + long frame = i->frame; + float value = i->value; + long rframe = lrintf(value * m_aligned->getSampleRate()); + m_reversePath->addPoint(PathPoint(rframe, frame)); + } + +#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 -AlignmentModel::align(SparseTimeValueModel *path, size_t frame) const +AlignmentModel::align(PathModel *path, size_t frame) const { - // The path consists of a series of points, each with x (time) - // equal to the time on the source model and y (value) equal to - // the time on the target model. Times and values are both - // monotonically increasing. + if (!path) return frame; - const SparseTimeValueModel::PointList &points = path->getPoints(); + // The path consists of a series of points, each with frame equal + // to the frame on the source model and mapframe equal to the + // frame on the target model. Both should be monotonically + // increasing. + + 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; } - SparseTimeValueModel::Point point(frame); - SparseTimeValueModel::PointList::const_iterator i = points.lower_bound(point); - if (i == points.end()) --i; - while (i != points.begin() && i->frame > frame) --i; +#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()) { +#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; - float foundTime = i->value; + long foundMapFrame = i->mapframe; long followingFrame = foundFrame; - float followingTime = foundTime; + long followingMapFrame = foundMapFrame; if (++i != points.end()) { +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "another point available" << std::endl; +#endif followingFrame = i->frame; - followingTime = i->value; + followingMapFrame = i->mapframe; + } else { +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "no other point available" << std::endl; +#endif + } + + if (foundMapFrame < 0) return 0; + + size_t resultFrame = foundMapFrame; + + if (followingFrame != foundFrame && long(frame) > foundFrame) { + float interp = + float(frame - foundFrame) / + float(followingFrame - foundFrame); + resultFrame += lrintf((followingMapFrame - foundMapFrame) * interp); } - float resultTime = foundTime; - - if (followingFrame != foundFrame && frame > foundFrame) { - -// std::cerr << "AlignmentModel::align: foundFrame = " << foundFrame << ", frame = " << frame << ", followingFrame = " << followingFrame << std::endl; - - float interp = float(frame - foundFrame) / float(followingFrame - foundFrame); -// std::cerr << "AlignmentModel::align: interp = " << interp << ", result " << resultTime << " -> "; - - resultTime += (followingTime - foundTime) * interp; - -// std::cerr << resultTime << std::endl; - } - - size_t resultFrame = lrintf(resultTime * getSampleRate()); - -// std::cerr << "AlignmentModel::align: resultFrame = " << resultFrame << std::endl; +#ifdef DEBUG_ALIGNMENT_MODEL + std::cerr << "AlignmentModel::align: resultFrame = " << resultFrame << std::endl; +#endif return resultFrame; } diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/AlignmentModel.h --- a/data/model/AlignmentModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/AlignmentModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -17,6 +17,11 @@ #define _ALIGNMENT_MODEL_H_ #include "Model.h" +#include "SparseModel.h" +#include "base/RealTime.h" + +#include +#include class SparseTimeValueModel; @@ -39,6 +44,8 @@ virtual bool isReady(int *completion = 0) const; virtual const ZoomConstraint *getZoomConstraint() const; + QString getTypeName() const { return tr("Alignment"); } + const Model *getReferenceModel() const; const Model *getAlignedModel() const; @@ -60,14 +67,65 @@ Model *m_aligned; // I don't own this Model *m_inputModel; // I own this - SparseTimeValueModel *m_path; // I own this - mutable SparseTimeValueModel *m_reversePath; // I own this + + struct PathPoint + { + PathPoint(long _frame) : frame(_frame), mapframe(_frame) { } + PathPoint(long _frame, long _mapframe) : + frame(_frame), mapframe(_mapframe) { } + + int getDimensions() const { return 2; } + + long frame; + long mapframe; + + QString getLabel() const { return ""; } + + void toXml(QTextStream &stream, QString indent = "", + QString extraAttributes = "") const { + stream << QString("%1\n") + .arg(indent).arg(frame).arg(mapframe).arg(extraAttributes); + } + + QString toDelimitedDataString(QString delimiter, + size_t sampleRate) const { + QStringList list; + list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); + list << QString("%1").arg(mapframe); + return list.join(delimiter); + } + + struct Comparator { + bool operator()(const PathPoint &p1, const PathPoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + return p1.mapframe < p2.mapframe; + } + }; + + struct OrderComparator { + bool operator()(const PathPoint &p1, const PathPoint &p2) const { + return p1.frame < p2.frame; + } + }; + }; + + class PathModel : public SparseModel + { + public: + PathModel(size_t sampleRate, size_t resolution, bool notify = true) : + SparseModel(sampleRate, resolution, notify) { } + }; + + SparseTimeValueModel *m_rawPath; // I own this + mutable PathModel *m_path; // I own this + mutable PathModel *m_reversePath; // I own this bool m_pathBegun; bool m_pathComplete; + void constructPath() const; void constructReversePath() const; - size_t align(SparseTimeValueModel *path, size_t frame) const; + size_t align(PathModel *path, size_t frame) const; }; #endif diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/DenseThreeDimensionalModel.h --- a/data/model/DenseThreeDimensionalModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/DenseThreeDimensionalModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -98,6 +98,8 @@ return getValueAt(x, y) > threshold; } + QString getTypeName() const { return tr("Dense 3-D"); } + virtual int getCompletion() const = 0; protected: diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/DenseTimeValueModel.h --- a/data/model/DenseTimeValueModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/DenseTimeValueModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -70,6 +70,17 @@ */ 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"); } }; #endif diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/EditableDenseThreeDimensionalModel.h --- a/data/model/EditableDenseThreeDimensionalModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/EditableDenseThreeDimensionalModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -109,6 +109,8 @@ virtual void setCompletion(int completion, bool update = true); virtual int getCompletion() const { return m_completion; } + QString getTypeName() const { return tr("Editable Dense 3-D"); } + virtual QString toDelimitedDataString(QString delimiter) const; virtual void toXml(QTextStream &out, diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/FFTModel.cpp --- a/data/model/FFTModel.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/FFTModel.cpp Wed Feb 27 11:59:42 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, diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/FFTModel.h --- a/data/model/FFTModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/FFTModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -165,6 +165,11 @@ virtual void suspendWrites() { m_server->suspendWrites(); } virtual void resume() { m_server->resume(); } + QString getTypeName() const { return tr("FFT"); } + +public slots: + void sourceModelAboutToBeDeleted(); + private: FFTModel(const FFTModel &); // not implemented FFTModel &operator=(const FFTModel &); // not implemented diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/ImageModel.h --- a/data/model/ImageModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/ImageModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -38,6 +38,8 @@ long frame; QString image; QString label; + + QString getLabel() const { return label; } void toXml(QTextStream &stream, QString indent = "", @@ -87,6 +89,8 @@ SparseModel(sampleRate, resolution, notifyOnAdd) { } + QString getTypeName() const { return tr("Image"); } + virtual void toXml(QTextStream &out, QString indent = "", QString extraAttributes = "") const diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/Labeller.h --- a/data/model/Labeller.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/Labeller.h Wed Feb 27 11:59:42 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 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) { diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/Model.cpp --- a/data/model/Model.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/Model.cpp Wed Feb 27 11:59:42 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(); } @@ -113,9 +115,8 @@ else return frame; } size_t refFrame = m_alignment->toReference(frame); - //!!! this should be totally wrong, but because alignToReference and - // alignFromReference are the wrong way around, it's right... *sigh* - if (refFrame > getEndFrame()) refFrame = getEndFrame(); + const Model *m = m_alignment->getReferenceModel(); + if (m && refFrame > m->getEndFrame()) refFrame = m->getEndFrame(); return refFrame; } @@ -127,6 +128,7 @@ else return refFrame; } size_t frame = m_alignment->fromReference(refFrame); + if (frame > getEndFrame()) frame = getEndFrame(); return frame; } @@ -148,12 +150,21 @@ Model::getTitle() const { if (m_sourceModel) return m_sourceModel->getTitle(); + else return ""; } QString Model::getMaker() const { if (m_sourceModel) return m_sourceModel->getMaker(); + else return ""; +} + +QString +Model::getLocation() const +{ + if (m_sourceModel) return m_sourceModel->getLocation(); + else return ""; } void diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/Model.h --- a/data/model/Model.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/Model.h Wed Feb 27 11:59:42 2008 +0000 @@ -77,6 +77,18 @@ virtual QString getMaker() const; /** + * Return the location of the data in this model (e.g. source + * URL). This should not normally be returned for editable models + * that have been edited. + */ + virtual QString getLocation() const; + + /** + * Return the type of the model. For display purposes only. + */ + virtual QString getTypeName() const = 0; + + /** * Return a copy of this model. * * If the model is not editable, this may be effectively a shallow diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/NoteModel.h --- a/data/model/NoteModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/NoteModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -32,24 +32,27 @@ struct Note { public: - Note(long _frame) : frame(_frame), value(0.0f), duration(0) { } - Note(long _frame, float _value, size_t _duration, QString _label) : - frame(_frame), value(_value), duration(_duration), label(_label) { } + Note(long _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { } + Note(long _frame, float _value, size_t _duration, float _level, QString _label) : + frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { } int getDimensions() const { return 3; } long frame; float value; size_t duration; + float level; QString label; + QString getLabel() const { return label; } + void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const { stream << - QString("%1\n") - .arg(indent).arg(frame).arg(value).arg(duration).arg(label).arg(extraAttributes); + QString("%1\n") + .arg(indent).arg(frame).arg(value).arg(duration).arg(level).arg(label).arg(extraAttributes); } QString toDelimitedDataString(QString delimiter, size_t sampleRate) const @@ -57,7 +60,8 @@ QStringList list; list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str(); list << QString("%1").arg(value); - list << QString("%1").arg(duration); + list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str(); + list << QString("%1").arg(level); if (label != "") list << label; return list.join(delimiter); } @@ -68,6 +72,7 @@ if (p1.frame != p2.frame) return p1.frame < p2.frame; if (p1.value != p2.value) return p1.value < p2.value; if (p1.duration != p2.duration) return p1.duration < p2.duration; + if (p1.level != p2.level) return p1.level < p2.level; return p1.label < p2.label; } }; @@ -122,6 +127,8 @@ */ virtual PointList getPoints(long frame) const; + QString getTypeName() const { return tr("Note"); } + virtual void toXml(QTextStream &out, QString indent = "", QString extraAttributes = "") const diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/PowerOfSqrtTwoZoomConstraint.cpp --- a/data/model/PowerOfSqrtTwoZoomConstraint.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.cpp Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/RangeSummarisableTimeValueModel.h --- a/data/model/RangeSummarisableTimeValueModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/RangeSummarisableTimeValueModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -71,6 +71,10 @@ * block size equal to the distance between start and end frames. */ 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"); } }; #endif diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/SparseModel.h --- a/data/model/SparseModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/SparseModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -132,6 +132,8 @@ virtual bool hasTextLabels() const { return m_hasTextLabels; } + QString getTypeName() const { return tr("Sparse"); } + virtual void toXml(QTextStream &out, QString indent = "", QString extraAttributes = "") const; @@ -462,7 +464,7 @@ QMutexLocker locker(&m_mutex); m_points.insert(point); m_pointCount++; - if (point.label != "") m_hasTextLabels = true; + if (point.getLabel() != "") m_hasTextLabels = true; } // Even though this model is nominally sparse, there may still be diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/SparseOneDimensionalModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -30,7 +30,9 @@ long frame; QString label; - + + QString getLabel() const { return label; } + void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const @@ -84,6 +86,8 @@ } return -1; } + + QString getTypeName() const { return tr("Sparse 1-D"); } }; #endif diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/SparseTimeValueModel.h --- a/data/model/SparseTimeValueModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/SparseTimeValueModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -38,6 +38,8 @@ long frame; float value; QString label; + + QString getLabel() const { return label; } void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const @@ -93,6 +95,8 @@ { PlayParameterRepository::getInstance()->addModel(this); } + + QString getTypeName() const { return tr("Sparse Time-Value"); } }; diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/SparseValueModel.h --- a/data/model/SparseValueModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/SparseValueModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -51,6 +51,9 @@ using SparseModel::m_points; using SparseModel::modelChanged; using SparseModel::getPoints; + using SparseModel::tr; + + QString getTypeName() const { return tr("Sparse Value"); } virtual float getValueMinimum() const { return m_valueMinimum; } virtual float getValueMaximum() const { return m_valueMaximum; } diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/TextModel.h --- a/data/model/TextModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/TextModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -38,6 +38,8 @@ long frame; float height; QString label; + + QString getLabel() const { return label; } void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const @@ -93,6 +95,8 @@ QString("%1 subtype=\"text\"") .arg(extraAttributes)); } + + QString getTypeName() const { return tr("Text"); } }; diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/WaveFileModel.cpp --- a/data/model/WaveFileModel.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/WaveFileModel.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -173,6 +173,13 @@ if (m_reader) return m_reader->getMaker(); return ""; } + +QString +WaveFileModel::getLocation() const +{ + if (m_reader) return m_reader->getLocation(); + return ""; +} size_t WaveFileModel::getData(int channel, size_t start, size_t count, @@ -182,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; @@ -204,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; @@ -255,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; @@ -285,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; @@ -301,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(); @@ -316,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); @@ -341,6 +461,8 @@ } } + m_directReadMutex.unlock(); + if (got > 0) { ranges.push_back(Range(min, max, total / got)); } @@ -353,6 +475,8 @@ const RangeBlock &cache = m_cache[cacheType]; + blockSize = roundedBlockSize; + size_t cacheBlock, div; if (cacheType == 0) { diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/WaveFileModel.h --- a/data/model/WaveFileModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/WaveFileModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -50,6 +50,7 @@ QString getTitle() const; QString getMaker() const; + QString getLocation() const; virtual Model *clone() const; @@ -67,12 +68,20 @@ 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; virtual Range getSummary(size_t channel, size_t start, size_t count) const; + QString getTypeName() const { return tr("Wave File"); } + virtual void toXml(QTextStream &out, QString indent = "", QString extraAttributes = "") const; @@ -121,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 diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/WritableWaveFileModel.cpp --- a/data/model/WritableWaveFileModel.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/WritableWaveFileModel.cpp Wed Feb 27 11:59:42 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, diff -r a6fab10ff9e6 -r 6f6ab834449d data/model/WritableWaveFileModel.h --- a/data/model/WritableWaveFileModel.h Thu Nov 15 14:03:56 2007 +0000 +++ b/data/model/WritableWaveFileModel.h Wed Feb 27 11:59:42 2008 +0000 @@ -68,11 +68,19 @@ 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; virtual Range getSummary(size_t channel, size_t start, size_t count) const; + QString getTypeName() const { return tr("Writable Wave File"); } + virtual void toXml(QTextStream &out, QString indent = "", QString extraAttributes = "") const; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/DSSIPluginInstance.cpp --- a/plugin/DSSIPluginInstance.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/DSSIPluginInstance.cpp Wed Feb 27 11:59:42 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(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) diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/DSSIPluginInstance.h --- a/plugin/DSSIPluginInstance.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/DSSIPluginInstance.h Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/FeatureExtractionPluginFactory.cpp --- a/plugin/FeatureExtractionPluginFactory.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/FeatureExtractionPluginFactory.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -95,6 +95,7 @@ if (factory) { std::vector 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]); } } diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/LADSPAPluginFactory.cpp --- a/plugin/LADSPAPluginFactory.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/LADSPAPluginFactory.cpp Wed Feb 27 11:59:42 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; } diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/LADSPAPluginInstance.cpp --- a/plugin/LADSPAPluginInstance.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/LADSPAPluginInstance.cpp Wed Feb 27 11:59:42 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(m_factory); + if (f) { + return f->getPortDisplayHint(m_descriptor, m_controlPortsIn[parameter].first); + } else { + return PortHint::NoHint; + } +} + void LADSPAPluginInstance::run(const Vamp::RealTime &) { diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/LADSPAPluginInstance.h --- a/plugin/LADSPAPluginInstance.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/LADSPAPluginInstance.h Wed Feb 27 11:59:42 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; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/RealTimePluginInstance.h --- a/plugin/RealTimePluginInstance.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/RealTimePluginInstance.h Wed Feb 27 11:59:42 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(); } diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/plugin.pro --- a/plugin/plugin.pro Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/plugin.pro Wed Feb 27 11:59:42 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 \ diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/FeatureExtractionModelTransformer.cpp --- a/plugin/transform/FeatureExtractionModelTransformer.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/FeatureExtractionModelTransformer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -29,81 +29,130 @@ #include "data/model/FFTModel.h" #include "data/model/WaveFileModel.h" +#include "TransformFactory.h" + #include #include -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; } - if (configurationXml != "") { - PluginXml(m_plugin).setParametersFromXml(configurationXml); - } + TransformFactory::getInstance()->makeContextConsistentWithPlugin + (m_transform, m_plugin); - 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]); @@ -112,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; } @@ -138,7 +187,7 @@ haveExtents = true; } - size_t modelRate = m_input->getSampleRate(); + size_t modelRate = input->getSampleRate(); size_t modelResolution = 1; switch (m_descriptor->sampleType) { @@ -150,7 +199,7 @@ break; case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: - modelResolution = m_context.stepSize; + modelResolution = m_transform.getStepSize(); break; case Vamp::Plugin::OutputDescriptor::FixedSampleRate: @@ -217,7 +266,7 @@ m_output = model; } - if (m_output) m_output->setSourceModel(m_input); + if (m_output) m_output->setSourceModel(input); } FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer() @@ -228,12 +277,12 @@ } DenseTimeValueModel * -FeatureExtractionModelTransformer::getInput() +FeatureExtractionModelTransformer::getConformingInput() { DenseTimeValueModel *dtvm = dynamic_cast(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; } @@ -241,7 +290,7 @@ void FeatureExtractionModelTransformer::run() { - DenseTimeValueModel *input = getInput(); + DenseTimeValueModel *input = getConformingInput(); if (!input) return; if (!m_output) return; @@ -258,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) { @@ -267,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 fftModels; @@ -277,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()) { @@ -299,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; @@ -325,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 >= @@ -334,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 @@ -369,7 +426,7 @@ prevCompletion = completion; } - blockFrame += m_context.stepSize; + blockFrame += stepSize; } if (m_abandoned) return; @@ -392,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; @@ -408,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; @@ -470,8 +551,10 @@ if (binCount == 0) { - SparseOneDimensionalModel *model = getOutput(); + SparseOneDimensionalModel *model = + getConformingOutput(); if (!model) return; + model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str())); } else if (binCount == 1) { @@ -479,8 +562,10 @@ float value = 0.0; if (feature.values.size() > 0) value = feature.values[0]; - SparseTimeValueModel *model = getOutput(); + SparseTimeValueModel *model = + getConformingOutput(); 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; @@ -495,12 +580,15 @@ float velocity = 100; if (feature.values.size() > 2) velocity = feature.values[2]; + if (velocity < 0) velocity = 127; + if (velocity > 127) velocity = 127; - NoteModel *model = getOutput(); + NoteModel *model = getConformingOutput(); if (!model) return; model->addPoint(NoteModel::Point(frame, pitch, lrintf(duration), + velocity / 127.f, feature.label.c_str())); } else { @@ -508,7 +596,7 @@ DenseThreeDimensionalModel::Column values = feature.values; EditableDenseThreeDimensionalModel *model = - getOutput(); + getConformingOutput(); if (!model) return; model->setColumn(frame / model->getResolution(), values); @@ -528,29 +616,32 @@ if (binCount == 0) { - SparseOneDimensionalModel *model = getOutput(); + SparseOneDimensionalModel *model = + getConformingOutput(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } else if (binCount == 1) { - SparseTimeValueModel *model = getOutput(); + SparseTimeValueModel *model = + getConformingOutput(); 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 *model = + getConformingOutput(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } else { EditableDenseThreeDimensionalModel *model = - getOutput(); + getConformingOutput(); if (!model) return; - model->setCompletion(completion, m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } } diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/FeatureExtractionModelTransformer.h --- a/plugin/transform/FeatureExtractionModelTransformer.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/FeatureExtractionModelTransformer.h Wed Feb 27 11:59:42 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 + +#include + +#include 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 ModelClass *getOutput() { + DenseTimeValueModel *getConformingInput(); + template ModelClass *getConformingOutput() { ModelClass *mc = dynamic_cast(m_output); if (!mc) { std::cerr << "FeatureExtractionModelTransformer::getOutput: Output model not conformable" << std::endl; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/ModelTransformer.cpp --- a/plugin/transform/ModelTransformer.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/ModelTransformer.cpp Wed Feb 27 11:59:42 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) diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/ModelTransformer.h --- a/plugin/transform/ModelTransformer.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/ModelTransformer.h Wed Feb 27 11:59:42 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 diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/ModelTransformerFactory.cpp --- a/plugin/transform/ModelTransformerFactory.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/ModelTransformerFactory.cpp Wed Feb 27 11:59:42 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,22 +67,24 @@ } } -Model * -ModelTransformerFactory::getConfigurationForTransformer(TransformId identifier, - const std::vector &candidateInputModels, - PluginTransformer::ExecutionContext &context, - QString &configurationXml, - AudioCallbackPlaySource *source, - size_t startFrame, - size_t duration) +ModelTransformer::Input +ModelTransformerFactory::getConfigurationForTransform(Transform &transform, + const std::vector &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, //as we'll need to reinitialise the channel settings in the dialog - Model *inputModel = candidateInputModels[0]; //!!! for now + Model *inputModel = candidateInputModels[0]; QStringList candidateModelNames; + QString defaultModelName; std::map modelMap; for (size_t i = 0; i < candidateInputModels.size(); ++i) { QString modelName = candidateInputModels[i]->objectName(); @@ -92,17 +95,20 @@ } modelMap[modelName] = candidateInputModels[i]; candidateModelNames.push_back(modelName); + if (candidateInputModels[i] == defaultInputModel) { + defaultModelName = modelName; + } } - 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; @@ -112,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 @@ -179,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(inputModel)) { @@ -192,7 +205,8 @@ } int minChannels = 1, maxChannels = sourceChannels; - getChannelRange(identifier, plugin, minChannels, maxChannels); + getChannelRange(transform.getIdentifier(), plugin, + minChannels, maxChannels); int targetChannels = sourceChannels; if (!effect) { @@ -200,12 +214,13 @@ if (sourceChannels > maxChannels) targetChannels = maxChannels; } - int defaultChannel = context.channel; + int defaultChannel = -1; //!!! no longer saved! [was context.channel] PluginParameterDialog *dialog = new PluginParameterDialog(plugin); if (candidateModelNames.size() > 1 && !generator) { - dialog->setCandidateInputModels(candidateModelNames); + dialog->setCandidateInputModels(candidateModelNames, + defaultModelName); } if (startFrame != 0 || duration != 0) { @@ -236,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; @@ -262,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) @@ -289,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())); @@ -336,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)); @@ -353,6 +389,8 @@ t->wait(); } + message = t->getMessage(); + return model; } diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/ModelTransformerFactory.h --- a/plugin/transform/ModelTransformerFactory.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/ModelTransformerFactory.h Wed Feb 27 11:59:42 2008 +0000 @@ -21,8 +21,6 @@ #include "ModelTransformer.h" -#include "PluginTransformer.h" - #include #include @@ -40,27 +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 &candidateInputModels, - 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 &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 @@ -69,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(); @@ -84,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 TransformerConfigurationMap; TransformerConfigurationMap m_lastConfigurations; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/PluginTransformer.cpp --- a/plugin/transform/PluginTransformer.cpp Thu Nov 15 14:03:56 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +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(_plugin); - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; - vp = dynamic_cast(_plugin); //!!! why? -} - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; - vp = dynamic_cast(_plugin); //!!! no, I mean really why? - } - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl; - } - - if (!vp) { - domain = Vamp::Plugin::TimeDomain; - if (!stepSize) { - if (!blockSize) blockSize = 1024; - stepSize = blockSize; - } else { - if (!blockSize) blockSize = stepSize; - } - } else { - domain = vp->getInputDomain(); - 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; - } - } - } -} - - diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/PluginTransformer.h --- a/plugin/transform/PluginTransformer.h Thu Nov 15 14:03:56 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 diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/RealTimeEffectModelTransformer.cpp --- a/plugin/transform/RealTimeEffectModelTransformer.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/RealTimeEffectModelTransformer.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -25,22 +25,23 @@ #include "data/model/WritableWaveFileModel.h" #include "data/model/WaveFileModel.h" +#include "TransformFactory.h" + #include -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(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) { diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/RealTimeEffectModelTransformer.h --- a/plugin/transform/RealTimeEffectModelTransformer.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/RealTimeEffectModelTransformer.h Wed Feb 27 11:59:42 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 diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/Transform.cpp --- a/plugin/transform/Transform.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/Transform.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -19,6 +19,17 @@ #include "plugin/FeatureExtractionPluginFactory.h" +#include + +#include +#include +#include +#include + +#include + +#include + 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("::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("\n") + .arg(encodeEntities(i->first)) + .arg(i->second); + } + + for (ConfigurationMap::const_iterator i = m_configuration.begin(); + i != m_configuration.end(); ++i) { + out << indent << " " + << QString("\n") + .arg(encodeEntities(i->first)) + .arg(encodeEntities(i->second)); + } + + out << indent << "\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::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()); + } +} + diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/Transform.h --- a/plugin/transform/Transform.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/Transform.h Wed Feb 27 11:59:42 2008 +0000 @@ -18,13 +18,14 @@ #include "base/XmlExportable.h" #include "base/Window.h" - -#include +#include "base/RealTime.h" #include 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 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 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; }; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/TransformDescription.h --- a/plugin/transform/TransformDescription.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/TransformDescription.h Wed Feb 27 11:59:42 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); }; }; diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/TransformFactory.cpp --- a/plugin/transform/TransformFactory.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/TransformFactory.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -28,6 +28,7 @@ #include #include +#include TransformFactory * TransformFactory::m_instance = new TransformFactory; @@ -43,25 +44,39 @@ } TransformList -TransformFactory::getAllTransforms() +TransformFactory::getAllTransformDescriptions() { if (m_transforms.empty()) populateTransforms(); std::set 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::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 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(plugin); + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; + vp = dynamic_cast(plugin); //!!! why? +} + if (!vp) { +// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; + vp = dynamic_cast(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(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(plugin); - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl; - vp = dynamic_cast(plugin); //!!! why? -} - if (!vp) { -// std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl; - vp = dynamic_cast(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; } diff -r a6fab10ff9e6 -r 6f6ab834449d plugin/transform/TransformFactory.h --- a/plugin/transform/TransformFactory.h Thu Nov 15 14:03:56 2007 +0000 +++ b/plugin/transform/TransformFactory.h Wed Feb 27 11:59:42 2008 +0000 @@ -18,11 +18,11 @@ #include "TransformDescription.h" +#include + #include #include -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 getAllTransformTypes(); - std::vector getTransformCategories(QString transformType); std::vector 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 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 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 TransformDescriptionMap; @@ -103,6 +163,8 @@ void populateFeatureExtractionPlugins(TransformDescriptionMap &); void populateRealTimePlugins(TransformDescriptionMap &); + Vamp::PluginBase *instantiateDefaultPluginFor(TransformId id, size_t rate); + static TransformFactory *m_instance; }; diff -r a6fab10ff9e6 -r 6f6ab834449d system/Init.cpp --- a/system/Init.cpp Thu Nov 15 14:03:56 2007 +0000 +++ b/system/Init.cpp Wed Feb 27 11:59:42 2008 +0000 @@ -79,9 +79,9 @@ #ifdef Q_WS_WIN32 redirectStderr(); - QFont fn = qApp->font(); - fn.setFamily("Tahoma"); - qApp->setFont(fn); +// QFont fn = qApp->font(); +// fn.setFamily("Tahoma"); +// qApp->setFont(fn); #else #ifdef Q_WS_X11 // QFont fn = qApp->font(); diff -r a6fab10ff9e6 -r 6f6ab834449d system/System.h --- a/system/System.h Thu Nov 15 14:03:56 2007 +0000 +++ b/system/System.h Wed Feb 27 11:59:42 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 */