# HG changeset patch # User Chris Cannam # Date 1537188674 -3600 # Node ID 710e6250a4016c6057407814001f6f3ea8d843b5 # Parent d4a28d1479a8576bbe1d9d4c1f458cfd8e0d08e5# Parent 8988b27ebf3868f7d28f51079c4c578b4f694520 Merge from default branch diff -r d4a28d1479a8 -r 710e6250a401 base/AudioLevel.cpp --- a/base/AudioLevel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/AudioLevel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -31,7 +31,7 @@ struct FaderDescription { FaderDescription(double _minDb, double _maxDb, double _zeroPoint) : - minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { } + minDb(_minDb), maxDb(_maxDb), zeroPoint(_zeroPoint) { } double minDb; double maxDb; @@ -97,17 +97,17 @@ double db = 0.0f; if (def >= 50.0f) { - db = (def - 50.0f) / 2.5f - 20.0f; + db = (def - 50.0f) / 2.5f - 20.0f; } else if (def >= 30.0f) { - db = (def - 30.0f) / 2.0f - 30.0f; + db = (def - 30.0f) / 2.0f - 30.0f; } else if (def >= 15.0f) { - db = (def - 15.0f) / 1.5f - 40.0f; + db = (def - 15.0f) / 1.5f - 40.0f; } else if (def >= 7.5f) { - db = (def - 7.5f) / 0.75f - 50.0f; + db = (def - 7.5f) / 0.75f - 50.0f; } else if (def >= 2.5f) { - db = (def - 2.5f) / 0.5f - 60.0f; + db = (def - 2.5f) / 0.5f - 60.0f; } else { - db = (def / 0.25f) - 70.0f; + db = (def / 0.25f) - 70.0f; } return db; @@ -120,32 +120,32 @@ if (type == IEC268Meter || type == IEC268LongMeter) { - double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb); - double percent = double(level) * maxPercent / double(maxLevel); - double dB = iec_fader_to_dB(percent); - return dB; + double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb); + double percent = double(level) * maxPercent / double(maxLevel); + double dB = iec_fader_to_dB(percent); + return dB; } else { // scale proportional to sqrt(fabs(dB)) - int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint); + int zeroLevel = int(round(maxLevel * faderTypes[type].zeroPoint)); - if (level >= zeroLevel) { - - double value = level - zeroLevel; - double scale = (maxLevel - zeroLevel) / - sqrt(faderTypes[type].maxDb); - value /= scale; - double dB = pow(value, 2.); - return dB; - - } else { - - double value = zeroLevel - level; - double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb); - value /= scale; - double dB = pow(value, 2.); - return 0. - dB; - } + if (level >= zeroLevel) { + + double value = level - zeroLevel; + double scale = (maxLevel - zeroLevel) / + sqrt(faderTypes[type].maxDb); + value /= scale; + double dB = pow(value, 2.); + return dB; + + } else { + + double value = zeroLevel - level; + double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb); + value /= scale; + double dB = pow(value, 2.); + return 0. - dB; + } } } @@ -157,25 +157,25 @@ if (type == IEC268Meter || type == IEC268LongMeter) { - // The IEC scale gives a "percentage travel" for a given dB - // level, but it reaches 100% at 0dB. So we want to treat the - // result not as a percentage, but as a scale between 0 and - // whatever the "percentage" for our (possibly >0dB) max dB is. - - double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb); - double percent = iec_dB_to_fader(dB); - int faderLevel = int((maxLevel * percent) / maxPercent + 0.01f); - - if (faderLevel < 0) faderLevel = 0; - if (faderLevel > maxLevel) faderLevel = maxLevel; - return faderLevel; + // The IEC scale gives a "percentage travel" for a given dB + // level, but it reaches 100% at 0dB. So we want to treat the + // result not as a percentage, but as a scale between 0 and + // whatever the "percentage" for our (possibly >0dB) max dB is. + + double maxPercent = iec_dB_to_fader(faderTypes[type].maxDb); + double percent = iec_dB_to_fader(dB); + int faderLevel = int((maxLevel * percent) / maxPercent + 0.01f); + + if (faderLevel < 0) faderLevel = 0; + if (faderLevel > maxLevel) faderLevel = maxLevel; + return faderLevel; } else { - int zeroLevel = int(maxLevel * faderTypes[type].zeroPoint); + int zeroLevel = int(round(maxLevel * faderTypes[type].zeroPoint)); - if (dB >= 0.) { - + if (dB >= 0.) { + if (faderTypes[type].maxDb <= 0.) { return maxLevel; @@ -189,21 +189,21 @@ if (level > maxLevel) level = maxLevel; return level; } - - } else { + + } else { - dB = 0. - dB; - double value = sqrt(dB); - double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb); - value *= scale; - int level = zeroLevel - int(value + 0.01f); - if (level < 0) level = 0; - return level; - } + dB = 0. - dB; + double value = sqrt(dB); + double scale = zeroLevel / sqrt(0. - faderTypes[type].minDb); + value *= scale; + int level = zeroLevel - int(value + 0.01f); + if (level < 0) level = 0; + return level; + } } } - + double AudioLevel::fader_to_multiplier(int level, int maxLevel, FaderType type) { @@ -226,12 +226,12 @@ { LevelList &ll = previewLevelCache[levels]; if (ll.empty()) { - for (int i = 0; i <= levels; ++i) { - double m = AudioLevel::fader_to_multiplier - (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel); - if (levels == 1) m /= 100; // noise - ll.push_back(m); - } + for (int i = 0; i <= levels; ++i) { + double m = AudioLevel::fader_to_multiplier + (i + levels/4, levels + levels/4, AudioLevel::PreviewLevel); + if (levels == 1) m /= 100; // noise + ll.push_back(m); + } } return ll; } @@ -255,23 +255,23 @@ // binary search int level = -1; while (result < 0) { - int newlevel = (lo + hi) / 2; - if (newlevel == level || - newlevel == 0 || - newlevel == levels) { - result = newlevel; - break; - } - level = newlevel; - if (ll[level] >= m) { - hi = level; - } else if (ll[level+1] >= m) { - result = level; - } else { - lo = level; - } + int newlevel = (lo + hi) / 2; + if (newlevel == level || + newlevel == 0 || + newlevel == levels) { + result = newlevel; + break; + } + level = newlevel; + if (ll[level] >= m) { + hi = level; + } else if (ll[level+1] >= m) { + result = level; + } else { + lo = level; + } } - + return result; */ @@ -288,5 +288,5 @@ return ll[level]; */ } - + diff -r d4a28d1479a8 -r 710e6250a401 base/AudioLevel.h --- a/base/AudioLevel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/AudioLevel.h Mon Sep 17 13:51:14 2018 +0100 @@ -38,11 +38,11 @@ static const double DB_FLOOR; enum FaderType { - ShortFader = 0, // -40 -> +6 dB + ShortFader = 0, // -40 -> +6 dB LongFader = 1, // -70 -> +10 dB IEC268Meter = 2, // -70 -> 0 dB IEC268LongMeter = 3, // -70 -> +10 dB (0dB aligns with LongFader) - PreviewLevel = 4 + PreviewLevel = 4 }; static double multiplier_to_dB(double multiplier); @@ -53,7 +53,7 @@ static double fader_to_multiplier(int level, int maxLevel, FaderType type); static int multiplier_to_fader(double multiplier, int maxFaderLevel, - FaderType type); + FaderType type); // fast if "levels" doesn't change often -- for audio segment previews static int multiplier_to_preview(double multiplier, int levels); diff -r d4a28d1479a8 -r 710e6250a401 base/AudioPlaySource.h --- a/base/AudioPlaySource.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/AudioPlaySource.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _AUDIO_PLAY_SOURCE_H_ -#define _AUDIO_PLAY_SOURCE_H_ +#ifndef SV_AUDIO_PLAY_SOURCE_H +#define SV_AUDIO_PLAY_SOURCE_H #include "BaseTypes.h" @@ -59,7 +59,9 @@ /** * Return the current (or thereabouts) output levels in the range - * 0.0 -> 1.0, for metering purposes. + * 0.0 -> 1.0, for metering purposes. The values returned are + * peak values since the last call to this function was made + * (i.e. calling this function also resets them). */ virtual bool getOutputLevels(float &left, float &right) = 0; diff -r d4a28d1479a8 -r 710e6250a401 base/AudioRecordTarget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/AudioRecordTarget.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,50 @@ +/* -*- 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 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 SV_AUDIO_RECORD_TARGET_H +#define SV_AUDIO_RECORD_TARGET_H + +#include "BaseTypes.h" + +/** + * The record target API used by the view manager. See also AudioPlaySource. + */ +class AudioRecordTarget +{ +public: + virtual ~AudioRecordTarget() { } + + /** + * Return whether recording is currently happening. + */ + virtual bool isRecording() const = 0; + + /** + * Return the approximate duration of the audio recording so far. + */ + virtual sv_frame_t getRecordDuration() const = 0; + + /** + * Return the current (or thereabouts) input levels in the range + * 0.0 -> 1.0, for metering purposes. Only valid while recording. + * The values returned are peak values since the last call to this + * function was made (i.e. calling this function also resets them). + */ + virtual bool getInputLevels(float &left, float &right) = 0; +}; + +#endif + + + diff -r d4a28d1479a8 -r 710e6250a401 base/BaseTypes.h --- a/base/BaseTypes.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/BaseTypes.h Mon Sep 17 13:51:14 2018 +0100 @@ -16,6 +16,10 @@ #define SV_BASE_TYPES_H #include +#include +#include + +#include /** Frame index, the unit of our time axis. This is signed because the axis conceptually extends below zero: zero represents the start of @@ -34,9 +38,9 @@ { if (i < 0) return false; if (sizeof(T) > sizeof(typename C::size_type)) { - return i < static_cast(container.size()); + return i < static_cast(container.size()); } else { - return static_cast(i) < container.size(); + return static_cast(i) < container.size(); } } @@ -46,6 +50,10 @@ */ typedef double sv_samplerate_t; +typedef std::vector> floatvec_t; + +typedef std::vector, + breakfastquay::StlAllocator>> complexvec_t; /** Display zoom level. Can be an integer number of samples per pixel, * or an integer number of pixels per sample. diff -r d4a28d1479a8 -r 710e6250a401 base/ColumnOp.cpp --- a/base/ColumnOp.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ColumnOp.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -49,10 +49,33 @@ if (n == ColumnNormalization::None || in.empty()) { return in; } + + float shift = 0.f; + float scale = 1.f; - float scale = 1.f; - - if (n == ColumnNormalization::Sum1) { + if (n == ColumnNormalization::Range01) { + + float min = 0.f; + float max = 0.f; + bool have = false; + for (auto v: in) { + if (v < min || !have) { + min = v; + } + if (v > max || !have) { + max = v; + } + have = true; + } + if (min != 0.f) { + shift = -min; + max -= min; + } + if (max != 0.f) { + scale = 1.f / max; + } + + } else if (n == ColumnNormalization::Sum1) { float sum = 0.f; @@ -86,7 +109,7 @@ } } - return applyGain(in, scale); + return applyGain(applyShift(in, shift), scale); } ColumnOp::Column diff -r d4a28d1479a8 -r 710e6250a401 base/ColumnOp.h --- a/base/ColumnOp.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ColumnOp.h Mon Sep 17 13:51:14 2018 +0100 @@ -26,6 +26,9 @@ * Max1 means to normalize to max value = 1.0. * Sum1 means to normalize to sum of values = 1.0. * + * Range01 means to normalize such that the max value = 1.0 and the + * min value (if different from the max value) = 0.0. + * * Hybrid means normalize to max = 1.0 and then multiply by * log10 of the max value, to retain some difference between * levels of neighbouring columns. @@ -36,6 +39,7 @@ None, Max1, Sum1, + Range01, Hybrid }; @@ -56,10 +60,21 @@ */ static Column applyGain(const Column &in, double gain) { if (gain == 1.0) return in; - Column out; - out.reserve(in.size()); - for (auto v: in) out.push_back(float(v * gain)); - return out; + Column out; + out.reserve(in.size()); + for (auto v: in) out.push_back(float(v * gain)); + return out; + } + + /** + * Shift the values in the given column by the given offset. + */ + static Column applyShift(const Column &in, float offset) { + if (offset == 0.f) return in; + Column out; + out.reserve(in.size()); + for (auto v: in) out.push_back(v + offset); + return out; } /** @@ -80,13 +95,13 @@ if (!in_range_for(in, ix+1)) { return in[ix] > in[ix-1]; } - if (in[ix] < in[ix+1]) { + if (in[ix] < in[ix+1]) { return false; } - if (in[ix] <= in[ix-1]) { + if (in[ix] <= in[ix-1]) { return false; } - return true; + return true; } /** @@ -115,10 +130,10 @@ * with the bin of index minbin. */ static Column distribute(const Column &in, - int h, - const std::vector &binfory, - int minbin, - bool interpolate); + int h, + const std::vector &binfory, + int minbin, + bool interpolate); }; diff -r d4a28d1479a8 -r 710e6250a401 base/Command.cpp --- a/base/Command.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Command.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -24,7 +24,7 @@ MacroCommand::~MacroCommand() { for (size_t i = 0; i < m_commands.size(); ++i) { - delete m_commands[i]; + delete m_commands[i]; } } @@ -38,13 +38,13 @@ MacroCommand::deleteCommand(Command *command) { for (std::vector::iterator i = m_commands.begin(); - i != m_commands.end(); ++i) { + i != m_commands.end(); ++i) { - if (*i == command) { - m_commands.erase(i); - delete command; - return; - } + if (*i == command) { + m_commands.erase(i); + delete command; + return; + } } } @@ -58,7 +58,7 @@ MacroCommand::execute() { for (size_t i = 0; i < m_commands.size(); ++i) { - m_commands[i]->execute(); + m_commands[i]->execute(); } } @@ -66,7 +66,7 @@ MacroCommand::unexecute() { for (size_t i = 0; i < m_commands.size(); ++i) { - m_commands[m_commands.size() - i - 1]->unexecute(); + m_commands[m_commands.size() - i - 1]->unexecute(); } } diff -r d4a28d1479a8 -r 710e6250a401 base/HitCount.h --- a/base/HitCount.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/HitCount.h Mon Sep 17 13:51:14 2018 +0100 @@ -25,34 +25,34 @@ { public: HitCount(std::string name) : - m_name(name), - m_hit(0), - m_partial(0), - m_miss(0) + m_name(name), + m_hit(0), + m_partial(0), + m_miss(0) { } ~HitCount() { #ifndef NO_HIT_COUNTS - using namespace std; - int total = m_hit + m_partial + m_miss; - cerr << "Hit count: " << m_name << ": "; - if (m_partial > 0) { - cerr << m_hit << " hits, " << m_partial << " partial, " - << m_miss << " misses"; - } else { - cerr << m_hit << " hits, " << m_miss << " misses"; - } - if (total > 0) { - if (m_partial > 0) { - cerr << " (" << ((m_hit * 100.0) / total) << "%, " - << ((m_partial * 100.0) / total) << "%, " - << ((m_miss * 100.0) / total) << "%)"; - } else { - cerr << " (" << ((m_hit * 100.0) / total) << "%, " - << ((m_miss * 100.0) / total) << "%)"; - } - } - cerr << endl; + using namespace std; + int total = m_hit + m_partial + m_miss; + cerr << "Hit count: " << m_name << ": "; + if (m_partial > 0) { + cerr << m_hit << " hits, " << m_partial << " partial, " + << m_miss << " misses"; + } else { + cerr << m_hit << " hits, " << m_miss << " misses"; + } + if (total > 0) { + if (m_partial > 0) { + cerr << " (" << ((m_hit * 100.0) / total) << "%, " + << ((m_partial * 100.0) / total) << "%, " + << ((m_miss * 100.0) / total) << "%)"; + } else { + cerr << " (" << ((m_hit * 100.0) / total) << "%, " + << ((m_miss * 100.0) / total) << "%)"; + } + } + cerr << endl; #endif } diff -r d4a28d1479a8 -r 710e6250a401 base/LogRange.cpp --- a/base/LogRange.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/LogRange.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -23,30 +23,31 @@ void LogRange::mapRange(double &min, double &max, double logthresh) { + static double eps = 1e-10; + + // ensure that max > min: if (min > max) std::swap(min, max); if (max == min) max = min + 1; -// cerr << "LogRange::mapRange: min = " << min << ", max = " << max << endl; - - if (min >= 0.f) { + if (min >= 0.0) { - max = log10(max); // we know max != 0 + // and max > min, so we know min >= 0 and max > 0 + + max = log10(max); - if (min == 0.f) min = std::min(logthresh, max); + if (min == 0.0) min = std::min(logthresh, max); else min = log10(min); -// cerr << "LogRange::mapRange: positive: min = " << min << ", max = " << max << endl; + } else if (max <= 0.0) { - } else if (max <= 0.f) { + // and max > min, so we know min < 0 and max <= 0 - min = log10(-min); // we know min != 0 - - if (max == 0.f) max = std::min(logthresh, min); + min = log10(-min); + + if (max == 0.0) max = std::min(logthresh, min); else max = log10(-max); std::swap(min, max); - -// cerr << "LogRange::mapRange: negative: min = " << min << ", max = " << max << endl; } else { @@ -54,17 +55,15 @@ max = log10(std::max(max, -min)); min = std::min(logthresh, max); - -// cerr << "LogRange::mapRange: spanning: min = " << min << ", max = " << max << endl; } - if (min == max) min = max - 1; + if (fabs(max - min) < eps) min = max - 1; } double LogRange::map(double value, double thresh) { - if (value == 0.f) return thresh; + if (value == 0.0) return thresh; return log10(fabs(value)); } @@ -77,7 +76,7 @@ static double sd(const std::vector &values, int start, int n) { - double sum = 0.f, mean = 0.f, variance = 0.f; + double sum = 0.0, mean = 0.0, variance = 0.0; for (int i = 0; i < n; ++i) { sum += values[start + i]; } @@ -91,7 +90,7 @@ } bool -LogRange::useLogScale(std::vector values) +LogRange::shouldUseLogScale(std::vector values) { // Principle: Partition the data into two sets around the median; // calculate the standard deviation of each set; if the two SDs diff -r d4a28d1479a8 -r 710e6250a401 base/LogRange.h --- a/base/LogRange.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/LogRange.h Mon Sep 17 13:51:14 2018 +0100 @@ -23,14 +23,6 @@ { public: /** - * Map a linear range onto a logarithmic range. min and max are - * passed as the extents of the linear range and returned as the - * extents of the logarithmic range. thresh is the minimum value - * for the log range, to be used if the linear range spans zero. - */ - static void mapRange(double &min, double &max, double thresh = -10); - - /** * Map a value onto a logarithmic range. This just means taking * the base-10 log of the absolute value, or using the threshold * value if the absolute value is zero. @@ -44,11 +36,19 @@ static double unmap(double value); /** + * Map a linear range onto a logarithmic range. min and max are + * passed as the extents of the linear range and returned as the + * extents of the logarithmic range. thresh is the minimum value + * for the log range, to be used if the linear range spans zero. + */ + static void mapRange(double &min, double &max, double thresh = -10); + + /** * Estimate whether a set of values would be more properly shown * using a logarithmic than a linear scale. This is only ever * going to be a rough guess. */ - static bool useLogScale(std::vector values); + static bool shouldUseLogScale(std::vector values); }; diff -r d4a28d1479a8 -r 710e6250a401 base/MagnitudeRange.h --- a/base/MagnitudeRange.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/MagnitudeRange.h Mon Sep 17 13:51:14 2018 +0100 @@ -29,7 +29,7 @@ MagnitudeRange(float min, float max) : m_min(min), m_max(max) { } bool operator==(const MagnitudeRange &r) { - return r.m_min == m_min && r.m_max == m_max; + return r.m_min == m_min && r.m_max == m_max; } bool operator!=(const MagnitudeRange &r) { return !(*this == r); @@ -37,20 +37,20 @@ bool isSet() const { return (m_min != 0.f || m_max != 0.f); } void set(float min, float max) { - m_min = min; - m_max = max; - if (m_max < m_min) m_max = m_min; + m_min = min; + m_max = max; + if (m_max < m_min) m_max = m_min; } bool sample(float f) { - bool changed = false; - if (isSet()) { - if (f < m_min) { m_min = f; changed = true; } - if (f > m_max) { m_max = f; changed = true; } - } else { - m_max = m_min = f; - changed = true; - } - return changed; + bool changed = false; + if (isSet()) { + if (f < m_min) { m_min = f; changed = true; } + if (f > m_max) { m_max = f; changed = true; } + } else { + m_max = m_min = f; + changed = true; + } + return changed; } bool sample(const std::vector &ff) { bool changed = false; @@ -62,16 +62,16 @@ return changed; } bool sample(const MagnitudeRange &r) { - bool changed = false; - if (isSet()) { - if (r.m_min < m_min) { m_min = r.m_min; changed = true; } - if (r.m_max > m_max) { m_max = r.m_max; changed = true; } - } else { - m_min = r.m_min; - m_max = r.m_max; - changed = true; - } - return changed; + bool changed = false; + if (isSet()) { + if (r.m_min < m_min) { m_min = r.m_min; changed = true; } + if (r.m_max > m_max) { m_max = r.m_max; changed = true; } + } else { + m_min = r.m_min; + m_max = r.m_max; + changed = true; + } + return changed; } float getMin() const { return m_min; } float getMax() const { return m_max; } diff -r d4a28d1479a8 -r 710e6250a401 base/Pitch.cpp --- a/base/Pitch.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Pitch.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -21,8 +21,8 @@ double Pitch::getFrequencyForPitch(int midiPitch, - double centsOffset, - double concertA) + double centsOffset, + double concertA) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); @@ -33,8 +33,8 @@ int Pitch::getPitchForFrequency(double frequency, - double *centsOffsetReturn, - double concertA) + double *centsOffsetReturn, + double concertA) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); @@ -45,12 +45,12 @@ double centsOffset = (p - midiPitch) * 100.0; if (centsOffset >= 50.0) { - midiPitch = midiPitch + 1; - centsOffset = -(100.0 - centsOffset); + midiPitch = midiPitch + 1; + centsOffset = -(100.0 - centsOffset); } if (centsOffset < -50.0) { - midiPitch = midiPitch - 1; - centsOffset = (100.0 + centsOffset); + midiPitch = midiPitch - 1; + centsOffset = (100.0 + centsOffset); } if (centsOffsetReturn) *centsOffsetReturn = centsOffset; @@ -80,8 +80,8 @@ double centsOffset = (p - midiPitch) * 100.0; if (centsOffset >= 50.0) { - midiPitch = midiPitch + 1; - centsOffset = -(100.0 - centsOffset); + midiPitch = midiPitch + 1; + centsOffset = -(100.0 - centsOffset); } if (centsOffsetReturn) *centsOffsetReturn = centsOffset; @@ -120,12 +120,12 @@ // spelled from a MIDI pitch + flats flag in isolation. if (midiPitch < 0) { - while (midiPitch < 0) { - midiPitch += 12; - --octave; - } + while (midiPitch < 0) { + midiPitch += 12; + --octave; + } } else { - octave = midiPitch / 12 + baseOctave; + octave = midiPitch / 12 + baseOctave; } note = midiPitch % 12; @@ -133,8 +133,8 @@ QString Pitch::getPitchLabel(int midiPitch, - double centsOffset, - bool useFlats) + double centsOffset, + bool useFlats) { int note, octave; getNoteAndOctaveForPitch(midiPitch, note, octave); @@ -149,8 +149,8 @@ QString Pitch::getPitchLabelForFrequency(double frequency, - double concertA, - bool useFlats) + double concertA, + bool useFlats) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); diff -r d4a28d1479a8 -r 710e6250a401 base/Pitch.h --- a/base/Pitch.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Pitch.h Mon Sep 17 13:51:14 2018 +0100 @@ -31,8 +31,8 @@ * specified in the application preferences (default 440Hz). */ static double getFrequencyForPitch(int midiPitch, - double centsOffset = 0, - double concertA = 0.0); + double centsOffset = 0, + double concertA = 0.0); /** * Return the nearest MIDI pitch to the given frequency. @@ -47,16 +47,16 @@ * specified in the application preferences (default 440Hz). */ static int getPitchForFrequency(double frequency, - double *centsOffsetReturn = 0, - double concertA = 0.0); + double *centsOffsetReturn = 0, + double concertA = 0.0); /** * Compatibility version of getPitchForFrequency accepting float * pointer argument. */ static int getPitchForFrequency(double frequency, - float *centsOffsetReturn, - double concertA = 0.0) { + float *centsOffsetReturn, + double concertA = 0.0) { double c; int p = getPitchForFrequency(frequency, &c, concertA); if (centsOffsetReturn) *centsOffsetReturn = float(c); @@ -127,8 +127,8 @@ * e.g. Bb3 instead of A#3. */ static QString getPitchLabel(int midiPitch, - double centsOffset = 0, - bool useFlats = false); + double centsOffset = 0, + bool useFlats = false); /** * Return a string describing the nearest MIDI pitch to the given @@ -142,8 +142,8 @@ * e.g. Bb3 instead of A#3. */ static QString getPitchLabelForFrequency(double frequency, - double concertA = 0.0, - bool useFlats = false); + double concertA = 0.0, + bool useFlats = false); /** * Return a string describing the given pitch range in octaves, diff -r d4a28d1479a8 -r 710e6250a401 base/PlayParameterRepository.cpp --- a/base/PlayParameterRepository.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/PlayParameterRepository.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -39,8 +39,8 @@ if (!getPlayParameters(playable)) { - // Give all playables the same type of play parameters for the - // moment + // Give all playables the same type of play parameters for the + // moment // cerr << "PlayParameterRepository:addPlayable: Adding play parameters for " << playable << endl; @@ -121,8 +121,8 @@ { // cerr << "PlayParameterRepository: PlayParameterRepository::clear" << endl; while (!m_playParameters.empty()) { - delete m_playParameters.begin()->second; - m_playParameters.erase(m_playParameters.begin()); + delete m_playParameters.begin()->second; + m_playParameters.erase(m_playParameters.begin()); } } diff -r d4a28d1479a8 -r 710e6250a401 base/Profiler.cpp --- a/base/Profiler.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Profiler.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -100,7 +100,7 @@ fprintf(stderr, "\tCPU: \t%.9g ms/call \t[%d ms total]\n", (((double)pp.second.first * 1000.0 / - (double)pp.first) / CLOCKS_PER_SEC), + (double)pp.first) / CLOCKS_PER_SEC), int((double(pp.second.first) * 1000.0) / CLOCKS_PER_SEC)); fprintf(stderr, "\tReal: \t%s ms \t[%s ms total]\n", @@ -192,8 +192,8 @@ RealTime elapsedTime = RealTime::fromTimeval(tv) - m_startTime; cerr << "Profiler : id = " << m_c - << " - elapsed so far = " << ((elapsedCPU * 1000) / CLOCKS_PER_SEC) - << "ms CPU, " << elapsedTime << " real" << endl; + << " - elapsed so far = " << ((elapsedCPU * 1000) / CLOCKS_PER_SEC) + << "ms CPU, " << elapsedTime << " real" << endl; } Profiler::~Profiler() @@ -215,7 +215,7 @@ if (m_showOnDestruct) cerr << "Profiler : id = " << m_c << " - elapsed = " << ((elapsedCPU * 1000) / CLOCKS_PER_SEC) - << "ms CPU, " << elapsedTime << " real" << endl; + << "ms CPU, " << elapsedTime << " real" << endl; m_ended = true; } diff -r d4a28d1479a8 -r 710e6250a401 base/ProgressReporter.cpp --- a/base/ProgressReporter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ProgressReporter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -16,7 +16,7 @@ #include "ProgressReporter.h" ProgressReporter::ProgressReporter(QObject *parent) : - QObject(parent) + QObject(parent) { } diff -r d4a28d1479a8 -r 710e6250a401 base/ProgressReporter.h --- a/base/ProgressReporter.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ProgressReporter.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _PROGRESS_REPORTER_H_ -#define _PROGRESS_REPORTER_H_ +#ifndef SV_PROGRESS_REPORTER_H +#define SV_PROGRESS_REPORTER_H #include #include diff -r d4a28d1479a8 -r 710e6250a401 base/PropertyContainer.cpp --- a/base/PropertyContainer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/PropertyContainer.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -178,6 +178,7 @@ case ValueProperty: case ColourProperty: + case ColourMapProperty: { int min, max; getPropertyRangeAndValue(name, &min, &max, 0); @@ -222,8 +223,8 @@ } PropertyContainer::SetPropertyCommand::SetPropertyCommand(PropertyContainer *pc, - const PropertyName &pn, - int value) : + const PropertyName &pn, + int value) : m_pc(pc), m_pn(pn), m_value(value), diff -r d4a28d1479a8 -r 710e6250a401 base/PropertyContainer.h --- a/base/PropertyContainer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/PropertyContainer.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _PROPERTY_CONTAINER_H_ -#define _PROPERTY_CONTAINER_H_ +#ifndef SV_PROPERTY_CONTAINER_H +#define SV_PROPERTY_CONTAINER_H #include "Command.h" @@ -36,12 +36,13 @@ typedef std::vector PropertyList; enum PropertyType { - ToggleProperty, // on or off - RangeProperty, // range of integers - ValueProperty, // range of integers given string labels - ColourProperty, // colours, get/set as ColourDatabase indices + ToggleProperty, // on or off + RangeProperty, // range of integers + ValueProperty, // range of integers given string labels + ColourProperty, // colours, get/set as ColourDatabase indices + ColourMapProperty, // colour maps, get/set as ColourMapper::StandardMap enum UnitsProperty, // unit from UnitDatabase, get/set unit id - InvalidProperty, // property not found! + InvalidProperty, // property not found! }; /** @@ -81,14 +82,14 @@ * passed as NULL if their values are not required. */ virtual int getPropertyRangeAndValue(const PropertyName &, - int *min, int *max, int *deflt) const; + int *min, int *max, int *deflt) const; /** * If the given property is a ValueProperty, return the display * label to be used for the given value for that property. */ virtual QString getPropertyValueLabel(const PropertyName &, - int value) const; + int value) const; /** * If the given property is a ValueProperty, return the icon to be @@ -158,18 +159,18 @@ class SetPropertyCommand : public Command { public: - SetPropertyCommand(PropertyContainer *pc, const PropertyName &pn, int); - virtual ~SetPropertyCommand() { } + SetPropertyCommand(PropertyContainer *pc, const PropertyName &pn, int); + virtual ~SetPropertyCommand() { } - virtual void execute(); - virtual void unexecute(); - virtual QString getName() const; + virtual void execute(); + virtual void unexecute(); + virtual QString getName() const; protected: - PropertyContainer *m_pc; - PropertyName m_pn; - int m_value; - int m_oldValue; + PropertyContainer *m_pc; + PropertyName m_pn; + int m_value; + int m_oldValue; }; virtual bool convertPropertyStrings(QString nameString, QString valueString, diff -r d4a28d1479a8 -r 710e6250a401 base/RangeMapper.cpp --- a/base/RangeMapper.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/RangeMapper.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -22,7 +22,7 @@ #include LinearRangeMapper::LinearRangeMapper(int minpos, int maxpos, - double minval, double maxval, + double minval, double maxval, QString unit, bool inverted, std::map labels) : m_minpos(minpos), diff -r d4a28d1479a8 -r 710e6250a401 base/RangeMapper.h --- a/base/RangeMapper.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/RangeMapper.h Mon Sep 17 13:51:14 2018 +0100 @@ -129,7 +129,7 @@ static void convertMinMax(int minpos, int maxpos, double minval, double maxval, - double &ratio, double &minlog); + double &minlog, double &ratio); virtual int getPositionForValue(double value) const; virtual int getPositionForValueUnclamped(double value) const; diff -r d4a28d1479a8 -r 710e6250a401 base/RealTime.h --- a/base/RealTime.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/RealTime.h Mon Sep 17 13:51:14 2018 +0100 @@ -51,10 +51,10 @@ RealTime(int s, int n); RealTime(const RealTime &r) : - sec(r.sec), nsec(r.nsec) { } + sec(r.sec), nsec(r.nsec) { } RealTime(const Vamp::RealTime &r) : - sec(r.sec), nsec(r.nsec) { } + sec(r.sec), nsec(r.nsec) { } static RealTime fromSeconds(double sec); static RealTime fromMilliseconds(int msec); @@ -65,27 +65,27 @@ Vamp::RealTime toVampRealTime() const { return Vamp::RealTime(sec, nsec); } RealTime &operator=(const RealTime &r) { - sec = r.sec; nsec = r.nsec; return *this; + sec = r.sec; nsec = r.nsec; return *this; } RealTime operator+(const RealTime &r) const { - return RealTime(sec + r.sec, nsec + r.nsec); + return RealTime(sec + r.sec, nsec + r.nsec); } RealTime operator-(const RealTime &r) const { - return RealTime(sec - r.sec, nsec - r.nsec); + return RealTime(sec - r.sec, nsec - r.nsec); } RealTime operator-() const { - return RealTime(-sec, -nsec); + return RealTime(-sec, -nsec); } bool operator <(const RealTime &r) const { - if (sec == r.sec) return nsec < r.nsec; - else return sec < r.sec; + if (sec == r.sec) return nsec < r.nsec; + else return sec < r.sec; } bool operator >(const RealTime &r) const { - if (sec == r.sec) return nsec > r.nsec; - else return sec > r.sec; + if (sec == r.sec) return nsec > r.nsec; + else return sec > r.sec; } bool operator==(const RealTime &r) const { diff -r d4a28d1479a8 -r 710e6250a401 base/RealTimeSV.cpp --- a/base/RealTimeSV.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/RealTimeSV.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -19,6 +19,7 @@ */ #include +#include #include #include @@ -43,16 +44,10 @@ RealTime::RealTime(int s, int n) : sec(s), nsec(n) { - if (sec == 0) { - while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } - while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } - } else if (sec < 0) { - while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; } - while (nsec > 0 && sec < 0) { nsec -= ONE_BILLION; ++sec; } - } else { - while (nsec >= ONE_BILLION) { nsec -= ONE_BILLION; ++sec; } - while (nsec < 0 && sec > 0) { nsec += ONE_BILLION; --sec; } - } + while (nsec <= -ONE_BILLION && sec > INT_MIN) { nsec += ONE_BILLION; --sec; } + while (nsec >= ONE_BILLION && sec < INT_MAX) { nsec -= ONE_BILLION; ++sec; } + while (nsec > 0 && sec < 0) { nsec -= ONE_BILLION; ++sec; } + while (nsec < 0 && sec > 0) { nsec += ONE_BILLION; --sec; } } RealTime @@ -174,9 +169,9 @@ std::ostream &operator<<(std::ostream &out, const RealTime &rt) { if (rt < RealTime::zeroTime) { - out << "-"; + out << "-"; } else { - out << " "; + out << " "; } int s = (rt.sec < 0 ? -rt.sec : rt.sec); @@ -187,8 +182,8 @@ int nn(n); if (nn == 0) out << "00000000"; else while (nn < (ONE_BILLION / 10)) { - out << "0"; - nn *= 10; + out << "0"; + nn *= 10; } out << n << "R"; @@ -319,24 +314,24 @@ int ms = msec(); if (ms != 0) { - out << "."; - out << (ms / 100); - ms = ms % 100; - if (ms != 0) { - out << (ms / 10); - ms = ms % 10; - } else if (fixedDp) { - out << "0"; - } - if (ms != 0) { - out << ms; - } else if (fixedDp) { - out << "0"; - } + out << "."; + out << (ms / 100); + ms = ms % 100; + if (ms != 0) { + out << (ms / 10); + ms = ms % 10; + } else if (fixedDp) { + out << "0"; + } + if (ms != 0) { + out << ms; + } else if (fixedDp) { + out << "0"; + } } else if (fixedDp) { - out << ".000"; + out << ".000"; } - + std::string s = out.str(); return s; @@ -371,7 +366,7 @@ out << d; div /= 10; } - + std::string s = out.str(); // cerr << "converted " << toString() << " to " << s << endl; diff -r d4a28d1479a8 -r 710e6250a401 base/RecordDirectory.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/RecordDirectory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,67 @@ +/* -*- 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 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 "RecordDirectory.h" +#include "TempDirectory.h" + +#include +#include + +#include "Debug.h" + +QString +RecordDirectory::getRecordContainerDirectory() +{ + QDir parent(TempDirectory::getInstance()->getContainingPath()); + QString subdirname("recorded"); + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: RecordDirectory::getRecordContainerDirectory: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + +QString +RecordDirectory::getRecordDirectory() +{ + QDir parent(getRecordContainerDirectory()); + QDateTime now = QDateTime::currentDateTime(); + QString subdirname = QString("%1").arg(now.toString("yyyyMMdd")); + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: RecordDirectory::getRecordDirectory: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + +QString +RecordDirectory::getConvertedAudioDirectory() +{ + QDir parent(getRecordContainerDirectory()); + QDateTime now = QDateTime::currentDateTime(); + QString subdirname = "converted"; + + if (!parent.mkpath(subdirname)) { + SVCERR << "ERROR: RecordDirectory::getConvertedAudioDirectory: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl; + return ""; + } else { + return parent.filePath(subdirname); + } +} + + diff -r d4a28d1479a8 -r 710e6250a401 base/RecordDirectory.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/RecordDirectory.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,59 @@ +/* -*- 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 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 SV_RECORD_DIRECTORY_H +#define SV_RECORD_DIRECTORY_H + +#include + +/** + * Report the intended target location for recorded audio files. + */ +class RecordDirectory +{ +public: + /** + * Return the directory in which a recorded file should be saved. + * This may vary depending on the current date and time, and so + * should be queried afresh for each recording. The directory will + * also be created if it does not yet exist. + * + * Returns an empty string if the record directory did not exist + * and could not be created. + */ + static QString getRecordDirectory(); + + /** + * Return the root "recorded files" directory. If + * getRecordDirectory() is returning a datestamped directory, then + * this will be its parent. The directory will also be created if + * it does not yet exist. + * + * Returns an empty string if the record directory did not exist + * and could not be created. + */ + static QString getRecordContainerDirectory(); + + /** + * Return the directory in which an audio file converted from a + * data file should be saved. The directory will also be created if + * it does not yet exist. + * + * Returns an empty string if the directory did not exist and + * could not be created. + */ + static QString getConvertedAudioDirectory(); +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 base/ResourceFinder.cpp --- a/base/ResourceFinder.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ResourceFinder.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -35,6 +35,8 @@ #include #include +#include "system/System.h" + /** Resource files may be found in three places: @@ -67,10 +69,11 @@ QStringList list; #ifdef Q_OS_WIN32 - char *programFiles = getenv("ProgramFiles"); - if (programFiles && programFiles[0]) { + std::string programFiles; + (void)getEnvUtf8("ProgramFiles", programFiles); + if (programFiles != "") { list << QString("%1/%2/%3") - .arg(programFiles) + .arg(QString::fromStdString(programFiles)) .arg(qApp->organizationName()) .arg(qApp->applicationName()); } else { @@ -133,14 +136,22 @@ } #if QT_VERSION >= 0x050000 + // This is expected to be much more reliable than // getOldStyleUserResourcePrefix(), but it returns a different // directory because it includes the organisation name (which is // fair enough). Hence migrateOldStyleResources() which moves // across any resources found in the old-style path the first time // we look for the new-style one +#if QT_VERSION >= 0x050400 return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); #else + cerr << "WARNING: ResourceFinder::getOldStyleUserResourcePrefix: Building with older version of Qt (pre 5.4), resource location may be incompatible with future versions" << endl; + return QStandardPaths::writableLocation(QStandardPaths::DataLocation); +#endif + +#else + cerr << "WARNING: ResourceFinder::getOldStyleUserResourcePrefix: Building with very old version of Qt (pre 5.0?), resource location may be incompatible with future versions" << endl; return getOldStyleUserResourcePrefix(); #endif } @@ -239,12 +250,12 @@ QString prefix = *i; - SVDEBUG << "ResourceFinder::getResourcePath: Looking up file \"" << fileName << "\" for category \"" << resourceCat << "\" in prefix \"" << prefix << "\"" << endl; +// cerr << "ResourceFinder::getResourcePath: Looking up file \"" << fileName << "\" for category \"" << resourceCat << "\" in prefix \"" << prefix << "\"" << endl; QString path = QString("%1%2/%3").arg(prefix).arg(resourceCat).arg(fileName); if (QFileInfo(path).exists() && QFileInfo(path).isReadable()) { - cerr << "Found it!" << endl; +// cerr << "Found it!" << endl; return path; } } diff -r d4a28d1479a8 -r 710e6250a401 base/ResourceFinder.h --- a/base/ResourceFinder.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ResourceFinder.h Mon Sep 17 13:51:14 2018 +0100 @@ -25,7 +25,7 @@ #include #include "Debug.h" - + class ResourceFinder { public: diff -r d4a28d1479a8 -r 710e6250a401 base/RingBuffer.h --- a/base/RingBuffer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/RingBuffer.h Mon Sep 17 13:51:14 2018 +0100 @@ -210,7 +210,7 @@ delete[] m_readers; if (m_mlocked) { - MUNLOCK((void *)m_buffer, m_size * sizeof(T)); + MUNLOCK((void *)m_buffer, m_size * sizeof(T)); } delete[] m_buffer; } @@ -293,15 +293,15 @@ { int space = 0; for (int i = 0; i < N; ++i) { - int here = (m_readers[i] + m_size - m_writer - 1) % m_size; - if (i == 0 || here < space) space = here; + int here = (m_readers[i] + m_size - m_writer - 1) % m_size; + if (i == 0 || here < space) space = here; } #ifdef DEBUG_RINGBUFFER int rs(getReadSpace()), rp(m_readers[0]); std::cerr << "RingBuffer: write space " << space << ", read space " - << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl; + << rs << ", total " << (space + rs) << ", m_size " << m_size << std::endl; std::cerr << "RingBuffer: reader " << rp << ", writer " << m_writer << std::endl; #endif @@ -323,20 +323,20 @@ int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; + std::cerr << "WARNING: Only " << available << " samples available" + << std::endl; #endif - memset(destination + available, 0, (n - available) * sizeof(T)); - n = available; + memset(destination + available, 0, (n - available) * sizeof(T)); + n = available; } if (n == 0) return n; int here = m_size - m_readers[R]; if (here >= n) { - memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); + memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); } else { - memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); - memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); + memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); + memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); } MBARRIER(); @@ -360,26 +360,26 @@ int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; + std::cerr << "WARNING: Only " << available << " samples available" + << std::endl; #endif - n = available; + n = available; } if (n == 0) return n; int here = m_size - m_readers[R]; if (here >= n) { - for (int i = 0; i < n; ++i) { - destination[i] += (m_buffer + m_readers[R])[i]; - } + for (int i = 0; i < n; ++i) { + destination[i] += (m_buffer + m_readers[R])[i]; + } } else { - for (int i = 0; i < here; ++i) { - destination[i] += (m_buffer + m_readers[R])[i]; - } - for (int i = 0; i < (n - here); ++i) { - destination[i + here] += m_buffer[i]; - } + for (int i = 0; i < here; ++i) { + destination[i] += (m_buffer + m_readers[R])[i]; + } + for (int i = 0; i < (n - here); ++i) { + destination[i + here] += m_buffer[i]; + } } MBARRIER(); @@ -397,12 +397,12 @@ if (m_writer == m_readers[R]) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: No sample available" - << std::endl; + std::cerr << "WARNING: No sample available" + << std::endl; #endif - T t; - memset(&t, 0, sizeof(T)); - return t; + T t; + memset(&t, 0, sizeof(T)); + return t; } T value = m_buffer[m_readers[R]]; MBARRIER(); @@ -421,20 +421,20 @@ int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; + std::cerr << "WARNING: Only " << available << " samples available" + << std::endl; #endif - memset(destination + available, 0, (n - available) * sizeof(T)); - n = available; + memset(destination + available, 0, (n - available) * sizeof(T)); + n = available; } if (n == 0) return n; int here = m_size - m_readers[R]; if (here >= n) { - memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); + memcpy(destination, m_buffer + m_readers[R], n * sizeof(T)); } else { - memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); - memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); + memcpy(destination, m_buffer + m_readers[R], here * sizeof(T)); + memcpy(destination + here, m_buffer, (n - here) * sizeof(T)); } #ifdef DEBUG_RINGBUFFER @@ -454,12 +454,12 @@ if (m_writer == m_readers[R]) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: No sample available" - << std::endl; + std::cerr << "WARNING: No sample available" + << std::endl; #endif - T t; - memset(&t, 0, sizeof(T)); - return t; + T t; + memset(&t, 0, sizeof(T)); + return t; } T value = m_buffer[m_readers[R]]; return value; @@ -476,10 +476,10 @@ int available = getReadSpace(R); if (n > available) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only " << available << " samples available" - << std::endl; + std::cerr << "WARNING: Only " << available << " samples available" + << std::endl; #endif - n = available; + n = available; } if (n == 0) return n; m_readers[R] = (m_readers[R] + n) % m_size; @@ -497,19 +497,19 @@ int available = getWriteSpace(); if (n > available) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only room for " << available << " samples" - << std::endl; + std::cerr << "WARNING: Only room for " << available << " samples" + << std::endl; #endif - n = available; + n = available; } if (n == 0) return n; int here = m_size - m_writer; if (here >= n) { - memcpy(m_buffer + m_writer, source, n * sizeof(T)); + memcpy(m_buffer + m_writer, source, n * sizeof(T)); } else { - memcpy(m_buffer + m_writer, source, here * sizeof(T)); - memcpy(m_buffer, source + here, (n - here) * sizeof(T)); + memcpy(m_buffer + m_writer, source, here * sizeof(T)); + memcpy(m_buffer, source + here, (n - here) * sizeof(T)); } MBARRIER(); @@ -533,19 +533,19 @@ int available = getWriteSpace(); if (n > available) { #ifdef DEBUG_RINGBUFFER - std::cerr << "WARNING: Only room for " << available << " samples" - << std::endl; + std::cerr << "WARNING: Only room for " << available << " samples" + << std::endl; #endif - n = available; + n = available; } if (n == 0) return n; int here = m_size - m_writer; if (here >= n) { - memset(m_buffer + m_writer, 0, n * sizeof(T)); + memset(m_buffer + m_writer, 0, n * sizeof(T)); } else { - memset(m_buffer + m_writer, 0, here * sizeof(T)); - memset(m_buffer, 0, (n - here) * sizeof(T)); + memset(m_buffer + m_writer, 0, here * sizeof(T)); + memset(m_buffer, 0, (n - here) * sizeof(T)); } MBARRIER(); diff -r d4a28d1479a8 -r 710e6250a401 base/ScaleTickIntervals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/ScaleTickIntervals.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,385 @@ +/* -*- 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-2017 Chris Cannam and 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 SV_SCALE_TICK_INTERVALS_H +#define SV_SCALE_TICK_INTERVALS_H + +#include +#include +#include + +#include "LogRange.h" +#include "Debug.h" + +// Can't have this on by default, as we're called on every refresh +//#define DEBUG_SCALE_TICK_INTERVALS 1 + +class ScaleTickIntervals +{ +public: + struct Range { + double min; // start of value range + double max; // end of value range + int n; // number of divisions (approximate only) + }; + + struct Tick { + double value; // value this tick represents + std::string label; // value as written + }; + + typedef std::vector Ticks; + + /** + * Return a set of ticks that divide the range r linearly into + * roughly r.n equal divisions, in such a way as to yield + * reasonably human-readable labels. + */ + static Ticks linear(Range r) { + return linearTicks(r); + } + + /** + * Return a set of ticks that divide the range r into roughly r.n + * logarithmic divisions, in such a way as to yield reasonably + * human-readable labels. + */ + static Ticks logarithmic(Range r) { + LogRange::mapRange(r.min, r.max); + return logarithmicAlready(r); + } + + /** + * Return a set of ticks that divide the range r into roughly r.n + * logarithmic divisions, on the asssumption that r.min and r.max + * already represent the logarithms of the boundary values rather + * than the values themselves. + */ + static Ticks logarithmicAlready(Range r) { + return logTicks(r); + } + +private: + enum Display { + Fixed, + Scientific, + Auto + }; + + struct Instruction { + double initial; // value of first tick + double limit; // max from original range + double spacing; // increment between ticks + double roundTo; // what all displayed values should be rounded to + // (if 0.0, then calculate based on precision) + Display display; // whether to use fixed precision (%e, %f, or %g) + int precision; // number of dp (%f) or sf (%e) + bool logUnmap; // true if values represent logs of display values + }; + + static Instruction linearInstruction(Range r) + { + Display display = Auto; + + if (r.max < r.min) { + return linearInstruction({ r.max, r.min, r.n }); + } + if (r.n < 1 || r.max == r.min) { + return { r.min, r.min, 1.0, r.min, display, 1, false }; + } + + double inc = (r.max - r.min) / r.n; + + double digInc = log10(inc); + double digMax = log10(fabs(r.max)); + double digMin = log10(fabs(r.min)); + + int precInc = int(floor(digInc)); + double roundTo = pow(10.0, precInc); + + if (precInc > -4 && precInc < 4) { + display = Fixed; + } else if ((digMax >= -2.0 && digMax <= 3.0) && + (digMin >= -3.0 && digMin <= 3.0)) { + display = Fixed; + } else { + display = Scientific; + } + + int precRange = int(ceil(digMax - digInc)); + + int prec = 1; + + if (display == Fixed) { + if (digInc < 0) { + prec = -precInc; + } else { + prec = 0; + } + } else { + prec = precRange; + } + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals: calculating linearInstruction" << endl + << "ScaleTickIntervals: min = " << r.min << ", max = " << r.max + << ", n = " << r.n << ", inc = " << inc << endl; + SVDEBUG << "ScaleTickIntervals: digMax = " << digMax + << ", digInc = " << digInc << endl; + SVDEBUG << "ScaleTickIntervals: display = " << display + << ", inc = " << inc << ", precInc = " << precInc + << ", precRange = " << precRange + << ", prec = " << prec << ", roundTo = " << roundTo + << endl; +#endif + + double min = r.min; + + if (roundTo != 0.0) { + // Round inc to the nearest multiple of roundTo, and min + // to the next multiple of roundTo up. The small offset of + // eps is included to avoid inc of 2.49999999999 rounding + // to 2 or a min of -0.9999999999 rounding to 0, both of + // which would prevent some of our test cases from getting + // the most natural results. + double eps = 1e-7; + inc = round(inc / roundTo + eps) * roundTo; + if (inc < roundTo) inc = roundTo; + min = ceil(min / roundTo - eps) * roundTo; + if (min > r.max) min = r.max; + if (min == -0.0) min = 0.0; +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals: rounded inc to " << inc + << " and min to " << min << endl; +#endif + } + + if (display == Scientific && min != 0.0) { + double digNewMin = log10(fabs(min)); + if (digNewMin < digInc) { + prec = int(ceil(digMax - digNewMin)); +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals: min is smaller than increment, adjusting prec to " << prec << endl; +#endif + } + } + + return { min, r.max, inc, roundTo, display, prec, false }; + } + + static Instruction logInstruction(Range r) + { + Display display = Auto; + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals::logInstruction: Range is " + << r.min << " to " << r.max << endl; +#endif + + if (r.n < 1) { + return {}; + } + if (r.max < r.min) { + return logInstruction({ r.max, r.min, r.n }); + } + if (r.max == r.min) { + return { r.min, r.max, 1.0, r.min, display, 1, true }; + } + + double inc = (r.max - r.min) / r.n; + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals::logInstruction: " + << "Naive increment is " << inc << endl; +#endif + + int precision = 1; + + if (inc < 1.0) { + precision = int(ceil(1.0 - inc)) + 1; + } + + double digInc = log10(inc); + int precInc = int(floor(digInc)); + double roundIncTo = pow(10.0, precInc); + + inc = round(inc / roundIncTo) * roundIncTo; + if (inc < roundIncTo) inc = roundIncTo; + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals::logInstruction: " + << "Rounded increment to " << inc << endl; +#endif + + // if inc is close to giving us powers of two, nudge it + if (fabs(inc - 0.301) < 0.01) { + inc = log10(2.0); + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals::logInstruction: " + << "Nudged increment to " << inc << " to get powers of two" + << endl; +#endif + } + + double min = r.min; + if (inc != 0.0) { + min = ceil(r.min / inc) * inc; + if (min > r.max) min = r.max; + } + + return { min, r.max, inc, 0.0, display, precision, true }; + } + + static Ticks linearTicks(Range r) { + Instruction instruction = linearInstruction(r); + Ticks ticks = explode(instruction); + return ticks; + } + + static Ticks logTicks(Range r) { + Instruction instruction = logInstruction(r); + Ticks ticks = explode(instruction); + return ticks; + } + + static Tick makeTick(Display display, int precision, double value) { + + if (value == -0.0) { + value = 0.0; + } + + const int buflen = 40; + char buffer[buflen]; + + if (display == Auto) { + + double eps = 1e-7; + + int digits = (value != 0.0 ? + 1 + int(floor(eps + log10(fabs(value)))) : + 0); + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "makeTick: display = Auto, precision = " + << precision << ", value = " << value + << ", resulting digits = " << digits << endl; +#endif + + // This is not the same logic as %g uses for determining + // whether to delegate to use scientific or fixed notation + + if (digits < -3 || digits > 4) { + + display = Auto; // delegate planning to %g + + } else { + + display = Fixed; + + // in %.*f, the * indicates decimal places, not sig figs + if (precision >= digits) { + precision -= digits; + } else { + precision = 0; + } + } + } + + const char *spec = (display == Auto ? "%.*g" : + display == Scientific ? "%.*e" : + "%.*f"); + +#pragma GCC diagnostic ignored "-Wformat-nonliteral" + + snprintf(buffer, buflen, spec, precision, value); + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "makeTick: spec = \"" << spec + << "\", prec = " << precision << ", value = " << value + << ", label = \"" << buffer << "\"" << endl; +#endif + + return Tick({ value, std::string(buffer) }); + } + + static Ticks explode(Instruction instruction) { + +#ifdef DEBUG_SCALE_TICK_INTERVALS + SVDEBUG << "ScaleTickIntervals::explode:" << endl + << "initial = " << instruction.initial + << ", limit = " << instruction.limit + << ", spacing = " << instruction.spacing + << ", roundTo = " << instruction.roundTo + << ", display = " << instruction.display + << ", precision = " << instruction.precision + << ", logUnmap = " << instruction.logUnmap + << endl; +#endif + + if (instruction.spacing == 0.0) { + return {}; + } + + double eps = 1e-7; + if (instruction.spacing < eps * 10.0) { + eps = instruction.spacing / 10.0; + } + + double max = instruction.limit; + int n = 0; + + Ticks ticks; + + while (true) { + + double value = instruction.initial + n * instruction.spacing; + + if (value >= max + eps) { + break; + } + + if (instruction.logUnmap) { + value = pow(10.0, value); + } + + double roundTo = instruction.roundTo; + + if (roundTo == 0.0 && value != 0.0) { + // We don't want the internal value secretly not + // matching the displayed one + roundTo = + pow(10, ceil(log10(fabs(value))) - instruction.precision); + } + + if (roundTo != 0.0) { + value = roundTo * round(value / roundTo); + } + + if (fabs(value) < eps) { + value = 0.0; + } + + ticks.push_back(makeTick(instruction.display, + instruction.precision, + value)); + ++n; + } + + return ticks; + } +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 base/Scavenger.h --- a/base/Scavenger.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Scavenger.h Mon Sep 17 13:51:14 2018 +0100 @@ -106,15 +106,15 @@ Scavenger::~Scavenger() { if (m_scavenged < m_claimed) { - for (size_t i = 0; i < m_objects.size(); ++i) { - ObjectTimePair &pair = m_objects[i]; - if (pair.first != 0) { - T *ot = pair.first; - pair.first = 0; - delete ot; - ++m_scavenged; - } - } + for (size_t i = 0; i < m_objects.size(); ++i) { + ObjectTimePair &pair = m_objects[i]; + if (pair.first != 0) { + T *ot = pair.first; + pair.first = 0; + delete ot; + ++m_scavenged; + } + } } clearExcess(0); @@ -131,17 +131,17 @@ time_t sec = tv.tv_sec; for (size_t i = 0; i < m_objects.size(); ++i) { - ObjectTimePair &pair = m_objects[i]; - if (pair.first == 0) { - pair.second = sec; - pair.first = t; - ++m_claimed; - return; - } + ObjectTimePair &pair = m_objects[i]; + if (pair.first == 0) { + pair.second = sec; + pair.first = t; + ++m_claimed; + return; + } } std::cerr << "WARNING: Scavenger::claim(" << t << "): run out of slots, " - << "using non-RT-safe method" << std::endl; + << "using non-RT-safe method" << std::endl; pushExcess(t); } @@ -158,14 +158,14 @@ time_t sec = tv.tv_sec; for (size_t i = 0; i < m_objects.size(); ++i) { - ObjectTimePair &pair = m_objects[i]; - if (clearNow || - (pair.first != 0 && pair.second + m_sec < sec)) { - T *ot = pair.first; - pair.first = 0; - delete ot; - ++m_scavenged; - } + ObjectTimePair &pair = m_objects[i]; + if (clearNow || + (pair.first != 0 && pair.second + m_sec < sec)) { + T *ot = pair.first; + pair.first = 0; + delete ot; + ++m_scavenged; + } } if (sec > m_lastExcess + m_sec) { @@ -191,8 +191,8 @@ { m_excessMutex.lock(); for (typename ObjectList::iterator i = m_excess.begin(); - i != m_excess.end(); ++i) { - delete *i; + i != m_excess.end(); ++i) { + delete *i; } m_excess.clear(); m_lastExcess = sec; diff -r d4a28d1479a8 -r 710e6250a401 base/Selection.cpp --- a/base/Selection.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Selection.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -27,9 +27,9 @@ m_endFrame(endFrame) { if (m_startFrame > m_endFrame) { - sv_frame_t tmp = m_endFrame; - m_endFrame = m_startFrame; - m_startFrame = tmp; + sv_frame_t tmp = m_endFrame; + m_endFrame = m_startFrame; + m_startFrame = tmp; } } @@ -43,8 +43,8 @@ Selection::operator=(const Selection &s) { if (this != &s) { - m_startFrame = s.m_startFrame; - m_endFrame = s.m_endFrame; + m_startFrame = s.m_startFrame; + m_endFrame = s.m_endFrame; } return *this; } @@ -81,11 +81,11 @@ Selection::operator<(const Selection &s) const { if (isEmpty()) { - if (s.isEmpty()) return false; - else return true; + if (s.isEmpty()) return false; + else return true; } else { - if (s.isEmpty()) return false; - else return (m_startFrame < s.m_startFrame); + if (s.isEmpty()) return false; + else return (m_startFrame < s.m_startFrame); } } @@ -95,7 +95,7 @@ if (isEmpty()) return s.isEmpty(); return (m_startFrame == s.m_startFrame && - m_endFrame == s.m_endFrame); + m_endFrame == s.m_endFrame); } @@ -134,21 +134,21 @@ // this is not just a frill. for (SelectionList::iterator i = m_selections.begin(); - i != m_selections.end(); ) { - - SelectionList::iterator j = i; - if (++j == m_selections.end()) break; + i != m_selections.end(); ) { + + SelectionList::iterator j = i; + if (++j == m_selections.end()) break; - if (i->getEndFrame() >= j->getStartFrame()) { - Selection merged(i->getStartFrame(), - std::max(i->getEndFrame(), j->getEndFrame())); - m_selections.erase(i); - m_selections.erase(j); - m_selections.insert(merged); - i = m_selections.begin(); - } else { - ++i; - } + if (i->getEndFrame() >= j->getStartFrame()) { + Selection merged(i->getStartFrame(), + std::max(i->getEndFrame(), j->getEndFrame())); + m_selections.erase(i); + m_selections.erase(j); + m_selections.insert(merged); + i = m_selections.begin(); + } else { + ++i; + } } } @@ -161,7 +161,7 @@ //appropriately) if (m_selections.find(selection) != m_selections.end()) { - m_selections.erase(selection); + m_selections.erase(selection); } } @@ -169,7 +169,7 @@ MultiSelection::clearSelections() { if (!m_selections.empty()) { - m_selections.clear(); + m_selections.clear(); } } @@ -180,7 +180,7 @@ endFrame = 0; for (SelectionList::const_iterator i = m_selections.begin(); - i != m_selections.end(); ++i) { + i != m_selections.end(); ++i) { if (i == m_selections.begin() || i->getStartFrame() < startFrame) { startFrame = i->getStartFrame(); @@ -200,14 +200,14 @@ // scalable method, and I think that may be what we need for (SelectionList::const_iterator i = m_selections.begin(); - i != m_selections.end(); ++i) { + i != m_selections.end(); ++i) { - if (i->contains(frame)) return *i; + if (i->contains(frame)) return *i; - if (i->getStartFrame() > frame) { - if (defaultToFollowing) return *i; - else return Selection(); - } + if (i->getStartFrame() > frame) { + if (defaultToFollowing) return *i; + else return Selection(); + } } return Selection(); @@ -219,10 +219,10 @@ { stream << indent << QString("\n").arg(extraAttributes); for (SelectionList::iterator i = m_selections.begin(); - i != m_selections.end(); ++i) { - stream << indent + i != m_selections.end(); ++i) { + stream << indent << QString(" \n") - .arg(i->getStartFrame()).arg(i->getEndFrame()); + .arg(i->getStartFrame()).arg(i->getEndFrame()); } stream << indent << "\n"; } diff -r d4a28d1479a8 -r 710e6250a401 base/Selection.h --- a/base/Selection.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Selection.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SELECTION_H_ -#define _SELECTION_H_ +#ifndef SV_SELECTION_H +#define SV_SELECTION_H #include #include diff -r d4a28d1479a8 -r 710e6250a401 base/StorageAdviser.cpp --- a/base/StorageAdviser.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/StorageAdviser.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -65,8 +65,8 @@ StorageAdviser::Recommendation StorageAdviser::recommend(Criteria criteria, - size_t minimumSize, - size_t maximumSize) + size_t minimumSize, + size_t maximumSize) { SVDEBUG << "StorageAdviser::recommend: criteria " << criteria << " (" + criteriaToString(criteria) + ")" @@ -83,7 +83,7 @@ QString path; try { path = TempDirectory::getInstance()->getPath(); - } catch (std::exception e) { + } catch (const std::exception &e) { SVDEBUG << "StorageAdviser::recommend: ERROR: Failed to get temporary directory path: " << e.what() << endl; int r = UseMemory | ConserveSpace; SVDEBUG << "StorageAdviser: returning fallback " << r @@ -97,6 +97,28 @@ SVDEBUG << "StorageAdviser: disc space: " << discFree << "M, memory free: " << memoryFree << "M, memory total: " << memoryTotal << "M" << endl; + + // In 32-bit addressing mode we can't address more than 4Gb. + // If the total memory is reported as more than 4Gb, we should + // reduce the available amount by the difference between 4Gb + // and the total. This won't give us an accurate idea of the + // amount of memory available any more, but it should be enough + // to prevent us from trying to allocate more for our own use + // than can be addressed at all! + if (sizeof(void *) < 8) { + if (memoryTotal > 4096) { + ssize_t excess = memoryTotal - 4096; + if (memoryFree > excess) { + memoryFree -= excess; + } else { + memoryFree = 0; + } + SVDEBUG << "StorageAdviser: more real memory found than we " + << "can address in a 32-bit process, reducing free " + << "estimate to " << memoryFree << "M accordingly" << endl; + } + } + SVDEBUG << "StorageAdviser: disc planned: " << (m_discPlanned / 1024) << "K, memory planned: " << (m_memoryPlanned / 1024) << "K" << endl; SVDEBUG << "StorageAdviser: min requested: " << minimumSize diff -r d4a28d1479a8 -r 710e6250a401 base/StringBits.cpp --- a/base/StringBits.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/StringBits.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -42,7 +42,7 @@ while (i < len) { - QChar c = s[i]; + QChar c = s[i]; if (c.isDigit()) { @@ -85,45 +85,45 @@ enum { sep, unq, q1, q2 } mode = sep; for (int i = 0; i < s.length(); ++i) { - - QChar c = s[i]; + + QChar c = s[i]; - if (c == '\'') { - switch (mode) { - case sep: mode = q1; break; - case unq: case q2: tok += c; break; - case q1: mode = unq; break; - } + if (c == '\'') { + switch (mode) { + case sep: mode = q1; break; + case unq: case q2: tok += c; break; + case q1: mode = unq; break; + } - } else if (c == '"') { - switch (mode) { - case sep: mode = q2; break; - case unq: case q1: tok += c; break; - case q2: mode = unq; break; - } + } else if (c == '"') { + switch (mode) { + case sep: mode = q2; break; + case unq: case q1: tok += c; break; + case q2: mode = unq; break; + } - } else if (c == separator || (separator == ' ' && c.isSpace())) { - switch (mode) { - case sep: if (separator != ' ') tokens << ""; break; - case unq: mode = sep; tokens << tok; tok = ""; break; - case q1: case q2: tok += c; break; - } + } else if (c == separator || (separator == ' ' && c.isSpace())) { + switch (mode) { + case sep: if (separator != ' ') tokens << ""; break; + case unq: mode = sep; tokens << tok; tok = ""; break; + case q1: case q2: tok += c; break; + } - } else if (c == '\\') { - if (++i < s.length()) { - c = s[i]; - switch (mode) { - case sep: mode = unq; tok += c; break; + } else if (c == '\\') { + if (++i < s.length()) { + c = s[i]; + switch (mode) { + case sep: mode = unq; tok += c; break; case unq: case q1: case q2: tok += c; break; - } - } + } + } - } else { - switch (mode) { - case sep: mode = unq; tok += c; break; + } else { + switch (mode) { + case sep: mode = unq; tok += c; break; case unq: case q1: case q2: tok += c; break; - } - } + } + } } if (tok != "" || mode != sep) { diff -r d4a28d1479a8 -r 710e6250a401 base/StringBits.h --- a/base/StringBits.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/StringBits.h Mon Sep 17 13:51:14 2018 +0100 @@ -18,8 +18,8 @@ This file copyright 2000-2010 Chris Cannam. */ -#ifndef _STRING_BITS_H_ -#define _STRING_BITS_H_ +#ifndef SV_STRING_BITS_H +#define SV_STRING_BITS_H #include #include diff -r d4a28d1479a8 -r 710e6250a401 base/TempDirectory.h --- a/base/TempDirectory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/TempDirectory.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _TEMP_DIRECTORY_H_ -#define _TEMP_DIRECTORY_H_ +#ifndef SV_TEMP_DIRECTORY_H +#define SV_TEMP_DIRECTORY_H #include #include diff -r d4a28d1479a8 -r 710e6250a401 base/TempWriteFile.cpp --- a/base/TempWriteFile.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/TempWriteFile.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -27,8 +27,8 @@ temp.setAutoRemove(false); temp.open(); // creates the file and opens it atomically if (temp.error()) { - cerr << "TempWriteFile: Failed to create temporary file in directory of " << m_target << ": " << temp.errorString() << endl; - throw FileOperationFailed(temp.fileName(), "creation"); + SVCERR << "TempWriteFile: Failed to create temporary file in directory of " << m_target << ": " << temp.errorString() << endl; + throw FileOperationFailed(temp.fileName(), "creation"); } m_temp = temp.fileName(); @@ -38,8 +38,8 @@ TempWriteFile::~TempWriteFile() { if (m_temp != "") { - QDir dir(QFileInfo(m_temp).dir()); - dir.remove(m_temp); + QDir dir(QFileInfo(m_temp).dir()); + dir.remove(m_temp); } } @@ -54,14 +54,18 @@ { if (m_temp == "") return; - QDir dir(QFileInfo(m_temp).dir()); - // According to http://doc.trolltech.com/4.4/qdir.html#rename - // some systems fail, if renaming over an existing file. - // Therefore, delete first the existing file. - if (dir.exists(m_target)) dir.remove(m_target); - if (!dir.rename(m_temp, m_target)) { - cerr << "TempWriteFile: Failed to rename temporary file " << m_temp << " to target " << m_target << endl; - throw FileOperationFailed(m_temp, "rename"); + QFile tempFile(m_temp); + QFile targetFile(m_target); + + if (targetFile.exists()) { + if (!targetFile.remove()) { + SVCERR << "TempWriteFile: WARNING: Failed to remove existing target file " << m_target << " prior to moving temporary file " << m_temp << " to it" << endl; + } + } + + if (!tempFile.rename(m_target)) { + SVCERR << "TempWriteFile: Failed to rename temporary file " << m_temp << " to target " << m_target << endl; + throw FileOperationFailed(m_temp, "rename"); } m_temp = ""; diff -r d4a28d1479a8 -r 710e6250a401 base/TempWriteFile.h --- a/base/TempWriteFile.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/TempWriteFile.h Mon Sep 17 13:51:14 2018 +0100 @@ -12,8 +12,8 @@ COPYING included with this distribution for more information. */ -#ifndef _TEMP_WRITE_FILE_H_ -#define _TEMP_WRITE_FILE_H_ +#ifndef SV_TEMP_WRITE_FILE_H +#define SV_TEMP_WRITE_FILE_H #include @@ -23,7 +23,6 @@ * use when saving a file over an existing one, to avoid clobbering * the original before the save is complete. */ - class TempWriteFile { public: diff -r d4a28d1479a8 -r 710e6250a401 base/TextMatcher.cpp --- a/base/TextMatcher.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/TextMatcher.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -25,7 +25,7 @@ void TextMatcher::test(Match &match, QStringList keywords, QString text, - QString textType, int score) + QString textType, int score) { /* if (text.toLower() == keyword.toLower()) { diff -r d4a28d1479a8 -r 710e6250a401 base/ViewManagerBase.h --- a/base/ViewManagerBase.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ViewManagerBase.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,14 +13,15 @@ COPYING included with this distribution for more information. */ -#ifndef _VIEW_MANAGER_BASE_H_ -#define _VIEW_MANAGER_BASE_H_ +#ifndef SV_VIEW_MANAGER_BASE_H +#define SV_VIEW_MANAGER_BASE_H #include #include "Selection.h" class AudioPlaySource; +class AudioRecordTarget; /** * Base class for ViewManager, with no GUI content. This should @@ -36,6 +37,7 @@ virtual ~ViewManagerBase(); virtual void setAudioPlaySource(AudioPlaySource *source) = 0; + virtual void setAudioRecordTarget(AudioRecordTarget *target) = 0; virtual sv_frame_t alignPlaybackFrameToReference(sv_frame_t) const = 0; virtual sv_frame_t alignReferenceToPlaybackFrame(sv_frame_t) const = 0; diff -r d4a28d1479a8 -r 710e6250a401 base/Window.h --- a/base/Window.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/Window.h Mon Sep 17 13:51:14 2018 +0100 @@ -57,11 +57,11 @@ encache(); } Window &operator=(const Window &w) { - if (&w == this) return *this; - m_type = w.m_type; - m_size = w.m_size; - encache(); - return *this; + if (&w == this) return *this; + m_type = w.m_type; + m_size = w.m_size; + encache(); + return *this; } virtual ~Window() { breakfastquay::deallocate(m_cache); @@ -107,38 +107,38 @@ int i; switch (m_type) { - + case RectangularWindow: - for (i = 0; i < n; ++i) { - m_cache[i] *= T(0.5); - } - break; - + for (i = 0; i < n; ++i) { + m_cache[i] *= T(0.5); + } + break; + case BartlettWindow: - for (i = 0; i < n/2; ++i) { - m_cache[i] *= T(i) / T(n/2); - m_cache[i + n/2] *= T(1.0) - T(i) / T(n/2); - } - break; - + for (i = 0; i < n/2; ++i) { + m_cache[i] *= T(i) / T(n/2); + m_cache[i + n/2] *= T(1.0) - T(i) / T(n/2); + } + break; + case HammingWindow: cosinewin(m_cache, 0.54, 0.46, 0.0, 0.0); - break; - + break; + case HanningWindow: cosinewin(m_cache, 0.50, 0.50, 0.0, 0.0); - break; - + break; + case BlackmanWindow: cosinewin(m_cache, 0.42, 0.50, 0.08, 0.0); - break; - + break; + case GaussianWindow: - for (i = 0; i < n; ++i) { + for (i = 0; i < n; ++i) { m_cache[i] *= T(pow(2, - pow((i - (n-1)/2.0) / ((n-1)/2.0 / 3), 2))); - } - break; - + } + break; + case ParzenWindow: { int N = n-1; @@ -158,13 +158,13 @@ case NuttallWindow: cosinewin(m_cache, 0.3635819, 0.4891775, 0.1365995, 0.0106411); - break; + break; case BlackmanHarrisWindow: cosinewin(m_cache, 0.35875, 0.48829, 0.14128, 0.01168); break; } - + m_area = 0; for (int i = 0; i < n; ++i) { m_area += m_cache[i]; diff -r d4a28d1479a8 -r 710e6250a401 base/XmlExportable.cpp --- a/base/XmlExportable.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/XmlExportable.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -41,11 +41,11 @@ XmlExportable::encodeEntities(QString s) { s - .replace("&", "&") - .replace("<", "<") - .replace(">", ">") - .replace("\"", """) - .replace("'", "'"); + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace("\"", """) + .replace("'", "'"); return s; } @@ -77,7 +77,7 @@ static int maxId = 0; if (idMap.find(object) == idMap.end()) { - idMap[object] = maxId++; + idMap[object] = maxId++; } return idMap[object]; diff -r d4a28d1479a8 -r 710e6250a401 base/XmlExportable.h --- a/base/XmlExportable.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/XmlExportable.h Mon Sep 17 13:51:14 2018 +0100 @@ -40,7 +40,7 @@ * Do not override this unless you really know what you're doing. */ virtual QString toXmlString(QString indent = "", - QString extraAttributes = "") const; + QString extraAttributes = "") const; static QString encodeEntities(QString); diff -r d4a28d1479a8 -r 710e6250a401 base/ZoomConstraint.h --- a/base/ZoomConstraint.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/ZoomConstraint.h Mon Sep 17 13:51:14 2018 +0100 @@ -35,9 +35,9 @@ virtual ~ZoomConstraint() { } enum RoundingDirection { - RoundDown, - RoundUp, - RoundNearest + RoundDown, + RoundUp, + RoundNearest }; /** @@ -52,7 +52,7 @@ */ virtual ZoomLevel getNearestZoomLevel(ZoomLevel requestedZoomLevel, RoundingDirection = RoundNearest) - const + const { if (getMaxZoomLevel() < requestedZoomLevel) return getMaxZoomLevel(); else return requestedZoomLevel; diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestColumnOp.h --- a/base/test/TestColumnOp.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/TestColumnOp.h Mon Sep 17 13:51:14 2018 +0100 @@ -55,7 +55,7 @@ QCOMPARE(C::applyGain({}, 1.0), Column()); Column c { 1, 2, 3, -4, 5, 6 }; Column actual(C::applyGain(c, 1.5)); - Column expected { 1.5, 3, 4.5, -6, 7.5, 9 }; + Column expected { 1.5f, 3, 4.5f, -6, 7.5f, 9 }; QCOMPARE(actual, expected); actual = C::applyGain(c, 1.0); QCOMPARE(actual, c); @@ -68,7 +68,7 @@ QCOMPARE(C::fftScale({}, 2.0), Column()); Column c { 1, 2, 3, -4, 5 }; Column actual(C::fftScale(c, 8)); - Column expected { 0.25, 0.5, 0.75, -1, 1.25 }; + Column expected { 0.25f, 0.5f, 0.75f, -1, 1.25f }; QCOMPARE(actual, expected); } @@ -79,33 +79,33 @@ } void isPeak_obvious() { - Column c { 0.4, 0.5, 0.3 }; + Column c { 0.4f, 0.5f, 0.3f }; QVERIFY(!C::isPeak(c, 0)); QVERIFY(C::isPeak(c, 1)); QVERIFY(!C::isPeak(c, 2)); } void isPeak_edges() { - Column c { 0.5, 0.4, 0.3 }; + Column c { 0.5f, 0.4f, 0.3f }; QVERIFY(C::isPeak(c, 0)); QVERIFY(!C::isPeak(c, 1)); QVERIFY(!C::isPeak(c, 2)); QVERIFY(!C::isPeak(c, 3)); QVERIFY(!C::isPeak(c, -1)); - c = { 1.4, 1.5 }; + c = { 1.4f, 1.5f }; QVERIFY(!C::isPeak(c, 0)); QVERIFY(C::isPeak(c, 1)); } void isPeak_flat() { - Column c { 0.0, 0.0, 0.0 }; + Column c { 0.0f, 0.0f, 0.0f }; QVERIFY(C::isPeak(c, 0)); QVERIFY(!C::isPeak(c, 1)); QVERIFY(!C::isPeak(c, 2)); } void isPeak_mixedSign() { - Column c { 0.4, -0.5, -0.3, -0.6, 0.1, -0.3 }; + Column c { 0.4f, -0.5f, -0.3f, -0.6f, 0.1f, -0.3f }; QVERIFY(C::isPeak(c, 0)); QVERIFY(!C::isPeak(c, 1)); QVERIFY(C::isPeak(c, 2)); @@ -115,12 +115,12 @@ } void isPeak_duplicate() { - Column c({ 0.5, 0.5, 0.4, 0.4 }); + Column c({ 0.5f, 0.5f, 0.4f, 0.4f }); QVERIFY(C::isPeak(c, 0)); QVERIFY(!C::isPeak(c, 1)); QVERIFY(!C::isPeak(c, 2)); QVERIFY(!C::isPeak(c, 3)); - c = { 0.4, 0.4, 0.5, 0.5 }; + c = { 0.4f, 0.4f, 0.5f, 0.5f }; QVERIFY(C::isPeak(c, 0)); // counterintuitive but necessary QVERIFY(!C::isPeak(c, 1)); QVERIFY(C::isPeak(c, 2)); @@ -129,16 +129,17 @@ void peakPick() { QCOMPARE(C::peakPick({}), Column()); - Column c({ 0.5, 0.5, 0.4, 0.4 }); - QCOMPARE(C::peakPick(c), Column({ 0.5, 0.0, 0.0, 0.0 })); - c = Column({ 0.4, -0.5, -0.3, -0.6, 0.1, -0.3 }); - QCOMPARE(C::peakPick(c), Column({ 0.4, 0.0, -0.3, 0.0, 0.1, 0.0 })); + Column c({ 0.5f, 0.5f, 0.4f, 0.4f }); + QCOMPARE(C::peakPick(c), Column({ 0.5f, 0.0f, 0.0f, 0.0f })); + c = Column({ 0.4f, -0.5f, -0.3f, -0.6f, 0.1f, -0.3f }); + QCOMPARE(C::peakPick(c), Column({ 0.4f, 0.0f, -0.3f, 0.0f, 0.1f, 0.0f })); } void normalize_null() { QCOMPARE(C::normalize({}, ColumnNormalization::None), Column()); QCOMPARE(C::normalize({}, ColumnNormalization::Sum1), Column()); QCOMPARE(C::normalize({}, ColumnNormalization::Max1), Column()); + QCOMPARE(C::normalize({}, ColumnNormalization::Range01), Column()); QCOMPARE(C::normalize({}, ColumnNormalization::Hybrid), Column()); } @@ -155,44 +156,56 @@ void normalize_sum1() { Column c { 1, 2, 4, 3 }; QCOMPARE(C::normalize(c, ColumnNormalization::Sum1), - Column({ 0.1, 0.2, 0.4, 0.3 })); + Column({ 0.1f, 0.2f, 0.4f, 0.3f })); } void normalize_sum1_mixedSign() { Column c { 1, 2, -4, -3 }; QCOMPARE(C::normalize(c, ColumnNormalization::Sum1), - Column({ 0.1, 0.2, -0.4, -0.3 })); + Column({ 0.1f, 0.2f, -0.4f, -0.3f })); } void normalize_max1() { Column c { 4, 3, 2, 1 }; QCOMPARE(C::normalize(c, ColumnNormalization::Max1), - Column({ 1.0, 0.75, 0.5, 0.25 })); + Column({ 1.0f, 0.75f, 0.5f, 0.25f })); } void normalize_max1_mixedSign() { Column c { -4, -3, 2, 1 }; QCOMPARE(C::normalize(c, ColumnNormalization::Max1), - Column({ -1.0, -0.75, 0.5, 0.25 })); + Column({ -1.0f, -0.75f, 0.5f, 0.25f })); + } + + void normalize_range01() { + Column c { 4, 3, 2, 1 }; + QCOMPARE(C::normalize(c, ColumnNormalization::Range01), + Column({ 1.0f, 2.f/3.f, 1.f/3.f, 0.0f })); + } + + void normalize_range01_mixedSign() { + Column c { -2, -3, 2, 1 }; + QCOMPARE(C::normalize(c, ColumnNormalization::Range01), + Column({ 0.2f, 0.0f, 1.0f, 0.8f })); } void normalize_hybrid() { // with max == 99, log10(max+1) == 2 so scale factor will be 2/99 Column c { 22, 44, 99, 66 }; QCOMPARE(C::normalize(c, ColumnNormalization::Hybrid), - Column({ 44.0/99.0, 88.0/99.0, 2.0, 132.0/99.0 })); + Column({ 44.0f/99.0f, 88.0f/99.0f, 2.0f, 132.0f/99.0f })); } void normalize_hybrid_mixedSign() { // with max == 99, log10(max+1) == 2 so scale factor will be 2/99 Column c { 22, 44, -99, -66 }; QCOMPARE(C::normalize(c, ColumnNormalization::Hybrid), - Column({ 44.0/99.0, 88.0/99.0, -2.0, -132.0/99.0 })); + Column({ 44.0f/99.0f, 88.0f/99.0f, -2.0f, -132.0f/99.0f })); } void distribute_simple() { Column in { 1, 2, 3 }; - BinMapping binfory { 0.0, 0.5, 1.0, 1.5, 2.0, 2.5 }; + BinMapping binfory { 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f }; Column expected { 1, 1, 2, 2, 3, 3 }; Column actual(C::distribute(in, 6, binfory, 0, false)); report(actual); @@ -201,7 +214,7 @@ void distribute_simple_interpolated() { Column in { 1, 2, 3 }; - BinMapping binfory { 0.0, 0.5, 1.0, 1.5, 2.0, 2.5 }; + BinMapping binfory { 0.0f, 0.5f, 1.0f, 1.5f, 2.0f, 2.5f }; // There is a 0.5-bin offset from the distribution you might // expect, because this corresponds visually to the way that // bin values are duplicated upwards in simple_distribution. @@ -209,7 +222,7 @@ // non-interpolated views retains the visual position of each // bin peak as somewhere in the middle of the scale area for // that bin. - Column expected { 1, 1, 1.5, 2, 2.5, 3 }; + Column expected { 1, 1, 1.5f, 2, 2.5f, 3 }; Column actual(C::distribute(in, 6, binfory, 0, true)); report(actual); QCOMPARE(actual, expected); @@ -217,7 +230,7 @@ void distribute_nonlinear() { Column in { 1, 2, 3 }; - BinMapping binfory { 0.0, 0.2, 0.5, 1.0, 2.0, 2.5 }; + BinMapping binfory { 0.0f, 0.2f, 0.5f, 1.0f, 2.0f, 2.5f }; Column expected { 1, 1, 1, 2, 3, 3 }; Column actual(C::distribute(in, 6, binfory, 0, false)); report(actual); @@ -227,7 +240,7 @@ void distribute_nonlinear_interpolated() { // See distribute_simple_interpolated Column in { 1, 2, 3 }; - BinMapping binfory { 0.0, 0.2, 0.5, 1.0, 2.0, 2.5 }; + BinMapping binfory { 0.0f, 0.2f, 0.5f, 1.0f, 2.0f, 2.5f }; Column expected { 1, 1, 1, 1.5, 2.5, 3 }; Column actual(C::distribute(in, 6, binfory, 0, true)); report(actual); @@ -236,7 +249,7 @@ void distribute_shrinking() { Column in { 4, 1, 2, 3, 5, 6 }; - BinMapping binfory { 0.0, 2.0, 4.0 }; + BinMapping binfory { 0.0f, 2.0f, 4.0f }; Column expected { 4, 3, 6 }; Column actual(C::distribute(in, 3, binfory, 0, false)); report(actual); @@ -247,7 +260,7 @@ // should be same as distribute_shrinking, we don't // interpolate when resizing down Column in { 4, 1, 2, 3, 5, 6 }; - BinMapping binfory { 0.0, 2.0, 4.0 }; + BinMapping binfory { 0.0f, 2.0f, 4.0f }; Column expected { 4, 3, 6 }; Column actual(C::distribute(in, 3, binfory, 0, true)); report(actual); @@ -259,13 +272,13 @@ // shrinking some bins but expanding others. See // distribute_simple_interpolated for note on 0.5 offset Column in { 4, 1, 2, 3, 5, 6 }; - BinMapping binfory { 0.0, 3.0, 4.0, 4.5 }; - Column expected { 4.0, 2.5, 4.0, 5.0 }; + BinMapping binfory { 0.0f, 3.0f, 4.0f, 4.5f }; + Column expected { 4.0f, 2.5f, 4.0f, 5.0f }; Column actual(C::distribute(in, 4, binfory, 0, true)); report(actual); QCOMPARE(actual, expected); - binfory = BinMapping { 0.5, 1.0, 2.0, 5.0 }; - expected = { 4.0, 2.5, 1.5, 5.5 }; + binfory = BinMapping { 0.5f, 1.0f, 2.0f, 5.0f }; + expected = { 4.0f, 2.5f, 1.5f, 5.5f }; actual = (C::distribute(in, 4, binfory, 0, true)); report(actual); QCOMPARE(actual, expected); diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestLogRange.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/test/TestLogRange.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,333 @@ +/* -*- 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 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 TEST_LOG_RANGE_H +#define TEST_LOG_RANGE_H + +#include "../LogRange.h" + +#include +#include + +#include +#include + +using namespace std; + +class TestLogRange : public QObject +{ + Q_OBJECT + +private slots: + + void mapPositiveAboveDefaultThreshold() + { + QCOMPARE(LogRange::map(10.0), 1.0); + QCOMPARE(LogRange::map(100.0), 2.0); + QCOMPARE(LogRange::map(0.1), -1.0); + QCOMPARE(LogRange::map(1.0), 0.0); + QCOMPARE(LogRange::map(0.0000001), -7.0); + QCOMPARE(LogRange::map(20.0), log10(20.0)); + } + + void mapPositiveAboveSetThreshold() + { + QCOMPARE(LogRange::map(10.0, -10.0), 1.0); + QCOMPARE(LogRange::map(100.0, 1.0), 2.0); + QCOMPARE(LogRange::map(0.1, -5.0), -1.0); + QCOMPARE(LogRange::map(1.0, -0.01), 0.0); + QCOMPARE(LogRange::map(0.0000001, -20.0), -7.0); + QCOMPARE(LogRange::map(20.0, 0.0), log10(20.0)); + } + + void mapZeroDefaultThreshold() + { + QCOMPARE(LogRange::map(0.0), -10.0); + } + + void mapZeroSetThreshold() + { + QCOMPARE(LogRange::map(0.0, 12.0), 12.0); + QCOMPARE(LogRange::map(0.0, -12.0), -12.0); + QCOMPARE(LogRange::map(0.0, 0.0), 0.0); + } + + void mapPositiveBelowDefaultThreshold() + { + // The threshold is used only for zero values, not for very + // small ones -- it's arguably a stand-in or replacement value + // rather than a threshold. So this should behave the same as + // for values above the threshold. + QCOMPARE(LogRange::map(1e-10), -10.0); + QCOMPARE(LogRange::map(1e-20), -20.0); + QCOMPARE(LogRange::map(1e-100), -100.0); + } + + void mapPositiveBelowSetThreshold() + { + // As above + QCOMPARE(LogRange::map(10.0, 4.0), 1.0); + QCOMPARE(LogRange::map(1e-10, 4.0), -10.0); + QCOMPARE(LogRange::map(1e-20, -15.0), -20.0); + QCOMPARE(LogRange::map(1e-100, -100.0), -100.0); + } + + void mapNegative() + { + // Should always return map of absolute value. These are + // picked from vaarious of the above tests. + + QCOMPARE(LogRange::map(-10.0), 1.0); + QCOMPARE(LogRange::map(-100.0), 2.0); + QCOMPARE(LogRange::map(-0.1), -1.0); + QCOMPARE(LogRange::map(-1.0), 0.0); + QCOMPARE(LogRange::map(-0.0000001), -7.0); + QCOMPARE(LogRange::map(-20.0), log10(20.0)); + QCOMPARE(LogRange::map(-10.0, 4.0), 1.0); + QCOMPARE(LogRange::map(-1e-10, 4.0), -10.0); + QCOMPARE(LogRange::map(-1e-20, -15.0), -20.0); + QCOMPARE(LogRange::map(-1e-100, -100.0), -100.0); + QCOMPARE(LogRange::map(-0.0, 12.0), 12.0); + QCOMPARE(LogRange::map(-0.0, -12.0), -12.0); + QCOMPARE(LogRange::map(-0.0, 0.0), 0.0); + } + + void unmap() + { + // Simply pow(10, x) + + QCOMPARE(LogRange::unmap(0.0), 1.0); + QCOMPARE(LogRange::unmap(1.0), 10.0); + QCOMPARE(LogRange::unmap(-1.0), 0.1); + QCOMPARE(LogRange::unmap(100.0), 1e+100); + QCOMPARE(LogRange::unmap(-100.0), 1e-100); + } + + void mapRangeAllPositiveDefaultThreshold() + { + double min, max; + + min = 1.0; max = 10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + min = 10.0; max = 1.0; + LogRange::mapRange(min, max); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + // if equal, the function uses an arbitrary 1.0 range before mapping + min = 10.0; max = 10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, 1.0); QCOMPARE(max, log10(11.0)); + } + + void mapRangeAllPositiveSetThreshold() + { + double min, max; + + min = 1.0; max = 10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + min = 10.0; max = 1.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + // if equal, the function uses an arbitrary 1.0 range before mapping + min = 10.0; max = 10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, 1.0); QCOMPARE(max, log10(11.0)); + } + + void mapRangeAllNegativeDefaultThreshold() + { + double min, max; + + min = -1.0; max = -10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + min = -10.0; max = -1.0; + LogRange::mapRange(min, max); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + // if equal, the function uses an arbitrary 1.0 range before mapping + min = -10.0; max = -10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, log10(9.0)); QCOMPARE(max, 1.0); + } + + void mapRangeAllNegativeSetThreshold() + { + double min, max; + + min = -1.0; max = -10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + min = -10.0; max = -1.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, 0.0); QCOMPARE(max, 1.0); + + // if equal, the function uses an arbitrary 1.0 range before mapping + min = -10.0; max = -10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, log10(9.0)); QCOMPARE(max, 1.0); + } + + void mapRangeAllNonNegativeDefaultThreshold() + { + double min, max; + + min = 0.0; max = 10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + min = 10.0; max = 0.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + // if equal, the function uses an arbitrary 1.0 range before mapping + min = 0.0; max = 0.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 0.0); + } + + void mapRangeAllNonNegativeSetThreshold() + { + double min, max; + + min = 0.0; max = 10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + min = 10.0; max = 0.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + // if equal, the function uses an arbitrary 1.0 range before mapping + min = 0.0; max = 0.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 0.0); + } + + void mapRangeAllNonPositiveDefaultThreshold() + { + double min, max; + + min = 0.0; max = -10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + min = -10.0; max = 0.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + } + + void mapRangeAllNonPositiveSetThreshold() + { + double min, max; + + min = 0.0; max = -10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + min = -10.0; max = 0.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + } + + void mapRangeSpanningZeroDefaultThreshold() + { + double min, max; + + min = -1.0; max = 10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + min = -100.0; max = 1.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 2.0); + + min = -10.0; max = 1e-200; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + min = 1e-200; max = -10.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + min = -1e-200; max = 100.0; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 2.0); + + min = 10.0; max = -1e-200; + LogRange::mapRange(min, max); + QCOMPARE(min, -10.0); QCOMPARE(max, 1.0); + + // if none of the input range is above the threshold in + // magnitude, but it still spans zero, we use the input max as + // threshold and then add 1 for range + min = -1e-200; max = 1e-300; + LogRange::mapRange(min, max); + QCOMPARE(min, -201.0); QCOMPARE(max, -200.0); + + min = 1e-200; max = -1e-300; + LogRange::mapRange(min, max); + QCOMPARE(min, -201.0); QCOMPARE(max, -200.0); + } + + void mapRangeSpanningZeroSetThreshold() + { + double min, max; + + min = -1.0; max = 10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + min = -100.0; max = 1.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 2.0); + + min = -10.0; max = 1e-200; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + min = 1e-200; max = -10.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + min = -1e-200; max = 100.0; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 2.0); + + min = 10.0; max = -1e-200; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -4.0); QCOMPARE(max, 1.0); + + // if none of the input range is above the threshold in + // magnitude, but it still spans zero, we use the input max as + // threshold and then add 1 for range + min = -1e-200; max = 1e-300; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -201.0); QCOMPARE(max, -200.0); + + min = 1e-200; max = -1e-300; + LogRange::mapRange(min, max, -4.0); + QCOMPARE(min, -201.0); QCOMPARE(max, -200.0); + } + +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestOurRealTime.h --- a/base/test/TestOurRealTime.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/TestOurRealTime.h Mon Sep 17 13:51:14 2018 +0100 @@ -42,242 +42,247 @@ void zero() { - QCOMPARE(RealTime(0, 0), RealTime::zeroTime); - QCOMPARE(RealTime(0, 0).sec, 0); - QCOMPARE(RealTime(0, 0).nsec, 0); - QCOMPARE(RealTime(0, 0).msec(), 0); - QCOMPARE(RealTime(0, 0).usec(), 0); + QCOMPARE(RealTime(0, 0), RealTime::zeroTime); + QCOMPARE(RealTime(0, 0).sec, 0); + QCOMPARE(RealTime(0, 0).nsec, 0); + QCOMPARE(RealTime(0, 0).msec(), 0); + QCOMPARE(RealTime(0, 0).usec(), 0); } void ctor() { - QCOMPARE(RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(RealTime(0, 0), RealTime(0, 0)); - // wraparounds - QCOMPARE(RealTime(0, ONE_BILLION/2), RealTime(1, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2), RealTime(-1, ONE_BILLION/2)); + // wraparounds + QCOMPARE(RealTime(0, ONE_BILLION/2), RealTime(1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2), RealTime(-1, ONE_BILLION/2)); - QCOMPARE(RealTime(1, ONE_BILLION), RealTime(2, 0)); - QCOMPARE(RealTime(1, -ONE_BILLION), RealTime(0, 0)); - QCOMPARE(RealTime(-1, ONE_BILLION), RealTime(0, 0)); - QCOMPARE(RealTime(-1, -ONE_BILLION), RealTime(-2, 0)); + QCOMPARE(RealTime(1, ONE_BILLION), RealTime(2, 0)); + QCOMPARE(RealTime(1, -ONE_BILLION), RealTime(0, 0)); + QCOMPARE(RealTime(-1, ONE_BILLION), RealTime(0, 0)); + QCOMPARE(RealTime(-1, -ONE_BILLION), RealTime(-2, 0)); QCOMPARE(RealTime(1, -ONE_BILLION-ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); QCOMPARE(RealTime(-1, ONE_BILLION+ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(2, -ONE_BILLION*2), RealTime(0, 0)); - QCOMPARE(RealTime(2, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(2, -ONE_BILLION*2), RealTime(0, 0)); + QCOMPARE(RealTime(2, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(-2, ONE_BILLION*2), RealTime(0, 0)); - QCOMPARE(RealTime(-2, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - - QCOMPARE(RealTime(0, 1).sec, 0); - QCOMPARE(RealTime(0, 1).nsec, 1); - QCOMPARE(RealTime(0, -1).sec, 0); - QCOMPARE(RealTime(0, -1).nsec, -1); - QCOMPARE(RealTime(1, -1).sec, 0); - QCOMPARE(RealTime(1, -1).nsec, ONE_BILLION-1); - QCOMPARE(RealTime(-1, 1).sec, 0); - QCOMPARE(RealTime(-1, 1).nsec, -ONE_BILLION+1); - QCOMPARE(RealTime(-1, -1).sec, -1); - QCOMPARE(RealTime(-1, -1).nsec, -1); - - QCOMPARE(RealTime(2, -ONE_BILLION*2).sec, 0); - QCOMPARE(RealTime(2, -ONE_BILLION*2).nsec, 0); - QCOMPARE(RealTime(2, -ONE_BILLION/2).sec, 1); - QCOMPARE(RealTime(2, -ONE_BILLION/2).nsec, ONE_BILLION/2); + QCOMPARE(RealTime(-2, ONE_BILLION*2), RealTime(0, 0)); + QCOMPARE(RealTime(-2, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - QCOMPARE(RealTime(-2, ONE_BILLION*2).sec, 0); - QCOMPARE(RealTime(-2, ONE_BILLION*2).nsec, 0); - QCOMPARE(RealTime(-2, ONE_BILLION/2).sec, -1); - QCOMPARE(RealTime(-2, ONE_BILLION/2).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime(1, -ONE_BILLION/2).sec, 0); + QCOMPARE(RealTime(1, -ONE_BILLION/2).nsec, ONE_BILLION/2); + QCOMPARE(RealTime(-1, ONE_BILLION/2).sec, 0); + QCOMPARE(RealTime(-1, ONE_BILLION/2).nsec, -ONE_BILLION/2); + + QCOMPARE(RealTime(0, 1).sec, 0); + QCOMPARE(RealTime(0, 1).nsec, 1); + QCOMPARE(RealTime(0, -1).sec, 0); + QCOMPARE(RealTime(0, -1).nsec, -1); + QCOMPARE(RealTime(1, -1).sec, 0); + QCOMPARE(RealTime(1, -1).nsec, ONE_BILLION-1); + QCOMPARE(RealTime(-1, 1).sec, 0); + QCOMPARE(RealTime(-1, 1).nsec, -ONE_BILLION+1); + QCOMPARE(RealTime(-1, -1).sec, -1); + QCOMPARE(RealTime(-1, -1).nsec, -1); + + QCOMPARE(RealTime(2, -ONE_BILLION*2).sec, 0); + QCOMPARE(RealTime(2, -ONE_BILLION*2).nsec, 0); + QCOMPARE(RealTime(2, -ONE_BILLION/2).sec, 1); + QCOMPARE(RealTime(2, -ONE_BILLION/2).nsec, ONE_BILLION/2); + + QCOMPARE(RealTime(-2, ONE_BILLION*2).sec, 0); + QCOMPARE(RealTime(-2, ONE_BILLION*2).nsec, 0); + QCOMPARE(RealTime(-2, ONE_BILLION/2).sec, -1); + QCOMPARE(RealTime(-2, ONE_BILLION/2).nsec, -ONE_BILLION/2); } void fromSeconds() { - QCOMPARE(RealTime::fromSeconds(0), RealTime(0, 0)); + QCOMPARE(RealTime::fromSeconds(0), RealTime(0, 0)); - QCOMPARE(RealTime::fromSeconds(0.5).sec, 0); - QCOMPARE(RealTime::fromSeconds(0.5).nsec, ONE_BILLION/2); - QCOMPARE(RealTime::fromSeconds(0.5).usec(), ONE_MILLION/2); - QCOMPARE(RealTime::fromSeconds(0.5).msec(), 500); - - QCOMPARE(RealTime::fromSeconds(0.5), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime::fromSeconds(1), RealTime(1, 0)); - QCOMPARE(RealTime::fromSeconds(1.5), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(0.5).sec, 0); + QCOMPARE(RealTime::fromSeconds(0.5).nsec, ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(0.5).usec(), ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(0.5).msec(), 500); + + QCOMPARE(RealTime::fromSeconds(0.5), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(1), RealTime(1, 0)); + QCOMPARE(RealTime::fromSeconds(1.5), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime::fromSeconds(-0.5).sec, 0); - QCOMPARE(RealTime::fromSeconds(-0.5).nsec, -ONE_BILLION/2); - QCOMPARE(RealTime::fromSeconds(-0.5).usec(), -ONE_MILLION/2); - QCOMPARE(RealTime::fromSeconds(-0.5).msec(), -500); - - QCOMPARE(RealTime::fromSeconds(-1.5).sec, -1); - QCOMPARE(RealTime::fromSeconds(-1.5).nsec, -ONE_BILLION/2); - QCOMPARE(RealTime::fromSeconds(-1.5).usec(), -ONE_MILLION/2); - QCOMPARE(RealTime::fromSeconds(-1.5).msec(), -500); - - QCOMPARE(RealTime::fromSeconds(-0.5), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime::fromSeconds(-1), RealTime(-1, 0)); - QCOMPARE(RealTime::fromSeconds(-1.5), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(-0.5).sec, 0); + QCOMPARE(RealTime::fromSeconds(-0.5).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(-0.5).usec(), -ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(-0.5).msec(), -500); + + QCOMPARE(RealTime::fromSeconds(-1.5).sec, -1); + QCOMPARE(RealTime::fromSeconds(-1.5).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(-1.5).usec(), -ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(-1.5).msec(), -500); + + QCOMPARE(RealTime::fromSeconds(-0.5), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(-1), RealTime(-1, 0)); + QCOMPARE(RealTime::fromSeconds(-1.5), RealTime(-1, -ONE_BILLION/2)); } void fromMilliseconds() { - QCOMPARE(RealTime::fromMilliseconds(0), RealTime(0, 0)); - QCOMPARE(RealTime::fromMilliseconds(500), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime::fromMilliseconds(1000), RealTime(1, 0)); - QCOMPARE(RealTime::fromMilliseconds(1500), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(0), RealTime(0, 0)); + QCOMPARE(RealTime::fromMilliseconds(500), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(1000), RealTime(1, 0)); + QCOMPARE(RealTime::fromMilliseconds(1500), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime::fromMilliseconds(-0), RealTime(0, 0)); - QCOMPARE(RealTime::fromMilliseconds(-500), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime::fromMilliseconds(-1000), RealTime(-1, 0)); - QCOMPARE(RealTime::fromMilliseconds(-1500), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(-0), RealTime(0, 0)); + QCOMPARE(RealTime::fromMilliseconds(-500), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(-1000), RealTime(-1, 0)); + QCOMPARE(RealTime::fromMilliseconds(-1500), RealTime(-1, -ONE_BILLION/2)); } void fromTimeval() { - struct timeval tv; + struct timeval tv; - tv.tv_sec = 0; tv.tv_usec = 0; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, 0)); - tv.tv_sec = 0; tv.tv_usec = ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, ONE_BILLION/2)); - tv.tv_sec = 1; tv.tv_usec = 0; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, 0)); - tv.tv_sec = 1; tv.tv_usec = ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, ONE_BILLION/2)); + tv.tv_sec = 0; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, 0)); + tv.tv_sec = 0; tv.tv_usec = ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, ONE_BILLION/2)); + tv.tv_sec = 1; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, 0)); + tv.tv_sec = 1; tv.tv_usec = ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, ONE_BILLION/2)); - tv.tv_sec = 0; tv.tv_usec = -ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, -ONE_BILLION/2)); - tv.tv_sec = -1; tv.tv_usec = 0; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, 0)); - tv.tv_sec = -1; tv.tv_usec = -ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, -ONE_BILLION/2)); + tv.tv_sec = 0; tv.tv_usec = -ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, -ONE_BILLION/2)); + tv.tv_sec = -1; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, 0)); + tv.tv_sec = -1; tv.tv_usec = -ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, -ONE_BILLION/2)); } void fromXsdDuration() { - QCOMPARE(RealTime::fromXsdDuration("PT0"), RealTime::zeroTime); - QCOMPARE(RealTime::fromXsdDuration("PT0S"), RealTime::zeroTime); - QCOMPARE(RealTime::fromXsdDuration("PT10S"), RealTime(10, 0)); - QCOMPARE(RealTime::fromXsdDuration("PT10.5S"), RealTime(10, ONE_BILLION/2)); - QCOMPARE(RealTime::fromXsdDuration("PT1.5S").sec, 1); - QCOMPARE(RealTime::fromXsdDuration("PT1.5S").msec(), 500); - QCOMPARE(RealTime::fromXsdDuration("-PT1.5S").sec, -1); - QCOMPARE(RealTime::fromXsdDuration("-PT1.5S").msec(), -500); - QCOMPARE(RealTime::fromXsdDuration("PT1M30.5S"), RealTime(90, ONE_BILLION/2)); - QCOMPARE(RealTime::fromXsdDuration("PT1H2M30.5S"), RealTime(3750, ONE_BILLION/2)); + QCOMPARE(RealTime::fromXsdDuration("PT0"), RealTime::zeroTime); + QCOMPARE(RealTime::fromXsdDuration("PT0S"), RealTime::zeroTime); + QCOMPARE(RealTime::fromXsdDuration("PT10S"), RealTime(10, 0)); + QCOMPARE(RealTime::fromXsdDuration("PT10.5S"), RealTime(10, ONE_BILLION/2)); + QCOMPARE(RealTime::fromXsdDuration("PT1.5S").sec, 1); + QCOMPARE(RealTime::fromXsdDuration("PT1.5S").msec(), 500); + QCOMPARE(RealTime::fromXsdDuration("-PT1.5S").sec, -1); + QCOMPARE(RealTime::fromXsdDuration("-PT1.5S").msec(), -500); + QCOMPARE(RealTime::fromXsdDuration("PT1M30.5S"), RealTime(90, ONE_BILLION/2)); + QCOMPARE(RealTime::fromXsdDuration("PT1H2M30.5S"), RealTime(3750, ONE_BILLION/2)); } void toDouble() { - QCOMPARE(RealTime(0, 0).toDouble(), 0.0); - QCOMPARE(RealTime(0, ONE_BILLION/2).toDouble(), 0.5); - QCOMPARE(RealTime(1, 0).toDouble(), 1.0); - QCOMPARE(RealTime(1, ONE_BILLION/2).toDouble(), 1.5); + QCOMPARE(RealTime(0, 0).toDouble(), 0.0); + QCOMPARE(RealTime(0, ONE_BILLION/2).toDouble(), 0.5); + QCOMPARE(RealTime(1, 0).toDouble(), 1.0); + QCOMPARE(RealTime(1, ONE_BILLION/2).toDouble(), 1.5); - QCOMPARE(RealTime(0, -ONE_BILLION/2).toDouble(), -0.5); - QCOMPARE(RealTime(-1, 0).toDouble(), -1.0); - QCOMPARE(RealTime(-1, -ONE_BILLION/2).toDouble(), -1.5); + QCOMPARE(RealTime(0, -ONE_BILLION/2).toDouble(), -0.5); + QCOMPARE(RealTime(-1, 0).toDouble(), -1.0); + QCOMPARE(RealTime(-1, -ONE_BILLION/2).toDouble(), -1.5); } void assign() { - RealTime r; - r = RealTime(0, 0); - QCOMPARE(r, RealTime::zeroTime); - r = RealTime(0, ONE_BILLION/2); + RealTime r; + r = RealTime(0, 0); + QCOMPARE(r, RealTime::zeroTime); + r = RealTime(0, ONE_BILLION/2); QCOMPARE(r.sec, 0); QCOMPARE(r.nsec, ONE_BILLION/2); - r = RealTime(-1, -ONE_BILLION/2); + r = RealTime(-1, -ONE_BILLION/2); QCOMPARE(r.sec, -1); QCOMPARE(r.nsec, -ONE_BILLION/2); } void plus() { - QCOMPARE(RealTime(0, 0) + RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(RealTime(0, 0) + RealTime(0, 0), RealTime(0, 0)); - QCOMPARE(RealTime(0, 0) + RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(0, ONE_BILLION/2), RealTime(1, 0)); - QCOMPARE(RealTime(1, 0) + RealTime(0, ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) + RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(0, ONE_BILLION/2), RealTime(1, 0)); + QCOMPARE(RealTime(1, 0) + RealTime(0, ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(0, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(-1, 0)); - QCOMPARE(RealTime(-1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(-1, 0)); + QCOMPARE(RealTime(-1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); - QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(-1, 0), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(1, 0), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(-1, 0), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(1, 0), RealTime(0, ONE_BILLION/2)); } void minus() { - QCOMPARE(RealTime(0, 0) - RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(RealTime(0, 0) - RealTime(0, 0), RealTime(0, 0)); - QCOMPARE(RealTime(0, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(0, ONE_BILLION/2), RealTime(0, 0)); - QCOMPARE(RealTime(1, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(0, ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(1, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(0, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); - QCOMPARE(RealTime(-1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(-1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, 0)); - QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, 0)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(-1, 0), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(1, 0), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(-1, 0), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(1, 0), RealTime(-1, -ONE_BILLION/2)); } void negate() { - QCOMPARE(-RealTime(0, 0), RealTime(0, 0)); - QCOMPARE(-RealTime(1, 0), RealTime(-1, 0)); - QCOMPARE(-RealTime(1, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - QCOMPARE(-RealTime(-1, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(-RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(-RealTime(1, 0), RealTime(-1, 0)); + QCOMPARE(-RealTime(1, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(-RealTime(-1, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); } void compare() { - int sec, nsec; - for (sec = -2; sec <= 2; sec += 2) { - for (nsec = -1; nsec <= 1; nsec += 1) { - QCOMPARE(RealTime(sec, nsec) < RealTime(sec, nsec), false); - QCOMPARE(RealTime(sec, nsec) > RealTime(sec, nsec), false); - QCOMPARE(RealTime(sec, nsec) == RealTime(sec, nsec), true); - QCOMPARE(RealTime(sec, nsec) != RealTime(sec, nsec), false); - QCOMPARE(RealTime(sec, nsec) <= RealTime(sec, nsec), true); - QCOMPARE(RealTime(sec, nsec) >= RealTime(sec, nsec), true); - } - } - RealTime prev(-3, 0); - for (sec = -2; sec <= 2; sec += 2) { - for (nsec = -1; nsec <= 1; nsec += 1) { + int sec, nsec; + for (sec = -2; sec <= 2; sec += 2) { + for (nsec = -1; nsec <= 1; nsec += 1) { + QCOMPARE(RealTime(sec, nsec) < RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) > RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) == RealTime(sec, nsec), true); + QCOMPARE(RealTime(sec, nsec) != RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) <= RealTime(sec, nsec), true); + QCOMPARE(RealTime(sec, nsec) >= RealTime(sec, nsec), true); + } + } + RealTime prev(-3, 0); + for (sec = -2; sec <= 2; sec += 2) { + for (nsec = -1; nsec <= 1; nsec += 1) { - RealTime curr(sec, nsec); + RealTime curr(sec, nsec); - QCOMPARE(prev < curr, true); - QCOMPARE(prev > curr, false); - QCOMPARE(prev == curr, false); - QCOMPARE(prev != curr, true); - QCOMPARE(prev <= curr, true); - QCOMPARE(prev >= curr, false); + QCOMPARE(prev < curr, true); + QCOMPARE(prev > curr, false); + QCOMPARE(prev == curr, false); + QCOMPARE(prev != curr, true); + QCOMPARE(prev <= curr, true); + QCOMPARE(prev >= curr, false); - QCOMPARE(curr < prev, false); - QCOMPARE(curr > prev, true); - QCOMPARE(curr == prev, false); - QCOMPARE(curr != prev, true); - QCOMPARE(curr <= prev, false); - QCOMPARE(curr >= prev, true); + QCOMPARE(curr < prev, false); + QCOMPARE(curr > prev, true); + QCOMPARE(curr == prev, false); + QCOMPARE(curr != prev, true); + QCOMPARE(curr <= prev, false); + QCOMPARE(curr >= prev, true); - prev = curr; - } - } + prev = curr; + } + } } void frame() diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestPitch.h --- a/base/test/TestPitch.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/TestPitch.h Mon Sep 17 13:51:14 2018 +0100 @@ -32,76 +32,76 @@ private slots: void init() { - Preferences::getInstance()->setOctaveOfMiddleC(4); - Preferences::getInstance()->setTuningFrequency(440); + Preferences::getInstance()->setOctaveOfMiddleC(4); + Preferences::getInstance()->setTuningFrequency(440); } void pitchLabel() { - QCOMPARE(Pitch::getPitchLabel(60, 0, false), QString("C4")); - QCOMPARE(Pitch::getPitchLabel(69, 0, false), QString("A4")); - QCOMPARE(Pitch::getPitchLabel(61, 0, false), QString("C#4")); - QCOMPARE(Pitch::getPitchLabel(61, 0, true), QString("Db4")); - QCOMPARE(Pitch::getPitchLabel(59, 0, false), QString("B3")); - QCOMPARE(Pitch::getPitchLabel(59, 0, true), QString("B3")); - QCOMPARE(Pitch::getPitchLabel(0, 0, false), QString("C-1")); + QCOMPARE(Pitch::getPitchLabel(60, 0, false), QString("C4")); + QCOMPARE(Pitch::getPitchLabel(69, 0, false), QString("A4")); + QCOMPARE(Pitch::getPitchLabel(61, 0, false), QString("C#4")); + QCOMPARE(Pitch::getPitchLabel(61, 0, true), QString("Db4")); + QCOMPARE(Pitch::getPitchLabel(59, 0, false), QString("B3")); + QCOMPARE(Pitch::getPitchLabel(59, 0, true), QString("B3")); + QCOMPARE(Pitch::getPitchLabel(0, 0, false), QString("C-1")); - QCOMPARE(Pitch::getPitchLabel(60, -40, false), QString("C4-40c")); - QCOMPARE(Pitch::getPitchLabel(60, 40, false), QString("C4+40c")); - QCOMPARE(Pitch::getPitchLabel(58, 4, false), QString("A#3+4c")); + QCOMPARE(Pitch::getPitchLabel(60, -40, false), QString("C4-40c")); + QCOMPARE(Pitch::getPitchLabel(60, 40, false), QString("C4+40c")); + QCOMPARE(Pitch::getPitchLabel(58, 4, false), QString("A#3+4c")); - Preferences::getInstance()->setOctaveOfMiddleC(3); + Preferences::getInstance()->setOctaveOfMiddleC(3); - QCOMPARE(Pitch::getPitchLabel(60, 0, false), QString("C3")); - QCOMPARE(Pitch::getPitchLabel(69, 0, false), QString("A3")); - QCOMPARE(Pitch::getPitchLabel(61, 0, false), QString("C#3")); - QCOMPARE(Pitch::getPitchLabel(61, 0, true), QString("Db3")); - QCOMPARE(Pitch::getPitchLabel(59, 0, false), QString("B2")); - QCOMPARE(Pitch::getPitchLabel(59, 0, true), QString("B2")); - QCOMPARE(Pitch::getPitchLabel(0, 0, false), QString("C-2")); + QCOMPARE(Pitch::getPitchLabel(60, 0, false), QString("C3")); + QCOMPARE(Pitch::getPitchLabel(69, 0, false), QString("A3")); + QCOMPARE(Pitch::getPitchLabel(61, 0, false), QString("C#3")); + QCOMPARE(Pitch::getPitchLabel(61, 0, true), QString("Db3")); + QCOMPARE(Pitch::getPitchLabel(59, 0, false), QString("B2")); + QCOMPARE(Pitch::getPitchLabel(59, 0, true), QString("B2")); + QCOMPARE(Pitch::getPitchLabel(0, 0, false), QString("C-2")); - QCOMPARE(Pitch::getPitchLabel(60, -40, false), QString("C3-40c")); - QCOMPARE(Pitch::getPitchLabel(60, 40, false), QString("C3+40c")); - QCOMPARE(Pitch::getPitchLabel(58, 4, false), QString("A#2+4c")); + QCOMPARE(Pitch::getPitchLabel(60, -40, false), QString("C3-40c")); + QCOMPARE(Pitch::getPitchLabel(60, 40, false), QString("C3+40c")); + QCOMPARE(Pitch::getPitchLabel(58, 4, false), QString("A#2+4c")); } void pitchLabelForFrequency() { - QCOMPARE(Pitch::getPitchLabelForFrequency(440, 440, false), QString("A4")); - QCOMPARE(Pitch::getPitchLabelForFrequency(440, 220, false), QString("A5")); - QCOMPARE(Pitch::getPitchLabelForFrequency(261.63, 440, false), QString("C4")); + QCOMPARE(Pitch::getPitchLabelForFrequency(440, 440, false), QString("A4")); + QCOMPARE(Pitch::getPitchLabelForFrequency(440, 220, false), QString("A5")); + QCOMPARE(Pitch::getPitchLabelForFrequency(261.63, 440, false), QString("C4")); } #define MIDDLE_C 261.6255653005986 void frequencyForPitch() { - QCOMPARE(Pitch::getFrequencyForPitch(60, 0), MIDDLE_C); - QCOMPARE(Pitch::getFrequencyForPitch(69, 0), 440.0); - QCOMPARE(Pitch::getFrequencyForPitch(60, 0, 220), MIDDLE_C / 2.0); - QCOMPARE(Pitch::getFrequencyForPitch(69, 0, 220), 220.0); + QCOMPARE(Pitch::getFrequencyForPitch(60, 0), MIDDLE_C); + QCOMPARE(Pitch::getFrequencyForPitch(69, 0), 440.0); + QCOMPARE(Pitch::getFrequencyForPitch(60, 0, 220), MIDDLE_C / 2.0); + QCOMPARE(Pitch::getFrequencyForPitch(69, 0, 220), 220.0); } void pitchForFrequency() { - double centsOffset = 0.0; - QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, ¢sOffset), 60); - QCOMPARE(centsOffset, 0.0); - QCOMPARE(Pitch::getPitchForFrequency(261.0, ¢sOffset), 60); - QCOMPARE(int(centsOffset), -4); - QCOMPARE(Pitch::getPitchForFrequency(440.0, ¢sOffset), 69); - QCOMPARE(centsOffset, 0.0); + double centsOffset = 0.0; + QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, ¢sOffset), 60); + QCOMPARE(centsOffset + 1.0, 1.0); // avoid ineffective fuzzy-compare to 0 + QCOMPARE(Pitch::getPitchForFrequency(261.0, ¢sOffset), 60); + QCOMPARE(int(centsOffset), -4); + QCOMPARE(Pitch::getPitchForFrequency(440.0, ¢sOffset), 69); + QCOMPARE(centsOffset + 1.0, 1.0); } void pitchForFrequencyF() { - float centsOffset = 0.f; - QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, ¢sOffset), 60); - QCOMPARE(centsOffset, 0.f); - QCOMPARE(Pitch::getPitchForFrequency(261.0, ¢sOffset), 60); - QCOMPARE(int(centsOffset), -4); - QCOMPARE(Pitch::getPitchForFrequency(440.0, ¢sOffset), 69); - QCOMPARE(centsOffset, 0.f); + float centsOffset = 0.f; + QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, ¢sOffset), 60); + QCOMPARE(centsOffset + 1.f, 1.f); // avoid ineffective fuzzy-compare to 0 + QCOMPARE(Pitch::getPitchForFrequency(261.0, ¢sOffset), 60); + QCOMPARE(int(centsOffset), -4); + QCOMPARE(Pitch::getPitchForFrequency(440.0, ¢sOffset), 69); + QCOMPARE(centsOffset + 1.f, 1.f); } }; diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestRangeMapper.h --- a/base/test/TestRangeMapper.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/TestRangeMapper.h Mon Sep 17 13:51:14 2018 +0100 @@ -32,250 +32,250 @@ private slots: void linearUpForward() { - LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", false); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getPositionForValue(0.5), 1); - QCOMPARE(rm.getPositionForValue(4.0), 8); - QCOMPARE(rm.getPositionForValue(3.0), 6); - QCOMPARE(rm.getPositionForValue(3.1), 6); - QCOMPARE(rm.getPositionForValue(3.4), 7); - QCOMPARE(rm.getPositionForValue(0.2), 1); - QCOMPARE(rm.getPositionForValue(-12), 1); - QCOMPARE(rm.getPositionForValue(6.1), 8); - QCOMPARE(rm.getPositionForValueUnclamped(3.0), 6); - QCOMPARE(rm.getPositionForValueUnclamped(0.2), 0); - QCOMPARE(rm.getPositionForValueUnclamped(-12), -24); - QCOMPARE(rm.getPositionForValueUnclamped(6.1), 12); + LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", false); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getPositionForValue(0.5), 1); + QCOMPARE(rm.getPositionForValue(4.0), 8); + QCOMPARE(rm.getPositionForValue(3.0), 6); + QCOMPARE(rm.getPositionForValue(3.1), 6); + QCOMPARE(rm.getPositionForValue(3.4), 7); + QCOMPARE(rm.getPositionForValue(0.2), 1); + QCOMPARE(rm.getPositionForValue(-12), 1); + QCOMPARE(rm.getPositionForValue(6.1), 8); + QCOMPARE(rm.getPositionForValueUnclamped(3.0), 6); + QCOMPARE(rm.getPositionForValueUnclamped(0.2), 0); + QCOMPARE(rm.getPositionForValueUnclamped(-12), -24); + QCOMPARE(rm.getPositionForValueUnclamped(6.1), 12); } void linearDownForward() { - LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", true); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getPositionForValue(0.5), 8); - QCOMPARE(rm.getPositionForValue(4.0), 1); - QCOMPARE(rm.getPositionForValue(3.0), 3); - QCOMPARE(rm.getPositionForValue(3.1), 3); - QCOMPARE(rm.getPositionForValue(3.4), 2); - QCOMPARE(rm.getPositionForValue(0.2), 8); - QCOMPARE(rm.getPositionForValue(-12), 8); - QCOMPARE(rm.getPositionForValue(6.1), 1); - QCOMPARE(rm.getPositionForValueUnclamped(3.0), 3); - QCOMPARE(rm.getPositionForValueUnclamped(0.2), 9); - QCOMPARE(rm.getPositionForValueUnclamped(-12), 33); - QCOMPARE(rm.getPositionForValueUnclamped(6.1), -3); + LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", true); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getPositionForValue(0.5), 8); + QCOMPARE(rm.getPositionForValue(4.0), 1); + QCOMPARE(rm.getPositionForValue(3.0), 3); + QCOMPARE(rm.getPositionForValue(3.1), 3); + QCOMPARE(rm.getPositionForValue(3.4), 2); + QCOMPARE(rm.getPositionForValue(0.2), 8); + QCOMPARE(rm.getPositionForValue(-12), 8); + QCOMPARE(rm.getPositionForValue(6.1), 1); + QCOMPARE(rm.getPositionForValueUnclamped(3.0), 3); + QCOMPARE(rm.getPositionForValueUnclamped(0.2), 9); + QCOMPARE(rm.getPositionForValueUnclamped(-12), 33); + QCOMPARE(rm.getPositionForValueUnclamped(6.1), -3); } void linearUpBackward() { - LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", false); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getValueForPosition(1), 0.5); - QCOMPARE(rm.getValueForPosition(8), 4.0); - QCOMPARE(rm.getValueForPosition(6), 3.0); - QCOMPARE(rm.getValueForPosition(7), 3.5); - QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(1)); - QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(8)); - QCOMPARE(rm.getValueForPositionUnclamped(6), 3.0); - QCOMPARE(rm.getValueForPositionUnclamped(0), 0.0); - QCOMPARE(rm.getValueForPositionUnclamped(-24), -12.0); - QCOMPARE(rm.getValueForPositionUnclamped(12), 6.0); + LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", false); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getValueForPosition(1), 0.5); + QCOMPARE(rm.getValueForPosition(8), 4.0); + QCOMPARE(rm.getValueForPosition(6), 3.0); + QCOMPARE(rm.getValueForPosition(7), 3.5); + QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(1)); + QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(8)); + QCOMPARE(rm.getValueForPositionUnclamped(6), 3.0); + QCOMPARE(rm.getValueForPositionUnclamped(0) + 1.0, 0.0 + 1.0); + QCOMPARE(rm.getValueForPositionUnclamped(-24), -12.0); + QCOMPARE(rm.getValueForPositionUnclamped(12), 6.0); } void linearDownBackward() { - LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", true); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getValueForPosition(8), 0.5); - QCOMPARE(rm.getValueForPosition(1), 4.0); - QCOMPARE(rm.getValueForPosition(3), 3.0); - QCOMPARE(rm.getValueForPosition(2), 3.5); - QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(1)); - QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(8)); - QCOMPARE(rm.getValueForPositionUnclamped(3), 3.0); - QCOMPARE(rm.getValueForPositionUnclamped(9), 0.0); - QCOMPARE(rm.getValueForPositionUnclamped(33), -12.0); - QCOMPARE(rm.getValueForPositionUnclamped(-3), 6.0); + LinearRangeMapper rm(1, 8, 0.5, 4.0, "x", true); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getValueForPosition(8), 0.5); + QCOMPARE(rm.getValueForPosition(1), 4.0); + QCOMPARE(rm.getValueForPosition(3), 3.0); + QCOMPARE(rm.getValueForPosition(2), 3.5); + QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(1)); + QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(8)); + QCOMPARE(rm.getValueForPositionUnclamped(3), 3.0); + QCOMPARE(rm.getValueForPositionUnclamped(9) + 1.0, 0.0 + 1.0); + QCOMPARE(rm.getValueForPositionUnclamped(33), -12.0); + QCOMPARE(rm.getValueForPositionUnclamped(-3), 6.0); } void logUpForward() { - LogRangeMapper rm(3, 7, 10, 100000, "x", false); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getPositionForValue(10.0), 3); - QCOMPARE(rm.getPositionForValue(100000.0), 7); - QCOMPARE(rm.getPositionForValue(1.0), 3); - QCOMPARE(rm.getPositionForValue(1000000.0), 7); - QCOMPARE(rm.getPositionForValue(1000.0), 5); - QCOMPARE(rm.getPositionForValue(900.0), 5); - QCOMPARE(rm.getPositionForValue(20000), 6); - QCOMPARE(rm.getPositionForValueUnclamped(1.0), 2); - QCOMPARE(rm.getPositionForValueUnclamped(1000000.0), 8); - QCOMPARE(rm.getPositionForValueUnclamped(1000.0), 5); + LogRangeMapper rm(3, 7, 10, 100000, "x", false); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getPositionForValue(10.0), 3); + QCOMPARE(rm.getPositionForValue(100000.0), 7); + QCOMPARE(rm.getPositionForValue(1.0), 3); + QCOMPARE(rm.getPositionForValue(1000000.0), 7); + QCOMPARE(rm.getPositionForValue(1000.0), 5); + QCOMPARE(rm.getPositionForValue(900.0), 5); + QCOMPARE(rm.getPositionForValue(20000), 6); + QCOMPARE(rm.getPositionForValueUnclamped(1.0), 2); + QCOMPARE(rm.getPositionForValueUnclamped(1000000.0), 8); + QCOMPARE(rm.getPositionForValueUnclamped(1000.0), 5); } void logDownForward() { - LogRangeMapper rm(3, 7, 10, 100000, "x", true); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getPositionForValue(10.0), 7); - QCOMPARE(rm.getPositionForValue(100000.0), 3); - QCOMPARE(rm.getPositionForValue(1.0), 7); - QCOMPARE(rm.getPositionForValue(1000000.0), 3); - QCOMPARE(rm.getPositionForValue(1000.0), 5); - QCOMPARE(rm.getPositionForValue(900.0), 5); - QCOMPARE(rm.getPositionForValue(20000), 4); - QCOMPARE(rm.getPositionForValueUnclamped(1.0), 8); - QCOMPARE(rm.getPositionForValueUnclamped(1000000.0), 2); - QCOMPARE(rm.getPositionForValueUnclamped(1000.0), 5); + LogRangeMapper rm(3, 7, 10, 100000, "x", true); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getPositionForValue(10.0), 7); + QCOMPARE(rm.getPositionForValue(100000.0), 3); + QCOMPARE(rm.getPositionForValue(1.0), 7); + QCOMPARE(rm.getPositionForValue(1000000.0), 3); + QCOMPARE(rm.getPositionForValue(1000.0), 5); + QCOMPARE(rm.getPositionForValue(900.0), 5); + QCOMPARE(rm.getPositionForValue(20000), 4); + QCOMPARE(rm.getPositionForValueUnclamped(1.0), 8); + QCOMPARE(rm.getPositionForValueUnclamped(1000000.0), 2); + QCOMPARE(rm.getPositionForValueUnclamped(1000.0), 5); } void logUpBackward() { - LogRangeMapper rm(3, 7, 10, 100000, "x", false); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getValueForPosition(3), 10.0); - QCOMPARE(rm.getValueForPosition(7), 100000.0); - QCOMPARE(rm.getValueForPosition(5), 1000.0); - QCOMPARE(rm.getValueForPosition(6), 10000.0); - QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(3)); - QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(7)); - QCOMPARE(rm.getValueForPositionUnclamped(2), 1.0); + LogRangeMapper rm(3, 7, 10, 100000, "x", false); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getValueForPosition(3), 10.0); + QCOMPARE(rm.getValueForPosition(7), 100000.0); + QCOMPARE(rm.getValueForPosition(5), 1000.0); + QCOMPARE(rm.getValueForPosition(6), 10000.0); + QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(3)); + QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(7)); + QCOMPARE(rm.getValueForPositionUnclamped(2), 1.0); QCOMPARE(rm.getValueForPositionUnclamped(8), 1000000.0); QCOMPARE(rm.getValueForPositionUnclamped(5), 1000.0); } void logDownBackward() { - LogRangeMapper rm(3, 7, 10, 100000, "x", true); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getValueForPosition(7), 10.0); - QCOMPARE(rm.getValueForPosition(3), 100000.0); - QCOMPARE(rm.getValueForPosition(5), 1000.0); - QCOMPARE(rm.getValueForPosition(4), 10000.0); - QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(3)); - QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(7)); - QCOMPARE(rm.getValueForPositionUnclamped(8), 1.0); + LogRangeMapper rm(3, 7, 10, 100000, "x", true); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getValueForPosition(7), 10.0); + QCOMPARE(rm.getValueForPosition(3), 100000.0); + QCOMPARE(rm.getValueForPosition(5), 1000.0); + QCOMPARE(rm.getValueForPosition(4), 10000.0); + QCOMPARE(rm.getValueForPosition(0), rm.getValueForPosition(3)); + QCOMPARE(rm.getValueForPosition(9), rm.getValueForPosition(7)); + QCOMPARE(rm.getValueForPositionUnclamped(8), 1.0); QCOMPARE(rm.getValueForPositionUnclamped(2), 1000000.0); QCOMPARE(rm.getValueForPositionUnclamped(5), 1000.0); } void interpolatingForward() { - InterpolatingRangeMapper::CoordMap mappings; - mappings[1] = 10; - mappings[3] = 30; - mappings[5] = 70; - InterpolatingRangeMapper rm(mappings, "x"); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getPositionForValue(1.0), 10); - QCOMPARE(rm.getPositionForValue(0.0), 10); - QCOMPARE(rm.getPositionForValue(5.0), 70); - QCOMPARE(rm.getPositionForValue(6.0), 70); - QCOMPARE(rm.getPositionForValue(3.0), 30); - QCOMPARE(rm.getPositionForValue(2.5), 25); - QCOMPARE(rm.getPositionForValue(4.5), 60); - QCOMPARE(rm.getPositionForValueUnclamped(0.0), 0); - QCOMPARE(rm.getPositionForValueUnclamped(2.5), 25); - QCOMPARE(rm.getPositionForValueUnclamped(6.0), 90); + InterpolatingRangeMapper::CoordMap mappings; + mappings[1] = 10; + mappings[3] = 30; + mappings[5] = 70; + InterpolatingRangeMapper rm(mappings, "x"); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getPositionForValue(1.0), 10); + QCOMPARE(rm.getPositionForValue(0.0), 10); + QCOMPARE(rm.getPositionForValue(5.0), 70); + QCOMPARE(rm.getPositionForValue(6.0), 70); + QCOMPARE(rm.getPositionForValue(3.0), 30); + QCOMPARE(rm.getPositionForValue(2.5), 25); + QCOMPARE(rm.getPositionForValue(4.5), 60); + QCOMPARE(rm.getPositionForValueUnclamped(0.0), 0); + QCOMPARE(rm.getPositionForValueUnclamped(2.5), 25); + QCOMPARE(rm.getPositionForValueUnclamped(6.0), 90); } void interpolatingBackward() { - InterpolatingRangeMapper::CoordMap mappings; - mappings[1] = 10; - mappings[3] = 30; - mappings[5] = 70; - InterpolatingRangeMapper rm(mappings, "x"); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getValueForPosition(10), 1.0); - QCOMPARE(rm.getValueForPosition(9), 1.0); - QCOMPARE(rm.getValueForPosition(70), 5.0); - QCOMPARE(rm.getValueForPosition(80), 5.0); - QCOMPARE(rm.getValueForPosition(30), 3.0); - QCOMPARE(rm.getValueForPosition(25), 2.5); - QCOMPARE(rm.getValueForPosition(60), 4.5); + InterpolatingRangeMapper::CoordMap mappings; + mappings[1] = 10; + mappings[3] = 30; + mappings[5] = 70; + InterpolatingRangeMapper rm(mappings, "x"); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getValueForPosition(10), 1.0); + QCOMPARE(rm.getValueForPosition(9), 1.0); + QCOMPARE(rm.getValueForPosition(70), 5.0); + QCOMPARE(rm.getValueForPosition(80), 5.0); + QCOMPARE(rm.getValueForPosition(30), 3.0); + QCOMPARE(rm.getValueForPosition(25), 2.5); + QCOMPARE(rm.getValueForPosition(60), 4.5); } void autoLinearForward() { - AutoRangeMapper::CoordMap mappings; - mappings[0.5] = 1; - mappings[4.0] = 8; - AutoRangeMapper rm1(mappings, "x"); - QCOMPARE(rm1.getUnit(), QString("x")); - QCOMPARE(rm1.getType(), AutoRangeMapper::StraightLine); - QCOMPARE(rm1.getPositionForValue(0.1), 1); - QCOMPARE(rm1.getPositionForValue(0.5), 1); - QCOMPARE(rm1.getPositionForValue(4.0), 8); - QCOMPARE(rm1.getPositionForValue(4.5), 8); - QCOMPARE(rm1.getPositionForValue(3.0), 6); - QCOMPARE(rm1.getPositionForValue(3.1), 6); - QCOMPARE(rm1.getPositionForValueUnclamped(0.1), 0); - QCOMPARE(rm1.getPositionForValueUnclamped(3.1), 6); - QCOMPARE(rm1.getPositionForValueUnclamped(4.5), 9); - mappings[3.0] = 6; - mappings[3.5] = 7; - AutoRangeMapper rm2(mappings, "x"); - QCOMPARE(rm2.getUnit(), QString("x")); - QCOMPARE(rm2.getType(), AutoRangeMapper::StraightLine); - QCOMPARE(rm2.getPositionForValue(0.5), 1); - QCOMPARE(rm2.getPositionForValue(4.0), 8); - QCOMPARE(rm2.getPositionForValue(3.0), 6); - QCOMPARE(rm2.getPositionForValue(3.1), 6); + AutoRangeMapper::CoordMap mappings; + mappings[0.5] = 1; + mappings[4.0] = 8; + AutoRangeMapper rm1(mappings, "x"); + QCOMPARE(rm1.getUnit(), QString("x")); + QCOMPARE(rm1.getType(), AutoRangeMapper::StraightLine); + QCOMPARE(rm1.getPositionForValue(0.1), 1); + QCOMPARE(rm1.getPositionForValue(0.5), 1); + QCOMPARE(rm1.getPositionForValue(4.0), 8); + QCOMPARE(rm1.getPositionForValue(4.5), 8); + QCOMPARE(rm1.getPositionForValue(3.0), 6); + QCOMPARE(rm1.getPositionForValue(3.1), 6); + QCOMPARE(rm1.getPositionForValueUnclamped(0.1), 0); + QCOMPARE(rm1.getPositionForValueUnclamped(3.1), 6); + QCOMPARE(rm1.getPositionForValueUnclamped(4.5), 9); + mappings[3.0] = 6; + mappings[3.5] = 7; + AutoRangeMapper rm2(mappings, "x"); + QCOMPARE(rm2.getUnit(), QString("x")); + QCOMPARE(rm2.getType(), AutoRangeMapper::StraightLine); + QCOMPARE(rm2.getPositionForValue(0.5), 1); + QCOMPARE(rm2.getPositionForValue(4.0), 8); + QCOMPARE(rm2.getPositionForValue(3.0), 6); + QCOMPARE(rm2.getPositionForValue(3.1), 6); } void autoLogForward() { - AutoRangeMapper::CoordMap mappings; - mappings[10] = 3; - mappings[1000] = 5; - mappings[100000] = 7; - AutoRangeMapper rm1(mappings, "x"); - QCOMPARE(rm1.getUnit(), QString("x")); - QCOMPARE(rm1.getType(), AutoRangeMapper::Logarithmic); - QCOMPARE(rm1.getPositionForValue(10.0), 3); - QCOMPARE(rm1.getPositionForValue(100000.0), 7); - QCOMPARE(rm1.getPositionForValue(1.0), 3); - QCOMPARE(rm1.getPositionForValue(1000000.0), 7); - QCOMPARE(rm1.getPositionForValue(1000.0), 5); - QCOMPARE(rm1.getPositionForValue(900.0), 5); - QCOMPARE(rm1.getPositionForValue(20000), 6); - QCOMPARE(rm1.getPositionForValueUnclamped(1.0), 2); - QCOMPARE(rm1.getPositionForValueUnclamped(900.0), 5); - QCOMPARE(rm1.getPositionForValueUnclamped(1000000.0), 8); - mappings[100] = 4; - AutoRangeMapper rm2(mappings, "x"); - QCOMPARE(rm2.getUnit(), QString("x")); - QCOMPARE(rm2.getType(), AutoRangeMapper::Logarithmic); - QCOMPARE(rm2.getPositionForValue(10.0), 3); - QCOMPARE(rm2.getPositionForValue(100000.0), 7); - QCOMPARE(rm2.getPositionForValue(1.0), 3); - QCOMPARE(rm2.getPositionForValue(1000000.0), 7); - QCOMPARE(rm2.getPositionForValue(1000.0), 5); - QCOMPARE(rm2.getPositionForValue(900.0), 5); - QCOMPARE(rm2.getPositionForValue(20000), 6); + AutoRangeMapper::CoordMap mappings; + mappings[10] = 3; + mappings[1000] = 5; + mappings[100000] = 7; + AutoRangeMapper rm1(mappings, "x"); + QCOMPARE(rm1.getUnit(), QString("x")); + QCOMPARE(rm1.getType(), AutoRangeMapper::Logarithmic); + QCOMPARE(rm1.getPositionForValue(10.0), 3); + QCOMPARE(rm1.getPositionForValue(100000.0), 7); + QCOMPARE(rm1.getPositionForValue(1.0), 3); + QCOMPARE(rm1.getPositionForValue(1000000.0), 7); + QCOMPARE(rm1.getPositionForValue(1000.0), 5); + QCOMPARE(rm1.getPositionForValue(900.0), 5); + QCOMPARE(rm1.getPositionForValue(20000), 6); + QCOMPARE(rm1.getPositionForValueUnclamped(1.0), 2); + QCOMPARE(rm1.getPositionForValueUnclamped(900.0), 5); + QCOMPARE(rm1.getPositionForValueUnclamped(1000000.0), 8); + mappings[100] = 4; + AutoRangeMapper rm2(mappings, "x"); + QCOMPARE(rm2.getUnit(), QString("x")); + QCOMPARE(rm2.getType(), AutoRangeMapper::Logarithmic); + QCOMPARE(rm2.getPositionForValue(10.0), 3); + QCOMPARE(rm2.getPositionForValue(100000.0), 7); + QCOMPARE(rm2.getPositionForValue(1.0), 3); + QCOMPARE(rm2.getPositionForValue(1000000.0), 7); + QCOMPARE(rm2.getPositionForValue(1000.0), 5); + QCOMPARE(rm2.getPositionForValue(900.0), 5); + QCOMPARE(rm2.getPositionForValue(20000), 6); } void autoInterpolatingForward() { - AutoRangeMapper::CoordMap mappings; - mappings[1] = 10; - mappings[3] = 30; - mappings[5] = 70; - AutoRangeMapper rm(mappings, "x"); - QCOMPARE(rm.getUnit(), QString("x")); - QCOMPARE(rm.getType(), AutoRangeMapper::Interpolating); - QCOMPARE(rm.getPositionForValue(1.0), 10); - QCOMPARE(rm.getPositionForValue(0.0), 10); - QCOMPARE(rm.getPositionForValue(5.0), 70); - QCOMPARE(rm.getPositionForValue(6.0), 70); - QCOMPARE(rm.getPositionForValue(3.0), 30); - QCOMPARE(rm.getPositionForValue(2.5), 25); - QCOMPARE(rm.getPositionForValue(4.5), 60); - QCOMPARE(rm.getPositionForValueUnclamped(0.0), 0); - QCOMPARE(rm.getPositionForValueUnclamped(5.0), 70); - QCOMPARE(rm.getPositionForValueUnclamped(6.0), 90); + AutoRangeMapper::CoordMap mappings; + mappings[1] = 10; + mappings[3] = 30; + mappings[5] = 70; + AutoRangeMapper rm(mappings, "x"); + QCOMPARE(rm.getUnit(), QString("x")); + QCOMPARE(rm.getType(), AutoRangeMapper::Interpolating); + QCOMPARE(rm.getPositionForValue(1.0), 10); + QCOMPARE(rm.getPositionForValue(0.0), 10); + QCOMPARE(rm.getPositionForValue(5.0), 70); + QCOMPARE(rm.getPositionForValue(6.0), 70); + QCOMPARE(rm.getPositionForValue(3.0), 30); + QCOMPARE(rm.getPositionForValue(2.5), 25); + QCOMPARE(rm.getPositionForValue(4.5), 60); + QCOMPARE(rm.getPositionForValueUnclamped(0.0), 0); + QCOMPARE(rm.getPositionForValueUnclamped(5.0), 70); + QCOMPARE(rm.getPositionForValueUnclamped(6.0), 90); } }; diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestScaleTickIntervals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/test/TestScaleTickIntervals.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,622 @@ +/* -*- 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 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 TEST_SCALE_TICK_INTERVALS_H +#define TEST_SCALE_TICK_INTERVALS_H + +#include "../ScaleTickIntervals.h" + +#include +#include +#include + +#include + +using namespace std; + +class TestScaleTickIntervals : public QObject +{ + Q_OBJECT + + void printDiff(vector ticks, + vector expected) { + + SVCERR << "Have " << ticks.size() << " ticks, expected " + << expected.size() << endl; + for (int i = 0; i < int(ticks.size()); ++i) { + SVCERR << i << ": have " << ticks[i].value << " \"" + << ticks[i].label << "\", expected "; + if (i < int(expected.size())) { + SVCERR << expected[i].value << " \"" << expected[i].label + << "\"" << endl; + } else { + SVCERR << "(n/a)" << endl; + } + } + } + + void compareTicks(ScaleTickIntervals::Ticks ticks, + ScaleTickIntervals::Ticks expected, + bool fuzzier = false) + { + for (int i = 0; i < int(expected.size()); ++i) { + if (i < int(ticks.size())) { + bool pass = true; + if (ticks[i].label != expected[i].label) { + pass = false; + } else { + double eps = fuzzier ? 1e-5 : 1e-10; + double diff = fabs(ticks[i].value - expected[i].value); + double limit = max(eps, fabs(ticks[i].value) * eps); + if (diff > limit) { + pass = false; + } + } + if (!pass) { + printDiff(ticks, expected); + } + QCOMPARE(ticks[i].label, expected[i].label); + QCOMPARE(ticks[i].value, expected[i].value); + } + } + if (ticks.size() != expected.size()) { + printDiff(ticks, expected); + } + QCOMPARE(ticks.size(), expected.size()); + } + +private slots: + void linear_0_1_10() + { + auto ticks = ScaleTickIntervals::linear({ 0, 1, 10 }); + ScaleTickIntervals::Ticks expected { + { 0.0, "0.0" }, + { 0.1, "0.1" }, + { 0.2, "0.2" }, + { 0.3, "0.3" }, + { 0.4, "0.4" }, + { 0.5, "0.5" }, + { 0.6, "0.6" }, + { 0.7, "0.7" }, + { 0.8, "0.8" }, + { 0.9, "0.9" }, + { 1.0, "1.0" } + }; + compareTicks(ticks, expected); + } + + void linear_0_5_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, 5, 5 }); + ScaleTickIntervals::Ticks expected { + { 0, "0" }, + { 1, "1" }, + { 2, "2" }, + { 3, "3" }, + { 4, "4" }, + { 5, "5" }, + }; + compareTicks(ticks, expected); + } + + void linear_0_10_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, 10, 5 }); + ScaleTickIntervals::Ticks expected { + { 0, "0" }, + { 2, "2" }, + { 4, "4" }, + { 6, "6" }, + { 8, "8" }, + { 10, "10" } + }; + compareTicks(ticks, expected); + } + + void linear_10_0_5() + { + auto ticks = ScaleTickIntervals::linear({ 10, 0, 5 }); + ScaleTickIntervals::Ticks expected { + { 0, "0" }, + { 2, "2" }, + { 4, "4" }, + { 6, "6" }, + { 8, "8" }, + { 10, "10" } + }; + compareTicks(ticks, expected); + } + + void linear_m10_0_5() + { + auto ticks = ScaleTickIntervals::linear({ -10, 0, 5 }); + ScaleTickIntervals::Ticks expected { + { -10, "-10" }, + { -8, "-8" }, + { -6, "-6" }, + { -4, "-4" }, + { -2, "-2" }, + { 0, "0" } + }; + compareTicks(ticks, expected); + } + + void linear_0_m10_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, -10, 5 }); + ScaleTickIntervals::Ticks expected { + { -10, "-10" }, + { -8, "-8" }, + { -6, "-6" }, + { -4, "-4" }, + { -2, "-2" }, + { 0, "0" } + }; + compareTicks(ticks, expected); + } + + void linear_0_0p1_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, 0.1, 5 }); + ScaleTickIntervals::Ticks expected { + { 0.00, "0.00" }, + { 0.02, "0.02" }, + { 0.04, "0.04" }, + { 0.06, "0.06" }, + { 0.08, "0.08" }, + { 0.10, "0.10" } + }; + compareTicks(ticks, expected); + } + + void linear_0_0p01_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, 0.01, 5 }); + ScaleTickIntervals::Ticks expected { + { 0.000, "0.000" }, + { 0.002, "0.002" }, + { 0.004, "0.004" }, + { 0.006, "0.006" }, + { 0.008, "0.008" }, + { 0.010, "0.010" } + }; + compareTicks(ticks, expected); + } + + void linear_0_0p005_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, 0.005, 5 }); + ScaleTickIntervals::Ticks expected { + { 0.000, "0.000" }, + { 0.001, "0.001" }, + { 0.002, "0.002" }, + { 0.003, "0.003" }, + { 0.004, "0.004" }, + { 0.005, "0.005" } + }; + compareTicks(ticks, expected); + } + + void linear_0_0p001_5() + { + auto ticks = ScaleTickIntervals::linear({ 0, 0.001, 5 }); + ScaleTickIntervals::Ticks expected { + { 0.0000, "0.0e+00" }, + { 0.0002, "2.0e-04" }, + { 0.0004, "4.0e-04" }, + { 0.0006, "6.0e-04" }, + { 0.0008, "8.0e-04" }, + { 0.0010, "1.0e-03" } + }; + compareTicks(ticks, expected); + } + + void linear_1_1p001_5() + { + auto ticks = ScaleTickIntervals::linear({ 1, 1.001, 5 }); + ScaleTickIntervals::Ticks expected { + { 1.0000, "1.0000" }, + { 1.0002, "1.0002" }, + { 1.0004, "1.0004" }, + { 1.0006, "1.0006" }, + { 1.0008, "1.0008" }, + { 1.0010, "1.0010" } + }; + compareTicks(ticks, expected); + } + + void linear_0p001_1_5() + { + auto ticks = ScaleTickIntervals::linear({ 0.001, 1, 5 }); + ScaleTickIntervals::Ticks expected { + { 0.1, "0.1" }, + { 0.3, "0.3" }, + { 0.5, "0.5" }, + { 0.7, "0.7" }, + { 0.9, "0.9" }, + }; + compareTicks(ticks, expected); + } + + void linear_10000_10010_5() + { + auto ticks = ScaleTickIntervals::linear({ 10000, 10010, 5 }); + ScaleTickIntervals::Ticks expected { + { 10000, "10000" }, + { 10002, "10002" }, + { 10004, "10004" }, + { 10006, "10006" }, + { 10008, "10008" }, + { 10010, "10010" }, + }; + compareTicks(ticks, expected); + } + + void linear_10000_20000_5() + { + auto ticks = ScaleTickIntervals::linear({ 10000, 20000, 5 }); + ScaleTickIntervals::Ticks expected { + { 10000, "10000" }, + { 12000, "12000" }, + { 14000, "14000" }, + { 16000, "16000" }, + { 18000, "18000" }, + { 20000, "20000" }, + }; + compareTicks(ticks, expected); + } + + void linear_m1_1_10() + { + auto ticks = ScaleTickIntervals::linear({ -1, 1, 10 }); + ScaleTickIntervals::Ticks expected { + { -1.0, "-1.0" }, + { -0.8, "-0.8" }, + { -0.6, "-0.6" }, + { -0.4, "-0.4" }, + { -0.2, "-0.2" }, + { 0.0, "0.0" }, + { 0.2, "0.2" }, + { 0.4, "0.4" }, + { 0.6, "0.6" }, + { 0.8, "0.8" }, + { 1.0, "1.0" } + }; + compareTicks(ticks, expected); + } + + void linear_221p23_623p7_57p4() + { + auto ticks = ScaleTickIntervals::linear({ 221.23, 623.7, 4 }); + // only 4 ticks, not 5, because none of the rounded tick + // values lies on an end value + ScaleTickIntervals::Ticks expected { + { 300, "300" }, + { 400, "400" }, + { 500, "500" }, + { 600, "600" }, + }; + compareTicks(ticks, expected); + } + + void linear_sqrt2_pi_7() + { + auto ticks = ScaleTickIntervals::linear({ sqrt(2.0), M_PI, 7 }); + // This would be better in steps of 0.25, but we only round to + // integral powers of ten + ScaleTickIntervals::Ticks expected { + { 1.5, "1.5" }, + { 1.7, "1.7" }, + { 1.9, "1.9" }, + { 2.1, "2.1" }, + { 2.3, "2.3" }, + { 2.5, "2.5" }, + { 2.7, "2.7" }, + { 2.9, "2.9" }, + { 3.1, "3.1" }, + }; + compareTicks(ticks, expected); + } + + void linear_pi_avogadro_7() + { + auto ticks = ScaleTickIntervals::linear({ M_PI, 6.022140857e23, 7 }); + ScaleTickIntervals::Ticks expected { + // not perfect, but ok-ish + { 0, "0.0e+00" }, + { 9e+22, "9.0e+22" }, + { 1.8e+23, "1.8e+23" }, + { 2.7e+23, "2.7e+23" }, + { 3.6e+23, "3.6e+23" }, + { 4.5e+23, "4.5e+23" }, + { 5.4e+23, "5.4e+23" }, + }; + compareTicks(ticks, expected); + } + + void linear_2_3_1() + { + auto ticks = ScaleTickIntervals::linear({ 2, 3, 1 }); + ScaleTickIntervals::Ticks expected { + { 2.0, "2" }, + { 3.0, "3" } + }; + compareTicks(ticks, expected); + } + + void linear_2_3_2() + { + auto ticks = ScaleTickIntervals::linear({ 2, 3, 2 }); + ScaleTickIntervals::Ticks expected { + { 2.0, "2.0" }, + { 2.5, "2.5" }, + { 3.0, "3.0" } + }; + compareTicks(ticks, expected); + } + + void linear_2_3_3() + { + auto ticks = ScaleTickIntervals::linear({ 2, 3, 3 }); + ScaleTickIntervals::Ticks expected { + { 2.0, "2.0" }, + { 2.3, "2.3" }, + { 2.6, "2.6" }, + { 2.9, "2.9" } + }; + compareTicks(ticks, expected); + } + + void linear_2_3_4() + { + auto ticks = ScaleTickIntervals::linear({ 2, 3, 4 }); + // This would be better in steps of 0.25, but we only round to + // integral powers of ten + ScaleTickIntervals::Ticks expected { + { 2.0, "2.0" }, + { 2.3, "2.3" }, + { 2.6, "2.6" }, + { 2.9, "2.9" } + }; + compareTicks(ticks, expected); + } + + void linear_2_3_5() + { + auto ticks = ScaleTickIntervals::linear({ 2, 3, 5 }); + ScaleTickIntervals::Ticks expected { + { 2.0, "2.0" }, + { 2.2, "2.2" }, + { 2.4, "2.4" }, + { 2.6, "2.6" }, + { 2.8, "2.8" }, + { 3.0, "3.0" } + }; + compareTicks(ticks, expected); + } + + void linear_2_3_6() + { + auto ticks = ScaleTickIntervals::linear({ 2, 3, 6 }); + ScaleTickIntervals::Ticks expected { + { 2.0, "2.0" }, + { 2.2, "2.2" }, + { 2.4, "2.4" }, + { 2.6, "2.6" }, + { 2.8, "2.8" }, + { 3.0, "3.0" } + }; + compareTicks(ticks, expected); + } + + void linear_1_1_10() + { + // pathological range + auto ticks = ScaleTickIntervals::linear({ 1, 1, 10 }); + ScaleTickIntervals::Ticks expected { + { 1.0, "1" } + }; + compareTicks(ticks, expected); + } + + void linear_0_0_10() + { + // pathological range + auto ticks = ScaleTickIntervals::linear({ 0, 0, 10 }); + ScaleTickIntervals::Ticks expected { + { 0.0, "0.0" } + }; + compareTicks(ticks, expected); + } + + void linear_0_1_1() + { + auto ticks = ScaleTickIntervals::linear({ 0, 1, 1 }); + ScaleTickIntervals::Ticks expected { + { 0.0, "0" }, + { 1.0, "1" } + }; + compareTicks(ticks, expected); + } + + void linear_0_1_0() + { + // senseless input + auto ticks = ScaleTickIntervals::linear({ 0, 1, 0 }); + ScaleTickIntervals::Ticks expected { + { 0.0, "0.0" }, + }; + compareTicks(ticks, expected); + } + + void linear_0_1_m1() + { + // senseless input + auto ticks = ScaleTickIntervals::linear({ 0, 1, -1 }); + ScaleTickIntervals::Ticks expected { + { 0.0, "0.0" }, + }; + compareTicks(ticks, expected); + } + + void linear_0p465_778_10() + { + // a case that gave unsatisfactory results in real life + // (initially it had the first tick at 1) + auto ticks = ScaleTickIntervals::linear({ 0.465, 778.08, 10 }); + ScaleTickIntervals::Ticks expected { + { 10, "10" }, + { 90, "90" }, + { 170, "170" }, + { 250, "250" }, + { 330, "330" }, + { 410, "410" }, + { 490, "490" }, + { 570, "570" }, + { 650, "650" }, + { 730, "730" }, + }; + compareTicks(ticks, expected); + } + + void log_1_10_2() + { + auto ticks = ScaleTickIntervals::logarithmic({ 1, 10, 2 }); + ScaleTickIntervals::Ticks expected { + { 1.0, "1.0" }, + { 3.2, "3.2" }, + { 10.0, "10" }, + }; + compareTicks(ticks, expected); + } + + void log_0_10_2() + { + auto ticks = ScaleTickIntervals::logarithmic({ 0, 10, 2 }); + ScaleTickIntervals::Ticks expected { + { 1e-6, "1e-06" }, + { 1, "1" }, + }; + compareTicks(ticks, expected); + } + + void log_pi_avogadro_7() + { + auto ticks = ScaleTickIntervals::logarithmic({ M_PI, 6.022140857e23, 7 }); + ScaleTickIntervals::Ticks expected { + { 1000, "1000" }, + { 1e+06, "1e+06" }, + { 1e+09, "1e+09" }, + { 1e+12, "1e+12" }, + { 1e+15, "1e+15" }, + { 1e+18, "1e+18" }, + { 1e+21, "1e+21" }, + }; + compareTicks(ticks, expected, true); + } + + void log_0p465_778_10() + { + auto ticks = ScaleTickIntervals::logarithmic({ 0.465, 778.08, 10 }); + ScaleTickIntervals::Ticks expected { + { 0.5, "0.50" }, + { 1, "1.0" }, + { 2, "2.0" }, + { 4, "4.0" }, + { 8, "8.0" }, + { 16, "16" }, + { 32, "32" }, + { 64, "64" }, + { 130, "130" }, + { 260, "260" }, + { 510, "510" }, + }; + compareTicks(ticks, expected); + } + + void log_1_10k_10() + { + auto ticks = ScaleTickIntervals::logarithmic({ 1.0, 10000.0, 10 }); + ScaleTickIntervals::Ticks expected { + { 1.0, "1.0" }, + { 2.5, "2.5" }, + { 6.3, "6.3" }, + { 16.0, "16" }, + { 40.0, "40" }, + { 100.0, "100" }, + { 250.0, "250" }, + { 630.0, "630" }, + { 1600.0, "1600" }, + { 4000.0, "4000" }, + { 10000.0, "1e+04" }, + }; + compareTicks(ticks, expected, true); + } + + void log_80_10k_6() + { + auto ticks = ScaleTickIntervals::logarithmic({ 80.0, 10000.0, 6 }); + ScaleTickIntervals::Ticks expected { + { 130, "130" }, + { 260, "260" }, + { 510, "510" }, + { 1000, "1000" }, + { 2000, "2000" }, + { 4100, "4100" }, + { 8200, "8200" } + }; + compareTicks(ticks, expected, true); + } + + void log_80_800k_10() + { + auto ticks = ScaleTickIntervals::logarithmic({ 80.0, 800000.0, 10 }); + ScaleTickIntervals::Ticks expected { + { 100, "100" }, + { 250, "250" }, + { 630, "630" }, + { 1600, "1600" }, + { 4000, "4000" }, + { 10000, "1e+04" }, + { 25000, "2.5e+04" }, + { 63000, "6.3e+04" }, + { 160000, "1.6e+05" }, + { 400000, "4e+05" }, + }; + compareTicks(ticks, expected, true); + } + + void log_0_1_0() + { + // senseless input + auto ticks = ScaleTickIntervals::logarithmic({ 0, 1, 0 }); + ScaleTickIntervals::Ticks expected { + }; + compareTicks(ticks, expected); + } + + void log_0_1_m1() + { + // senseless input + auto ticks = ScaleTickIntervals::logarithmic({ 0, 1, -1 }); + ScaleTickIntervals::Ticks expected { + }; + compareTicks(ticks, expected); + } + +}; + +#endif + + diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestStringBits.h --- a/base/test/TestStringBits.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/TestStringBits.h Mon Sep 17 13:51:14 2018 +0100 @@ -128,6 +128,34 @@ testSplitQuoted(in, out); } + void snested3() { + QString in = "'aa bb cc\"' dd"; + QStringList out; + out << "aa bb cc\"" << "dd"; + testSplitQuoted(in, out); + } + + void snested3a() { + QString in = "\"aa bb cc'\" dd"; + QStringList out; + out << "aa bb cc'" << "dd"; + testSplitQuoted(in, out); + } + + void snested4() { + QString in = "'aa \"bb cc\" dd'"; + QStringList out; + out << "aa \"bb cc\" dd"; + testSplitQuoted(in, out); + } + + void snested4a() { + QString in = "\"aa 'bb cc' dd\""; + QStringList out; + out << "aa 'bb cc' dd"; + testSplitQuoted(in, out); + } + void qquoted() { QString in = "a'a 'bb' \\\"cc\" dd\\\""; QStringList out; @@ -135,6 +163,19 @@ testSplitQuoted(in, out); } + void qspace() { + QString in = "\"a a\":\"b:b\":\"c d\""; + QStringList out1; + // Can't start a quote in the middle of a bare field - they + // are handled only if the first character in the field is a + // quote. Otherwise we'd have trouble with apostrophes etc + out1 << "a a:\"b:b\":\"c" << "d\""; + QCOMPARE(StringBits::splitQuoted(in, ' '), out1); + QStringList out2; + out2 << "a a" << "b:b" << "c d"; + QCOMPARE(StringBits::splitQuoted(in, ':'), out2); + } + void multispace() { QString in = " a'a \\' 'bb' ' \\\"cc\" ' dd\\\" '"; QStringList out; diff -r d4a28d1479a8 -r 710e6250a401 base/test/TestVampRealTime.h --- a/base/test/TestVampRealTime.h Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/TestVampRealTime.h Mon Sep 17 13:51:14 2018 +0100 @@ -32,223 +32,227 @@ Q_OBJECT void compareTexts(string s, const char *e) { - QCOMPARE(QString(s.c_str()), QString(e)); + QString actual(s.c_str()); + QString expected(e); + QCOMPARE(actual, expected); } typedef Vamp::RealTime RealTime; typedef long frame_type; - + private slots: void zero() { - QCOMPARE(RealTime(0, 0), RealTime::zeroTime); - QCOMPARE(RealTime(0, 0).sec, 0); - QCOMPARE(RealTime(0, 0).nsec, 0); - QCOMPARE(RealTime(0, 0).msec(), 0); - QCOMPARE(RealTime(0, 0).usec(), 0); + QCOMPARE(RealTime(0, 0), RealTime::zeroTime); + QCOMPARE(RealTime(0, 0).sec, 0); + QCOMPARE(RealTime(0, 0).nsec, 0); + QCOMPARE(RealTime(0, 0).msec(), 0); + QCOMPARE(RealTime(0, 0).usec(), 0); } void ctor() { - QCOMPARE(RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(RealTime(0, 0), RealTime(0, 0)); - // wraparounds - QCOMPARE(RealTime(0, ONE_BILLION/2), RealTime(1, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2), RealTime(-1, ONE_BILLION/2)); + // wraparounds + QCOMPARE(RealTime(0, ONE_BILLION/2), RealTime(1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2), RealTime(-1, ONE_BILLION/2)); - QCOMPARE(RealTime(1, ONE_BILLION), RealTime(2, 0)); - QCOMPARE(RealTime(1, -ONE_BILLION), RealTime(0, 0)); - QCOMPARE(RealTime(-1, ONE_BILLION), RealTime(0, 0)); - QCOMPARE(RealTime(-1, -ONE_BILLION), RealTime(-2, 0)); + QCOMPARE(RealTime(1, ONE_BILLION), RealTime(2, 0)); + QCOMPARE(RealTime(1, -ONE_BILLION), RealTime(0, 0)); + QCOMPARE(RealTime(-1, ONE_BILLION), RealTime(0, 0)); + QCOMPARE(RealTime(-1, -ONE_BILLION), RealTime(-2, 0)); - QCOMPARE(RealTime(2, -ONE_BILLION*2), RealTime(0, 0)); - QCOMPARE(RealTime(2, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(2, -ONE_BILLION*2), RealTime(0, 0)); + QCOMPARE(RealTime(2, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(-2, ONE_BILLION*2), RealTime(0, 0)); - QCOMPARE(RealTime(-2, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - - QCOMPARE(RealTime(0, 1).sec, 0); - QCOMPARE(RealTime(0, 1).nsec, 1); - QCOMPARE(RealTime(0, -1).sec, 0); - QCOMPARE(RealTime(0, -1).nsec, -1); - QCOMPARE(RealTime(1, -1).sec, 0); - QCOMPARE(RealTime(1, -1).nsec, ONE_BILLION-1); - QCOMPARE(RealTime(-1, 1).sec, 0); - QCOMPARE(RealTime(-1, 1).nsec, -ONE_BILLION+1); - QCOMPARE(RealTime(-1, -1).sec, -1); - QCOMPARE(RealTime(-1, -1).nsec, -1); - - QCOMPARE(RealTime(2, -ONE_BILLION*2).sec, 0); - QCOMPARE(RealTime(2, -ONE_BILLION*2).nsec, 0); - QCOMPARE(RealTime(2, -ONE_BILLION/2).sec, 1); - QCOMPARE(RealTime(2, -ONE_BILLION/2).nsec, ONE_BILLION/2); + QCOMPARE(RealTime(-2, ONE_BILLION*2), RealTime(0, 0)); + QCOMPARE(RealTime(-2, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + + QCOMPARE(RealTime(0, 1).sec, 0); + QCOMPARE(RealTime(0, 1).nsec, 1); + QCOMPARE(RealTime(0, -1).sec, 0); + QCOMPARE(RealTime(0, -1).nsec, -1); + QCOMPARE(RealTime(1, -1).sec, 0); + QCOMPARE(RealTime(1, -1).nsec, ONE_BILLION-1); + QCOMPARE(RealTime(-1, 1).sec, 0); + QCOMPARE(RealTime(-1, 1).nsec, -ONE_BILLION+1); + QCOMPARE(RealTime(-1, -1).sec, -1); + QCOMPARE(RealTime(-1, -1).nsec, -1); + + QCOMPARE(RealTime(2, -ONE_BILLION*2).sec, 0); + QCOMPARE(RealTime(2, -ONE_BILLION*2).nsec, 0); + QCOMPARE(RealTime(2, -ONE_BILLION/2).sec, 1); + QCOMPARE(RealTime(2, -ONE_BILLION/2).nsec, ONE_BILLION/2); - QCOMPARE(RealTime(-2, ONE_BILLION*2).sec, 0); - QCOMPARE(RealTime(-2, ONE_BILLION*2).nsec, 0); - QCOMPARE(RealTime(-2, ONE_BILLION/2).sec, -1); - QCOMPARE(RealTime(-2, ONE_BILLION/2).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime(-2, ONE_BILLION*2).sec, 0); + QCOMPARE(RealTime(-2, ONE_BILLION*2).nsec, 0); + QCOMPARE(RealTime(-2, ONE_BILLION/2).sec, -1); + QCOMPARE(RealTime(-2, ONE_BILLION/2).nsec, -ONE_BILLION/2); } void fromSeconds() { - QCOMPARE(RealTime::fromSeconds(0), RealTime(0, 0)); + QCOMPARE(RealTime::fromSeconds(0), RealTime(0, 0)); - QCOMPARE(RealTime::fromSeconds(0.5).sec, 0); - QCOMPARE(RealTime::fromSeconds(0.5).nsec, ONE_BILLION/2); - QCOMPARE(RealTime::fromSeconds(0.5).usec(), ONE_MILLION/2); - QCOMPARE(RealTime::fromSeconds(0.5).msec(), 500); - - QCOMPARE(RealTime::fromSeconds(0.5), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime::fromSeconds(1), RealTime(1, 0)); - QCOMPARE(RealTime::fromSeconds(1.5), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(0.5).sec, 0); + QCOMPARE(RealTime::fromSeconds(0.5).nsec, ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(0.5).usec(), ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(0.5).msec(), 500); + + QCOMPARE(RealTime::fromSeconds(0.5), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(1), RealTime(1, 0)); + QCOMPARE(RealTime::fromSeconds(1.5), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime::fromSeconds(-0.5).sec, 0); - QCOMPARE(RealTime::fromSeconds(-0.5).nsec, -ONE_BILLION/2); - QCOMPARE(RealTime::fromSeconds(-0.5).usec(), -ONE_MILLION/2); - QCOMPARE(RealTime::fromSeconds(-0.5).msec(), -500); - - QCOMPARE(RealTime::fromSeconds(-1.5).sec, -1); - QCOMPARE(RealTime::fromSeconds(-1.5).nsec, -ONE_BILLION/2); - QCOMPARE(RealTime::fromSeconds(-1.5).usec(), -ONE_MILLION/2); - QCOMPARE(RealTime::fromSeconds(-1.5).msec(), -500); - - QCOMPARE(RealTime::fromSeconds(-0.5), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime::fromSeconds(-1), RealTime(-1, 0)); - QCOMPARE(RealTime::fromSeconds(-1.5), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(-0.5).sec, 0); + QCOMPARE(RealTime::fromSeconds(-0.5).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(-0.5).usec(), -ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(-0.5).msec(), -500); + + QCOMPARE(RealTime::fromSeconds(-1.5).sec, -1); + QCOMPARE(RealTime::fromSeconds(-1.5).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(-1.5).usec(), -ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(-1.5).msec(), -500); + + QCOMPARE(RealTime::fromSeconds(-0.5), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(-1), RealTime(-1, 0)); + QCOMPARE(RealTime::fromSeconds(-1.5), RealTime(-1, -ONE_BILLION/2)); } void fromMilliseconds() { - QCOMPARE(RealTime::fromMilliseconds(0), RealTime(0, 0)); - QCOMPARE(RealTime::fromMilliseconds(500), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime::fromMilliseconds(1000), RealTime(1, 0)); - QCOMPARE(RealTime::fromMilliseconds(1500), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(0), RealTime(0, 0)); + QCOMPARE(RealTime::fromMilliseconds(500), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(1000), RealTime(1, 0)); + QCOMPARE(RealTime::fromMilliseconds(1500), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime::fromMilliseconds(-0), RealTime(0, 0)); - QCOMPARE(RealTime::fromMilliseconds(-500), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime::fromMilliseconds(-1000), RealTime(-1, 0)); - QCOMPARE(RealTime::fromMilliseconds(-1500), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(-0), RealTime(0, 0)); + QCOMPARE(RealTime::fromMilliseconds(-500), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(-1000), RealTime(-1, 0)); + QCOMPARE(RealTime::fromMilliseconds(-1500), RealTime(-1, -ONE_BILLION/2)); } - + +#ifndef Q_OS_WIN void fromTimeval() { - struct timeval tv; + struct timeval tv; - tv.tv_sec = 0; tv.tv_usec = 0; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, 0)); - tv.tv_sec = 0; tv.tv_usec = ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, ONE_BILLION/2)); - tv.tv_sec = 1; tv.tv_usec = 0; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, 0)); - tv.tv_sec = 1; tv.tv_usec = ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, ONE_BILLION/2)); + tv.tv_sec = 0; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, 0)); + tv.tv_sec = 0; tv.tv_usec = ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, ONE_BILLION/2)); + tv.tv_sec = 1; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, 0)); + tv.tv_sec = 1; tv.tv_usec = ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, ONE_BILLION/2)); - tv.tv_sec = 0; tv.tv_usec = -ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, -ONE_BILLION/2)); - tv.tv_sec = -1; tv.tv_usec = 0; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, 0)); - tv.tv_sec = -1; tv.tv_usec = -ONE_MILLION/2; - QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, -ONE_BILLION/2)); + tv.tv_sec = 0; tv.tv_usec = -ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, -ONE_BILLION/2)); + tv.tv_sec = -1; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, 0)); + tv.tv_sec = -1; tv.tv_usec = -ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, -ONE_BILLION/2)); } +#endif void assign() { - RealTime r; - r = RealTime(0, 0); - QCOMPARE(r, RealTime::zeroTime); - r = RealTime(0, ONE_BILLION/2); + RealTime r; + r = RealTime(0, 0); + QCOMPARE(r, RealTime::zeroTime); + r = RealTime(0, ONE_BILLION/2); QCOMPARE(r.sec, 0); QCOMPARE(r.nsec, ONE_BILLION/2); - r = RealTime(-1, -ONE_BILLION/2); + r = RealTime(-1, -ONE_BILLION/2); QCOMPARE(r.sec, -1); QCOMPARE(r.nsec, -ONE_BILLION/2); } void plus() { - QCOMPARE(RealTime(0, 0) + RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(RealTime(0, 0) + RealTime(0, 0), RealTime(0, 0)); - QCOMPARE(RealTime(0, 0) + RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(0, ONE_BILLION/2), RealTime(1, 0)); - QCOMPARE(RealTime(1, 0) + RealTime(0, ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) + RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(0, ONE_BILLION/2), RealTime(1, 0)); + QCOMPARE(RealTime(1, 0) + RealTime(0, ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(0, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(-1, 0)); - QCOMPARE(RealTime(-1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(-1, 0)); + QCOMPARE(RealTime(-1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); - QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(-1, 0), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(1, 0), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(-1, 0), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(1, 0), RealTime(0, ONE_BILLION/2)); } void minus() { - QCOMPARE(RealTime(0, 0) - RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(RealTime(0, 0) - RealTime(0, 0), RealTime(0, 0)); - QCOMPARE(RealTime(0, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(0, ONE_BILLION/2), RealTime(0, 0)); - QCOMPARE(RealTime(1, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(0, ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(1, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(0, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); - QCOMPARE(RealTime(-1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(-1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, 0)); - QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, 0)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, ONE_BILLION/2)); - QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(-1, 0), RealTime(1, ONE_BILLION/2)); - QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(1, 0), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(-1, 0), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(1, 0), RealTime(-1, -ONE_BILLION/2)); } void negate() { - QCOMPARE(-RealTime(0, 0), RealTime(0, 0)); - QCOMPARE(-RealTime(1, 0), RealTime(-1, 0)); - QCOMPARE(-RealTime(1, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); - QCOMPARE(-RealTime(-1, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(-RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(-RealTime(1, 0), RealTime(-1, 0)); + QCOMPARE(-RealTime(1, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(-RealTime(-1, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); } void compare() { - int sec, nsec; - for (sec = -2; sec <= 2; sec += 2) { - for (nsec = -1; nsec <= 1; nsec += 1) { - QCOMPARE(RealTime(sec, nsec) < RealTime(sec, nsec), false); - QCOMPARE(RealTime(sec, nsec) > RealTime(sec, nsec), false); - QCOMPARE(RealTime(sec, nsec) == RealTime(sec, nsec), true); - QCOMPARE(RealTime(sec, nsec) != RealTime(sec, nsec), false); - QCOMPARE(RealTime(sec, nsec) <= RealTime(sec, nsec), true); - QCOMPARE(RealTime(sec, nsec) >= RealTime(sec, nsec), true); - } - } - RealTime prev(-3, 0); - for (sec = -2; sec <= 2; sec += 2) { - for (nsec = -1; nsec <= 1; nsec += 1) { + int sec, nsec; + for (sec = -2; sec <= 2; sec += 2) { + for (nsec = -1; nsec <= 1; nsec += 1) { + QCOMPARE(RealTime(sec, nsec) < RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) > RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) == RealTime(sec, nsec), true); + QCOMPARE(RealTime(sec, nsec) != RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) <= RealTime(sec, nsec), true); + QCOMPARE(RealTime(sec, nsec) >= RealTime(sec, nsec), true); + } + } + RealTime prev(-3, 0); + for (sec = -2; sec <= 2; sec += 2) { + for (nsec = -1; nsec <= 1; nsec += 1) { - RealTime curr(sec, nsec); + RealTime curr(sec, nsec); - QCOMPARE(prev < curr, true); - QCOMPARE(prev > curr, false); - QCOMPARE(prev == curr, false); - QCOMPARE(prev != curr, true); - QCOMPARE(prev <= curr, true); - QCOMPARE(prev >= curr, false); + QCOMPARE(prev < curr, true); + QCOMPARE(prev > curr, false); + QCOMPARE(prev == curr, false); + QCOMPARE(prev != curr, true); + QCOMPARE(prev <= curr, true); + QCOMPARE(prev >= curr, false); - QCOMPARE(curr < prev, false); - QCOMPARE(curr > prev, true); - QCOMPARE(curr == prev, false); - QCOMPARE(curr != prev, true); - QCOMPARE(curr <= prev, false); - QCOMPARE(curr >= prev, true); + QCOMPARE(curr < prev, false); + QCOMPARE(curr > prev, true); + QCOMPARE(curr == prev, false); + QCOMPARE(curr != prev, true); + QCOMPARE(curr <= prev, false); + QCOMPARE(curr >= prev, true); - prev = curr; - } - } + prev = curr; + } + } } void frame() diff -r d4a28d1479a8 -r 710e6250a401 base/test/files.pri --- a/base/test/files.pri Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/files.pri Mon Sep 17 13:51:14 2018 +0100 @@ -1,10 +1,12 @@ TEST_HEADERS = \ + TestColumnOp.h \ + TestLogRange.h \ TestRangeMapper.h \ + TestOurRealTime.h \ TestPitch.h \ - TestOurRealTime.h \ - TestVampRealTime.h \ + TestScaleTickIntervals.h \ TestStringBits.h \ - TestColumnOp.h + TestVampRealTime.h TEST_SOURCES += \ svcore-base-test.cpp diff -r d4a28d1479a8 -r 710e6250a401 base/test/svcore-base-test.cpp --- a/base/test/svcore-base-test.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/base/test/svcore-base-test.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -11,8 +11,10 @@ COPYING included with this distribution for more information. */ +#include "TestLogRange.h" #include "TestRangeMapper.h" #include "TestPitch.h" +#include "TestScaleTickIntervals.h" #include "TestStringBits.h" #include "TestOurRealTime.h" #include "TestVampRealTime.h" @@ -27,45 +29,55 @@ int good = 0, bad = 0; QCoreApplication app(argc, argv); - app.setOrganizationName("Sonic Visualiser"); + app.setOrganizationName("sonic-visualiser"); app.setApplicationName("test-svcore-base"); { - TestRangeMapper t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + TestRangeMapper t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } { - TestPitch t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + TestPitch t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } { TestOurRealTime t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } { TestVampRealTime t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } { - TestStringBits t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + TestStringBits t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } { - TestColumnOp t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + TestColumnOp t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + { + TestLogRange t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + { + TestScaleTickIntervals t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } if (bad > 0) { - cerr << "\n********* " << bad << " test suite(s) failed!\n" << endl; - return 1; + SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; + return 1; } else { - cerr << "All tests passed" << endl; - return 0; + SVCERR << "All tests passed" << endl; + return 0; } } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/AudioFileReader.cpp --- a/data/fileio/AudioFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/AudioFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -17,17 +17,17 @@ using std::vector; -vector> +vector AudioFileReader::getDeInterleavedFrames(sv_frame_t start, sv_frame_t count) const { - vector interleaved = getInterleavedFrames(start, count); + floatvec_t interleaved = getInterleavedFrames(start, count); int channels = getChannelCount(); if (channels == 1) return { interleaved }; sv_frame_t rc = interleaved.size() / channels; - vector> frames(channels, vector(rc, 0.f)); + vector frames(channels, floatvec_t(rc, 0.f)); for (int c = 0; c < channels; ++c) { for (sv_frame_t i = 0; i < rc; ++i) { diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/AudioFileReader.h --- a/data/fileio/AudioFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/AudioFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -117,8 +117,8 @@ * thread-safe -- that is, safe to call from multiple threads with * different arguments on the same object at the same time. */ - virtual std::vector getInterleavedFrames(sv_frame_t start, - sv_frame_t count) const = 0; + virtual floatvec_t getInterleavedFrames(sv_frame_t start, + sv_frame_t count) const = 0; /** * Return de-interleaved samples for count frames from index @@ -127,8 +127,8 @@ * will contain getChannelCount() sample blocks of count samples * each (or fewer if end of file is reached). */ - virtual std::vector > getDeInterleavedFrames(sv_frame_t start, - sv_frame_t count) const; + virtual std::vector getDeInterleavedFrames(sv_frame_t start, + sv_frame_t count) const; // only subclasses that do not know exactly how long the audio // file is until it's been completely decoded should implement this diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/AudioFileReaderFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -19,7 +19,6 @@ #include "DecodingWavFileReader.h" #include "OggVorbisFileReader.h" #include "MP3FileReader.h" -#include "QuickTimeFileReader.h" #include "CoreAudioFileReader.h" #include "AudioFileSizeEstimator.h" @@ -43,9 +42,6 @@ OggVorbisFileReader::getSupportedExtensions(extensions); #endif #endif -#ifdef HAVE_QUICKTIME - QuickTimeFileReader::getSupportedExtensions(extensions); -#endif #ifdef HAVE_COREAUDIO CoreAudioFileReader::getSupportedExtensions(extensions); #endif @@ -67,15 +63,16 @@ { QString err; - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\"): Requested rate: " << params.targetRate << (params.targetRate == 0 ? " (use source rate)" : "") << endl; + SVDEBUG << "AudioFileReaderFactory: url \"" << source.getLocation() << "\": requested rate: " << params.targetRate << (params.targetRate == 0 ? " (use source rate)" : "") << endl; + SVDEBUG << "AudioFileReaderFactory: local filename \"" << source.getLocalFilename() << "\", content type \"" << source.getContentType() << "\"" << endl; if (!source.isOK()) { - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl; + SVCERR << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Failed to retrieve source (transmission error?): " << source.getErrorString() << endl; return 0; } if (!source.isAvailable()) { - SVDEBUG << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; + SVCERR << "AudioFileReaderFactory::createReader(\"" << source.getLocation() << "\": Source not found" << endl; return 0; } @@ -92,11 +89,16 @@ if (estimatedSamples > 0) { size_t kb = (estimatedSamples * sizeof(float)) / 1024; + SVDEBUG << "AudioFileReaderFactory: checking where to potentially cache " + << kb << "K of sample data" << endl; StorageAdviser::Recommendation rec = StorageAdviser::recommend(StorageAdviser::SpeedCritical, kb, kb); if ((rec & StorageAdviser::UseMemory) || (rec & StorageAdviser::PreferMemory)) { + SVDEBUG << "AudioFileReaderFactory: cacheing (if at all) in memory" << endl; cacheMode = CodedAudioFileReader::CacheInMemory; + } else { + SVDEBUG << "AudioFileReaderFactory: cacheing (if at all) on disc" << endl; } } @@ -118,6 +120,34 @@ bool anyReader = (any > 0); + if (!anyReader) { + SVDEBUG << "AudioFileReaderFactory: Checking whether any reader officially handles this source" << endl; + } else { + SVDEBUG << "AudioFileReaderFactory: Source not officially handled by any reader, trying again with each reader in turn" + << endl; + } + +#ifdef HAVE_OGGZ +#ifdef HAVE_FISHSOUND + // If we have the "real" Ogg reader, use that first. Otherwise + // the WavFileReader will likely accept Ogg files (as + // libsndfile supports them) but it has no ability to return + // file metadata, so we get a slightly less useful result. + if (anyReader || OggVorbisFileReader::supports(source)) { + + reader = new OggVorbisFileReader + (source, decodeMode, cacheMode, targetRate, normalised, reporter); + + if (reader->isOK()) { + SVDEBUG << "AudioFileReaderFactory: Ogg file reader is OK, returning it" << endl; + return reader; + } else { + delete reader; + } + } +#endif +#endif + if (anyReader || WavFileReader::supports(source)) { reader = new WavFileReader(source); @@ -130,7 +160,7 @@ (cacheMode == CodedAudioFileReader::CacheInMemory) || (targetRate != 0 && fileRate != targetRate))) { - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; + SVDEBUG << "AudioFileReaderFactory: WAV file reader rate: " << reader->getSampleRate() << ", normalised " << normalised << ", seekable " << reader->isQuicklySeekable() << ", in memory " << (cacheMode == CodedAudioFileReader::CacheInMemory) << ", creating decoding reader" << endl; delete reader; reader = new DecodingWavFileReader @@ -142,27 +172,12 @@ } if (reader->isOK()) { + SVDEBUG << "AudioFileReaderFactory: WAV file reader is OK, returning it" << endl; return reader; } else { delete reader; } } - -#ifdef HAVE_OGGZ -#ifdef HAVE_FISHSOUND - if (anyReader || OggVorbisFileReader::supports(source)) { - - reader = new OggVorbisFileReader - (source, decodeMode, cacheMode, targetRate, normalised, reporter); - - if (reader->isOK()) { - return reader; - } else { - delete reader; - } - } -#endif -#endif #ifdef HAVE_MAD if (anyReader || MP3FileReader::supports(source)) { @@ -177,20 +192,7 @@ targetRate, normalised, reporter); if (reader->isOK()) { - return reader; - } else { - delete reader; - } - } -#endif - -#ifdef HAVE_QUICKTIME - if (anyReader || QuickTimeFileReader::supports(source)) { - - reader = new QuickTimeFileReader - (source, decodeMode, cacheMode, targetRate, normalised, reporter); - - if (reader->isOK()) { + SVDEBUG << "AudioFileReaderFactory: MP3 file reader is OK, returning it" << endl; return reader; } else { delete reader; @@ -205,6 +207,7 @@ (source, decodeMode, cacheMode, targetRate, normalised, reporter); if (reader->isOK()) { + SVDEBUG << "AudioFileReaderFactory: CoreAudio reader is OK, returning it" << endl; return reader; } else { delete reader; @@ -214,10 +217,11 @@ } - SVDEBUG << "AudioFileReaderFactory::Failed to create a reader for " - << "url \"" << source.getLocation() - << "\" (content type \"" - << source.getContentType() << "\")" << endl; + SVCERR << "AudioFileReaderFactory::Failed to create a reader for " + << "url \"" << source.getLocation() + << "\" (local filename \"" << source.getLocalFilename() + << "\", content type \"" + << source.getContentType() << "\")" << endl; return nullptr; } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/AudioFileSizeEstimator.cpp --- a/data/fileio/AudioFileSizeEstimator.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/AudioFileSizeEstimator.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -18,90 +18,95 @@ #include -//#define DEBUG_AUDIO_FILE_SIZE_ESTIMATOR 1 +#include "base/Debug.h" sv_frame_t AudioFileSizeEstimator::estimate(FileSource source, - sv_samplerate_t targetRate) + sv_samplerate_t targetRate) { sv_frame_t estimate = 0; + SVDEBUG << "AudioFileSizeEstimator: Sample count estimate requested for file \"" + << source.getLocalFilename() << "\"" << endl; + // Most of our file readers don't know the sample count until // after they've finished decoding. This is an exception: WavFileReader *reader = new WavFileReader(source); if (reader->isOK() && - reader->getChannelCount() > 0 && - reader->getFrameCount() > 0) { - sv_frame_t samples = - reader->getFrameCount() * reader->getChannelCount(); - sv_samplerate_t rate = reader->getSampleRate(); - if (targetRate != 0.0 && targetRate != rate) { - samples = sv_frame_t(double(samples) * targetRate / rate); - } - delete reader; - estimate = samples; + reader->getChannelCount() > 0 && + reader->getFrameCount() > 0) { + sv_frame_t samples = + reader->getFrameCount() * reader->getChannelCount(); + sv_samplerate_t rate = reader->getSampleRate(); + if (targetRate != 0.0 && targetRate != rate) { + samples = sv_frame_t(double(samples) * targetRate / rate); + } + SVDEBUG << "AudioFileSizeEstimator: WAV file reader accepts this file, reports " + << samples << " samples" << endl; + estimate = samples; + } else { + SVDEBUG << "AudioFileSizeEstimator: WAV file reader doesn't like this file, " + << "estimating from file size and extension instead" << endl; } + delete reader; + reader = 0; + if (estimate == 0) { - // The remainder just makes an estimate based on the file size - // and extension. We don't even know its sample rate at this - // point, so the following is a wild guess. - - double rateRatio = 1.0; - if (targetRate != 0.0) { - rateRatio = targetRate / 44100.0; - } + // The remainder just makes an estimate based on the file size + // and extension. We don't even know its sample rate at this + // point, so the following is a wild guess. + + double rateRatio = 1.0; + if (targetRate != 0.0) { + rateRatio = targetRate / 44100.0; + } - QString extension = source.getExtension(); + QString extension = source.getExtension(); - source.waitForData(); - if (!source.isOK()) return 0; + source.waitForData(); + if (!source.isOK()) return 0; - sv_frame_t sz = 0; - { - QFile f(source.getLocalFilename()); - if (f.open(QFile::ReadOnly)) { -#ifdef DEBUG_AUDIO_FILE_SIZE_ESTIMATOR - cerr << "opened file, size is " << f.size() << endl; -#endif - sz = f.size(); - f.close(); - } - } + sv_frame_t sz = 0; - if (extension == "ogg" || extension == "oga" || - extension == "m4a" || extension == "mp3" || - extension == "wma") { + { + QFile f(source.getLocalFilename()); + if (f.open(QFile::ReadOnly)) { + SVDEBUG << "AudioFileSizeEstimator: opened file, size is " + << f.size() << endl; + sz = f.size(); + f.close(); + } + } - // Usually a lossy file. Compression ratios can vary - // dramatically, but don't usually exceed about 20x compared - // to 16-bit PCM (e.g. a 128kbps mp3 has 11x ratio over WAV at - // 44.1kHz). We can estimate the number of samples to be file - // size x 20, divided by 2 as we're comparing with 16-bit PCM. + if (extension == "ogg" || extension == "oga" || + extension == "m4a" || extension == "mp3" || + extension == "wma") { - estimate = sv_frame_t(double(sz) * 10 * rateRatio); - } + // Usually a lossy file. Compression ratios can vary + // dramatically, but don't usually exceed about 20x compared + // to 16-bit PCM (e.g. a 128kbps mp3 has 11x ratio over WAV at + // 44.1kHz). We can estimate the number of samples to be file + // size x 20, divided by 2 as we're comparing with 16-bit PCM. - if (extension == "flac") { - - // FLAC usually takes up a bit more than half the space of - // 16-bit PCM. So the number of 16-bit samples is roughly the - // same as the file size in bytes. As above, let's be - // conservative. + estimate = sv_frame_t(double(sz) * 10 * rateRatio); + } - estimate = sv_frame_t(double(sz) * 1.2 * rateRatio); - } + if (extension == "flac") { + + // FLAC usually takes up a bit more than half the space of + // 16-bit PCM. So the number of 16-bit samples is roughly the + // same as the file size in bytes. As above, let's be + // conservative. -#ifdef DEBUG_AUDIO_FILE_SIZE_ESTIMATOR - cerr << "AudioFileSizeEstimator: for extension " << extension << ", estimate = " << estimate << endl; -#endif + estimate = sv_frame_t(double(sz) * 1.2 * rateRatio); + } + + SVDEBUG << "AudioFileSizeEstimator: for extension \"" + << extension << "\", estimate = " << estimate << " samples" << endl; } - -#ifdef DEBUG_AUDIO_FILE_SIZE_ESTIMATOR - cerr << "estimate = " << estimate << endl; -#endif return estimate; } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/AudioFileSizeEstimator.h --- a/data/fileio/AudioFileSizeEstimator.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/AudioFileSizeEstimator.h Mon Sep 17 13:51:14 2018 +0100 @@ -43,7 +43,7 @@ * will return 0. */ static sv_frame_t estimate(FileSource source, - sv_samplerate_t targetRate = 0); + sv_samplerate_t targetRate = 0); }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/BZipFileDevice.cpp --- a/data/fileio/BZipFileDevice.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/BZipFileDevice.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -21,8 +21,16 @@ #include "base/Debug.h" +// for dup: +#ifdef _MSC_VER +#include +#else +#include +#endif + BZipFileDevice::BZipFileDevice(QString fileName) : m_fileName(fileName), + m_qfile(fileName), m_file(0), m_bzFile(0), m_atEnd(true), @@ -70,11 +78,39 @@ return false; } + // This is all going to be a bit silly. + // + // We open the file with QFile so as not to have to worry about locale + // support ourselves (especially on Windows). Then we get a fd from + // QFile and "convert" it to a FILE* using fdopen because that is what + // the bz2 library needs for reading and writing an already-open file. + // + // fdopen takes over the fd it is given, and will close it when fclose + // is called. (We must call fclose, because it's needed to avoid + // leaking the file stream structure.) + // + // But QFile will also close its fd, either when we call QFile::close + // or on destruction -- there doesn't seem to be a way to avoid that + // for a file that QFile opened. + // + // So we have to add an extra dup() in to the fdopen to avoid a double + // close. + // + // Note that bz2 will *not* fclose the FILE* it was passed, so we + // don't have a problem with calling both bzWriteClose and fclose. + if (mode & WriteOnly) { - m_file = fopen(m_fileName.toLocal8Bit().data(), "wb"); + if (!m_qfile.open(QIODevice::WriteOnly)) { + setErrorString(tr("Failed to open file for writing")); + m_ok = false; + return false; + } + + m_file = fdopen(dup(m_qfile.handle()), "wb"); if (!m_file) { - setErrorString(tr("Failed to open file for writing")); + setErrorString(tr("Failed to open file handle for writing")); + m_qfile.close(); m_ok = false; return false; } @@ -85,6 +121,7 @@ if (!m_bzFile) { fclose(m_file); m_file = 0; + m_qfile.close(); setErrorString(tr("Failed to open bzip2 stream for writing")); m_ok = false; return false; @@ -99,9 +136,15 @@ if (mode & ReadOnly) { - m_file = fopen(m_fileName.toLocal8Bit().data(), "rb"); + if (!m_qfile.open(QIODevice::ReadOnly)) { + setErrorString(tr("Failed to open file for reading")); + m_ok = false; + return false; + } + + m_file = fdopen(dup(m_qfile.handle()), "rb"); if (!m_file) { - setErrorString(tr("Failed to open file for reading")); + setErrorString(tr("Failed to open file handle for reading")); m_ok = false; return false; } @@ -112,6 +155,7 @@ if (!m_bzFile) { fclose(m_file); m_file = 0; + m_qfile.close(); setErrorString(tr("Failed to open bzip2 stream for reading")); m_ok = false; return false; @@ -145,11 +189,12 @@ if (openMode() & WriteOnly) { unsigned int in = 0, out = 0; BZ2_bzWriteClose(&bzError, m_bzFile, 0, &in, &out); -// cerr << "Wrote bzip2 stream (in=" << in << ", out=" << out << ")" << endl; - if (bzError != BZ_OK) { - setErrorString(tr("bzip2 stream write close error")); - } +// cerr << "Wrote bzip2 stream (in=" << in << ", out=" << out << ")" << endl; + if (bzError != BZ_OK) { + setErrorString(tr("bzip2 stream write close error")); + } fclose(m_file); + m_qfile.close(); m_bzFile = 0; m_file = 0; m_ok = false; @@ -162,6 +207,7 @@ setErrorString(tr("bzip2 stream read close error")); } fclose(m_file); + m_qfile.close(); m_bzFile = 0; m_file = 0; m_ok = false; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/BZipFileDevice.h --- a/data/fileio/BZipFileDevice.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/BZipFileDevice.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,10 +13,11 @@ COPYING included with this distribution for more information. */ -#ifndef _BZIP_FILE_DEVICE_H_ -#define _BZIP_FILE_DEVICE_H_ +#ifndef SV_BZIP_FILE_DEVICE_H +#define SV_BZIP_FILE_DEVICE_H #include +#include #include @@ -41,6 +42,7 @@ QString m_fileName; + QFile m_qfile; FILE *m_file; BZFILE *m_bzFile; bool m_atEnd; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVFileReader.cpp --- a/data/fileio/CSVFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CSVFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -18,60 +18,79 @@ #include "model/Model.h" #include "base/RealTime.h" #include "base/StringBits.h" +#include "base/ProgressReporter.h" +#include "base/RecordDirectory.h" #include "model/SparseOneDimensionalModel.h" #include "model/SparseTimeValueModel.h" #include "model/EditableDenseThreeDimensionalModel.h" #include "model/RegionModel.h" #include "model/NoteModel.h" +#include "model/WritableWaveFileModel.h" #include "DataFileReaderFactory.h" #include +#include #include #include #include #include #include +#include #include #include +#include using namespace std; CSVFileReader::CSVFileReader(QString path, CSVFormat format, - sv_samplerate_t mainModelSampleRate) : + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) : m_format(format), m_device(0), m_ownDevice(true), m_warnings(0), - m_mainModelSampleRate(mainModelSampleRate) + m_mainModelSampleRate(mainModelSampleRate), + m_fileSize(0), + m_readCount(0), + m_progress(-1), + m_reporter(reporter) { QFile *file = new QFile(path); bool good = false; if (!file->exists()) { - m_error = QFile::tr("File \"%1\" does not exist").arg(path); + m_error = QFile::tr("File \"%1\" does not exist").arg(path); } else if (!file->open(QIODevice::ReadOnly | QIODevice::Text)) { - m_error = QFile::tr("Failed to open file \"%1\"").arg(path); + m_error = QFile::tr("Failed to open file \"%1\"").arg(path); } else { - good = true; + good = true; } if (good) { m_device = file; m_filename = QFileInfo(path).fileName(); + m_fileSize = file->size(); + if (m_reporter) m_reporter->setDefinite(true); } else { - delete file; + delete file; } } CSVFileReader::CSVFileReader(QIODevice *device, CSVFormat format, - sv_samplerate_t mainModelSampleRate) : + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) : m_format(format), m_device(device), m_ownDevice(false), m_warnings(0), - m_mainModelSampleRate(mainModelSampleRate) + m_mainModelSampleRate(mainModelSampleRate), + m_fileSize(0), + m_readCount(0), + m_progress(-1), + m_reporter(reporter) { + if (m_reporter) m_reporter->setDefinite(false); } CSVFileReader::~CSVFileReader() @@ -137,12 +156,12 @@ if (!ok) { if (m_warnings < warnLimit) { - cerr << "WARNING: CSVFileReader::load: " + SVCERR << "WARNING: CSVFileReader::load: " << "Bad time format (\"" << s << "\") in data line " << lineno+1 << endl; } else if (m_warnings == warnLimit) { - cerr << "WARNING: Too many warnings" << endl; + SVCERR << "WARNING: Too many warnings" << endl; } ++m_warnings; } @@ -172,10 +191,10 @@ } else { windowSize = 1; } - if (timeUnits == CSVFormat::TimeSeconds || + if (timeUnits == CSVFormat::TimeSeconds || timeUnits == CSVFormat::TimeMilliseconds) { - sampleRate = m_mainModelSampleRate; - } + sampleRate = m_mainModelSampleRate; + } } SparseOneDimensionalModel *model1 = 0; @@ -183,6 +202,7 @@ RegionModel *model2a = 0; NoteModel *model2b = 0; EditableDenseThreeDimensionalModel *model3 = 0; + WritableWaveFileModel *modelW = 0; Model *model = 0; QTextStream in(m_device); @@ -202,8 +222,6 @@ sv_frame_t startFrame = 0; // for calculation of dense model resolution bool firstEverValue = true; - - map labelCountMap; int valueColumns = 0; for (int i = 0; i < m_format.getColumnCount(); ++i) { @@ -212,7 +230,41 @@ } } - while (!in.atEnd()) { + int audioChannels = 0; + float **audioSamples = 0; + float sampleShift = 0.f; + float sampleScale = 1.f; + + if (modelType == CSVFormat::WaveFileModel) { + + audioChannels = valueColumns; + + audioSamples = + breakfastquay::allocate_and_zero_channels + (audioChannels, 1); + + switch (m_format.getAudioSampleRange()) { + case CSVFormat::SampleRangeSigned1: + case CSVFormat::SampleRangeOther: + sampleShift = 0.f; + sampleScale = 1.f; + break; + case CSVFormat::SampleRangeUnsigned255: + sampleShift = -128.f; + sampleScale = 1.f / 128.f; + break; + case CSVFormat::SampleRangeSigned32767: + sampleShift = 0.f; + sampleScale = 1.f / 32768.f; + break; + } + } + + map labelCountMap; + + bool abandoned = false; + + while (!in.atEnd() && !abandoned) { // QTextStream's readLine doesn't cope with old-style Mac // CR-only line endings. Why did they bother making the class @@ -227,6 +279,26 @@ QString chunk = in.readLine(); QStringList lines = chunk.split('\r', QString::SkipEmptyParts); + + m_readCount += chunk.size() + 1; + + if (m_reporter) { + if (m_reporter->wasCancelled()) { + abandoned = true; + break; + } + int progress; + if (m_fileSize > 0) { + progress = int((double(m_readCount) / double(m_fileSize)) + * 100.0); + } else { + progress = int(m_readCount / 10000); + } + if (progress != m_progress) { + m_reporter->setProgress(progress); + m_progress = progress; + } + } for (int li = 0; li < lines.size(); ++li) { @@ -237,28 +309,30 @@ QStringList list = StringBits::split(line, separator, allowQuoting); if (!model) { + QString modelName = m_filename; + switch (modelType) { case CSVFormat::OneDimensionalModel: model1 = new SparseOneDimensionalModel(sampleRate, windowSize); model = model1; break; - + case CSVFormat::TwoDimensionalModel: model2 = new SparseTimeValueModel(sampleRate, windowSize, false); model = model2; break; - + case CSVFormat::TwoDimensionalModelWithDuration: model2a = new RegionModel(sampleRate, windowSize, false); model = model2a; break; - + case CSVFormat::TwoDimensionalModelWithDurationAndPitch: model2b = new NoteModel(sampleRate, windowSize, false); model = model2b; break; - + case CSVFormat::ThreeDimensionalModel: model3 = new EditableDenseThreeDimensionalModel (sampleRate, @@ -267,22 +341,50 @@ EditableDenseThreeDimensionalModel::NoCompression); model = model3; break; + + case CSVFormat::WaveFileModel: + { + bool normalise = (m_format.getAudioSampleRange() + == CSVFormat::SampleRangeOther); + QString path = getConvertedAudioFilePath(); + modelW = new WritableWaveFileModel + (path, sampleRate, valueColumns, + normalise ? + WritableWaveFileModel::Normalisation::Peak : + WritableWaveFileModel::Normalisation::None); + modelName = QFileInfo(path).fileName(); + model = modelW; + break; + } } - if (model) { - if (m_filename != "") { - model->setObjectName(m_filename); + if (model && model->isOK()) { + if (modelName != "") { + model->setObjectName(modelName); } } } + if (!model || !model->isOK()) { + SVCERR << "Failed to create model to load CSV file into" + << endl; + if (model) { + delete model; + model = 0; + model1 = 0; model2 = 0; model2a = 0; model2b = 0; + model3 = 0; modelW = 0; + } + abandoned = true; + break; + } + float value = 0.f; float pitch = 0.f; QString label = ""; duration = 0.f; haveEndTime = false; - + for (int i = 0; i < list.size(); ++i) { QString s = list[i]; @@ -334,7 +436,7 @@ } if (modelType == CSVFormat::OneDimensionalModel) { - + SparseOneDimensionalModel::Point point(frameNo, label); model1->addPoint(point); @@ -368,7 +470,7 @@ float value = list[i].toFloat(&ok); values.push_back(value); - + if (firstEverValue || value < min) min = value; if (firstEverValue || value > max) max = value; @@ -384,25 +486,69 @@ if (!ok) { if (warnings < warnLimit) { - cerr << "WARNING: CSVFileReader::load: " + SVCERR << "WARNING: CSVFileReader::load: " << "Non-numeric value \"" << list[i] << "\" in data line " << lineno+1 << ":" << endl; - cerr << line << endl; + SVCERR << line << endl; ++warnings; } else if (warnings == warnLimit) { -// cerr << "WARNING: Too many warnings" << endl; +// SVCERR << "WARNING: Too many warnings" << endl; } } } - + // SVDEBUG << "Setting bin values for count " << lineno << ", frame " // << frameNo << ", time " << RealTime::frame2RealTime(frameNo, sampleRate) << endl; model3->setColumn(lineno, values); + + } else if (modelType == CSVFormat::WaveFileModel) { + + int channel = 0; + + for (int i = 0; + i < list.size() && channel < audioChannels; + ++i) { + + if (m_format.getColumnPurpose(i) != + CSVFormat::ColumnValue) { + continue; + } + + bool ok = false; + float value = list[i].toFloat(&ok); + if (!ok) { + value = 0.f; + } + + value += sampleShift; + value *= sampleScale; + + audioSamples[channel][0] = value; + + ++channel; + } + + while (channel < audioChannels) { + audioSamples[channel][0] = 0.f; + ++channel; + } + + bool ok = modelW->addSamples(audioSamples, 1); + + if (!ok) { + if (warnings < warnLimit) { + SVCERR << "WARNING: CSVFileReader::load: " + << "Unable to add sample to wave-file model" + << endl; + SVCERR << line << endl; + ++warnings; + } + } } - + ++lineno; if (timingType == CSVFormat::ImplicitTiming || list.size() == 0) { @@ -426,11 +572,11 @@ for (map >::iterator i = countLabelValueMap.end(); i != countLabelValueMap.begin(); ) { --i; - cerr << "count -> " << i->first << endl; + SVCERR << "count -> " << i->first << endl; for (map::iterator j = i->second.begin(); j != i->second.end(); ++j) { j->second = v; - cerr << "label -> " << j->first << ", value " << v << endl; + SVCERR << "label -> " << j->first << ", value " << v << endl; v = v + 1.f; } } @@ -443,7 +589,7 @@ RegionModel::Point p(*i); int count = labelCountMap[p.label]; v = countLabelValueMap[count][p.label]; - cerr << "mapping from label \"" << p.label << "\" (count " << count << ") to value " << v << endl; + // SVCERR << "mapping from label \"" << p.label << "\" (count " << count << ") to value " << v << endl; RegionModel::Point pp(p.frame, v, p.duration, p.label); pointMap[p] = pp; } @@ -474,10 +620,36 @@ } if (model3) { - model3->setMinimumLevel(min); - model3->setMaximumLevel(max); + model3->setMinimumLevel(min); + model3->setMaximumLevel(max); + } + + if (modelW) { + breakfastquay::deallocate_channels(audioSamples, audioChannels); + modelW->updateModel(); + modelW->writeComplete(); } return model; } +QString +CSVFileReader::getConvertedAudioFilePath() const +{ + QString base = m_filename; + base.replace(QRegExp("[/\\,.:;~<>\"'|?%*]+"), "_"); + + QString convertedFileDir = RecordDirectory::getConvertedAudioDirectory(); + if (convertedFileDir == "") { + SVCERR << "WARNING: CSVFileReader::getConvertedAudioFilePath: Failed to retrieve converted audio directory" << endl; + return ""; + } + + auto ms = QDateTime::currentDateTime().toMSecsSinceEpoch(); + auto s = ms / 1000; // there is a toSecsSinceEpoch in Qt 5.8 but + // we currently want to support older versions + + return QDir(convertedFileDir).filePath + (QString("%1-%2.wav").arg(base).arg(s)); +} + diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVFileReader.h --- a/data/fileio/CSVFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CSVFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _CSV_FILE_READER_H_ -#define _CSV_FILE_READER_H_ +#ifndef SV_CSV_FILE_READER_H +#define SV_CSV_FILE_READER_H #include "DataFileReader.h" @@ -27,6 +27,7 @@ #include class QFile; +class ProgressReporter; class CSVFileReader : public DataFileReader { @@ -35,7 +36,9 @@ * Construct a CSVFileReader to read the CSV file at the given * path, with the given format. */ - CSVFileReader(QString path, CSVFormat format, sv_samplerate_t mainModelSampleRate); + CSVFileReader(QString path, CSVFormat format, + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); /** * Construct a CSVFileReader to read from the given @@ -43,7 +46,9 @@ * CSVFileReader will not close or delete it and it must outlive * the CSVFileReader. */ - CSVFileReader(QIODevice *device, CSVFormat format, sv_samplerate_t mainModelSampleRate); + CSVFileReader(QIODevice *device, CSVFormat format, + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); virtual ~CSVFileReader(); @@ -60,9 +65,15 @@ QString m_error; mutable int m_warnings; sv_samplerate_t m_mainModelSampleRate; + qint64 m_fileSize; + mutable qint64 m_readCount; + mutable int m_progress; + ProgressReporter *m_reporter; sv_frame_t convertTimeValue(QString, int lineno, sv_samplerate_t sampleRate, int windowSize) const; + + QString getConvertedAudioFilePath() const; }; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVFileWriter.cpp --- a/data/fileio/CSVFileWriter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CSVFileWriter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -14,6 +14,7 @@ */ #include "CSVFileWriter.h" +#include "CSVStreamWriter.h" #include "model/Model.h" #include "model/SparseOneDimensionalModel.h" @@ -27,6 +28,7 @@ #include #include +#include CSVFileWriter::CSVFileWriter(QString path, Model *model, @@ -59,30 +61,17 @@ void CSVFileWriter::write() { - try { - TempWriteFile temp(m_path); - - QFile file(temp.getTemporaryFilename()); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - m_error = tr("Failed to open file %1 for writing") - .arg(temp.getTemporaryFilename()); - return; - } - - QTextStream out(&file); - out << m_model->toDelimitedDataStringWithOptions - (m_delimiter, m_options); - - file.close(); - temp.moveToTarget(); - - } catch (FileOperationFailed &f) { - m_error = f.what(); - } + Selection all { + m_model->getStartFrame(), + m_model->getEndFrame() + }; + MultiSelection selections; + selections.addSelection(all); + writeSelection(selections); } void -CSVFileWriter::writeSelection(MultiSelection *selection) +CSVFileWriter::writeSelection(MultiSelection selection) { try { TempWriteFile temp(m_path); @@ -96,22 +85,34 @@ QTextStream out(&file); - for (MultiSelection::SelectionList::iterator i = - selection->getSelections().begin(); - i != selection->getSelections().end(); ++i) { - - sv_frame_t f0(i->getStartFrame()), f1(i->getEndFrame()); - out << m_model->toDelimitedDataStringSubsetWithOptions - (m_delimiter, m_options, f0, f1); + sv_frame_t blockSize = 65536; + + if (m_model->isSparse()) { + // Write the whole in one go, as re-seeking for each block + // may be very costly otherwise + sv_frame_t startFrame, endFrame; + selection.getExtents(startFrame, endFrame); + blockSize = endFrame - startFrame; } + + bool completed = CSVStreamWriter::writeInChunks( + out, + *m_model, + selection, + m_reporter, + m_delimiter, + m_options, + blockSize + ); file.close(); - temp.moveToTarget(); + if (completed) { + temp.moveToTarget(); + } } catch (FileOperationFailed &f) { m_error = f.what(); + } catch (const std::exception &e) { // ProgressReporter could throw + m_error = e.what(); } } - - - diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVFileWriter.h --- a/data/fileio/CSVFileWriter.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CSVFileWriter.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _CSV_FILE_WRITER_H_ -#define _CSV_FILE_WRITER_H_ +#ifndef SV_CSV_FILE_WRITER_H +#define SV_CSV_FILE_WRITER_H #include #include @@ -23,6 +23,7 @@ class Model; class MultiSelection; +class ProgressReporter; class CSVFileWriter : public QObject { @@ -33,13 +34,23 @@ Model *model, QString delimiter = ",", DataExportOptions options = DataExportDefaults); + + CSVFileWriter(QString path, + Model *model, + ProgressReporter *reporter, + QString delimiter = ",", + DataExportOptions options = DataExportDefaults) + : CSVFileWriter(path, model, delimiter, options) + { + m_reporter = reporter; + } virtual ~CSVFileWriter(); virtual bool isOK() const; virtual QString getError() const; virtual void write(); - virtual void writeSelection(MultiSelection *selection); + virtual void writeSelection(MultiSelection selection); protected: QString m_path; @@ -47,6 +58,7 @@ QString m_error; QString m_delimiter; DataExportOptions m_options; + ProgressReporter *m_reporter = nullptr; }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVFormat.cpp --- a/data/fileio/CSVFormat.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CSVFormat.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -25,18 +25,22 @@ #include +#include "base/Debug.h" + CSVFormat::CSVFormat(QString path) : m_separator(""), m_sampleRate(44100), m_windowSize(1024), m_allowQuoting(true) { - guessFormatFor(path); + (void)guessFormatFor(path); } -void +bool CSVFormat::guessFormatFor(QString path) { + m_separator = ""; // to prompt guessing for it + m_modelType = TwoDimensionalModel; m_timingType = ExplicitTiming; m_timeUnits = TimeSeconds; @@ -51,8 +55,17 @@ m_prevValues.clear(); QFile file(path); - if (!file.exists()) return; - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) return; + if (!file.exists()) { + SVCERR << "CSVFormat::guessFormatFor(" << path + << "): File does not exist" << endl; + return false; + } + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + SVCERR << "CSVFormat::guessFormatFor(" << path + << "): File could not be opened for reading" << endl; + return false; + } + SVDEBUG << "CSVFormat::guessFormatFor(" << path << ")" << endl; QTextStream in(&file); in.seek(0); @@ -69,38 +82,52 @@ for (int li = 0; li < lines.size(); ++li) { QString line = lines[li]; - if (line.startsWith("#") || line == "") continue; + if (line.startsWith("#") || line == "") { + continue; + } guessQualities(line, lineno); ++lineno; } - if (lineno >= 50) break; + if (lineno >= 150) break; } guessPurposes(); + guessAudioSampleRange(); + + return true; } void CSVFormat::guessSeparator(QString line) { - char candidates[] = { ',', '\t', ' ', '|', '/', ':' }; - for (int i = 0; i < int(sizeof(candidates)/sizeof(candidates[0])); ++i) { - if (StringBits::split(line, candidates[i], m_allowQuoting).size() >= 2) { + QString candidates = "\t|,/: "; + + for (int i = 0; i < candidates.length(); ++i) { + auto bits = StringBits::split(line, candidates[i], m_allowQuoting); + if (bits.size() >= 2) { + SVDEBUG << "Successfully split the line into:" << endl; + for (auto b: bits) { + SVDEBUG << b << endl; + } m_separator = candidates[i]; + SVDEBUG << "Estimated column separator: '" << m_separator + << "'" << endl; return; } } - m_separator = " "; } void CSVFormat::guessQualities(QString line, int lineno) { - if (m_separator == "") guessSeparator(line); + if (m_separator == "") { + guessSeparator(line); + } - QStringList list = StringBits::split(line, m_separator[0], m_allowQuoting); + QStringList list = StringBits::split(line, getSeparator(), m_allowQuoting); int cols = list.size(); if (lineno == 0 || (cols > m_columnCount)) m_columnCount = cols; @@ -110,10 +137,11 @@ // something that indicates otherwise: ColumnQualities defaultQualities = - ColumnNumeric | ColumnIntegral | ColumnIncreasing | ColumnNearEmpty; + ColumnNumeric | ColumnIntegral | ColumnSmall | + ColumnIncreasing | ColumnNearEmpty; for (int i = 0; i < cols; ++i) { - + while (m_columnQualities.size() <= i) { m_columnQualities.push_back(defaultQualities); m_prevValues.push_back(0.f); @@ -124,10 +152,15 @@ ColumnQualities qualities = m_columnQualities[i]; +// Looks like this is defined on Windows +#undef small + bool numeric = (qualities & ColumnNumeric); bool integral = (qualities & ColumnIntegral); bool increasing = (qualities & ColumnIncreasing); + bool small = (qualities & ColumnSmall); bool large = (qualities & ColumnLarge); // this one defaults to off + bool signd = (qualities & ColumnSigned); // also defaults to off bool emptyish = (qualities & ColumnNearEmpty); if (lineno > 1 && s.trimmed() != "") { @@ -144,9 +177,25 @@ value = (float)StringBits::stringToDoubleLocaleFree(s, &ok); } if (ok) { - if (lineno < 2 && value > 1000.f) large = true; + if (lineno < 2 && value > 1000.f) { + large = true; + } + if (value < 0.f) { + signd = true; + } + if (value < -1.f || value > 1.f) { + small = false; + } } else { numeric = false; + + // If the column is not numeric, it can't be any of + // these things either + integral = false; + increasing = false; + small = false; + large = false; + signd = false; } } @@ -166,12 +215,14 @@ m_prevValues[i] = value; } - + m_columnQualities[i] = (numeric ? ColumnNumeric : 0) | (integral ? ColumnIntegral : 0) | (increasing ? ColumnIncreasing : 0) | + (small ? ColumnSmall : 0) | (large ? ColumnLarge : 0) | + (signd ? ColumnSigned : 0) | (emptyish ? ColumnNearEmpty : 0); } @@ -182,11 +233,13 @@ } } -// cerr << "Estimated column qualities: "; -// for (int i = 0; i < m_columnCount; ++i) { -// cerr << int(m_columnQualities[i]) << " "; -// } -// cerr << endl; + if (lineno < 10) { + SVDEBUG << "Estimated column qualities for line " << lineno << " (reporting up to first 10): "; + for (int i = 0; i < m_columnCount; ++i) { + SVDEBUG << int(m_columnQualities[i]) << " "; + } + SVDEBUG << endl; + } } void @@ -194,8 +247,15 @@ { m_timingType = CSVFormat::ImplicitTiming; m_timeUnits = CSVFormat::TimeWindows; - + int timingColumnCount = 0; + bool haveDurationOrEndTime = false; + + SVDEBUG << "Estimated column qualities overall: "; + for (int i = 0; i < m_columnCount; ++i) { + SVDEBUG << int(m_columnQualities[i]) << " "; + } + SVDEBUG << endl; // if our first column has zero or one entries in it and the rest // have more, then we'll default to ignoring the first column and @@ -251,6 +311,7 @@ if (timingColumnCount == 2 && m_timingType == ExplicitTiming) { purpose = ColumnEndTime; + haveDurationOrEndTime = true; } } } @@ -294,15 +355,17 @@ if (m_columnQualities[timecol] & ColumnIncreasing) { // This shouldn't happen; should have been settled above m_columnPurposes[timecol] = ColumnEndTime; + haveDurationOrEndTime = true; } else { m_columnPurposes[timecol] = ColumnDuration; + haveDurationOrEndTime = true; } --valueCount; } } } - if (timingColumnCount > 1) { + if (timingColumnCount > 1 || haveDurationOrEndTime) { m_modelType = TwoDimensionalModelWithDuration; } else { if (valueCount == 0) { @@ -314,15 +377,83 @@ } } -// cerr << "Estimated column purposes: "; -// for (int i = 0; i < m_columnCount; ++i) { -// cerr << int(m_columnPurposes[i]) << " "; -// } -// cerr << endl; + SVDEBUG << "Estimated column purposes: "; + for (int i = 0; i < m_columnCount; ++i) { + SVDEBUG << int(m_columnPurposes[i]) << " "; + } + SVDEBUG << endl; -// cerr << "Estimated model type: " << m_modelType << endl; -// cerr << "Estimated timing type: " << m_timingType << endl; -// cerr << "Estimated units: " << m_timeUnits << endl; + SVDEBUG << "Estimated model type: " << m_modelType << endl; + SVDEBUG << "Estimated timing type: " << m_timingType << endl; + SVDEBUG << "Estimated units: " << m_timeUnits << endl; +} + +void +CSVFormat::guessAudioSampleRange() +{ + AudioSampleRange range = SampleRangeSigned1; + + range = SampleRangeSigned1; + bool knownSigned = false; + bool knownNonIntegral = false; + + SVDEBUG << "CSVFormat::guessAudioSampleRange: starting with assumption of " + << range << endl; + + for (int i = 0; i < m_columnCount; ++i) { + if (m_columnPurposes[i] != ColumnValue) { + SVDEBUG << "... column " << i + << " is not apparently a value, ignoring" << endl; + continue; + } + if (!(m_columnQualities[i] & ColumnIntegral)) { + knownNonIntegral = true; + if (range == SampleRangeUnsigned255 || + range == SampleRangeSigned32767) { + range = SampleRangeOther; + } + SVDEBUG << "... column " << i + << " is non-integral, updating range to " << range << endl; + } + if (m_columnQualities[i] & ColumnLarge) { + if (range == SampleRangeSigned1 || + range == SampleRangeUnsigned255) { + if (knownNonIntegral) { + range = SampleRangeOther; + } else { + range = SampleRangeSigned32767; + } + } + SVDEBUG << "... column " << i << " is large, updating range to " + << range << endl; + } + if (m_columnQualities[i] & ColumnSigned) { + knownSigned = true; + if (range == SampleRangeUnsigned255) { + range = SampleRangeSigned32767; + } + SVDEBUG << "... column " << i << " is signed, updating range to " + << range << endl; + } + if (!(m_columnQualities[i] & ColumnSmall)) { + if (range == SampleRangeSigned1) { + if (knownNonIntegral) { + range = SampleRangeOther; + } else if (knownSigned) { + range = SampleRangeSigned32767; + } else { + range = SampleRangeUnsigned255; + } + } + SVDEBUG << "... column " << i << " is not small, updating range to " + << range << endl; + } + } + + SVDEBUG << "CSVFormat::guessAudioSampleRange: ended up with range " + << range << endl; + + m_audioSampleRange = range; } CSVFormat::ColumnPurpose diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVFormat.h --- a/data/fileio/CSVFormat.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CSVFormat.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _CSV_FORMAT_H_ -#define _CSV_FORMAT_H_ +#ifndef SV_CSV_FORMAT_H +#define SV_CSV_FORMAT_H #include #include @@ -25,23 +25,24 @@ { public: enum ModelType { - OneDimensionalModel, - TwoDimensionalModel, + OneDimensionalModel, + TwoDimensionalModel, TwoDimensionalModelWithDuration, TwoDimensionalModelWithDurationAndPitch, - ThreeDimensionalModel + ThreeDimensionalModel, + WaveFileModel }; enum TimingType { - ExplicitTiming, - ImplicitTiming + ExplicitTiming, + ImplicitTiming }; enum TimeUnits { - TimeSeconds, + TimeSeconds, TimeMilliseconds, - TimeAudioFrames, - TimeWindows, + TimeAudioFrames, + TimeWindows, }; enum ColumnPurpose { @@ -55,14 +56,23 @@ }; enum ColumnQuality { - ColumnNumeric = 1, - ColumnIntegral = 2, - ColumnIncreasing = 4, - ColumnLarge = 8, - ColumnNearEmpty = 16, + ColumnNumeric = 1, // No non-numeric values were seen in sample + ColumnIntegral = 2, // All sampled values were integers + ColumnIncreasing = 4, // Sampled values were monotonically increasing + ColumnSmall = 8, // All sampled values had magnitude < 1 + ColumnLarge = 16, // Values "quickly" grew to over 1000 + ColumnSigned = 32, // Some negative values were seen + ColumnNearEmpty = 64, // Nothing in this column beyond first row }; typedef unsigned int ColumnQualities; + enum AudioSampleRange { + SampleRangeSigned1 = 0, // -1 .. 1 + SampleRangeUnsigned255, // 0 .. 255 + SampleRangeSigned32767, // -32768 .. 32767 + SampleRangeOther // Other/unknown: Normalise on load + }; + CSVFormat() : // arbitrary defaults m_modelType(TwoDimensionalModel), m_timingType(ExplicitTiming), @@ -72,6 +82,7 @@ m_windowSize(1024), m_columnCount(0), m_variableColumnCount(false), + m_audioSampleRange(SampleRangeOther), m_allowQuoting(true), m_maxExampleCols(0) { } @@ -84,8 +95,21 @@ * string, the separator character will also be guessed; otherwise * the current separator will be used. The other properties of * this object will be set according to guesses from the file. + * + * The properties that are guessed from the file contents are: + * separator, column count, variable-column-count flag, audio + * sample range, timing type, time units, column qualities, column + * purposes, and model type. The sample rate and window size + * cannot be guessed and will not be changed by this function. + * Note also that this function will never guess WaveFileModel for + * the model type. + * + * Return false if there is some fundamental error, e.g. the file + * could not be opened at all. Return true otherwise. Note that + * this function returns true even if the file doesn't appear to + * make much sense as a data format. */ - void guessFormatFor(QString path); + bool guessFormatFor(QString path); ModelType getModelType() const { return m_modelType; } TimingType getTimingType() const { return m_timingType; } @@ -93,6 +117,7 @@ sv_samplerate_t getSampleRate() const { return m_sampleRate; } int getWindowSize() const { return m_windowSize; } int getColumnCount() const { return m_columnCount; } + AudioSampleRange getAudioSampleRange() const { return m_audioSampleRange; } bool getAllowQuoting() const { return m_allowQuoting; } QChar getSeparator() const { if (m_separator == "") return ' '; @@ -106,6 +131,7 @@ void setSampleRate(sv_samplerate_t r) { m_sampleRate = r; } void setWindowSize(int s) { m_windowSize = s; } void setColumnCount(int c) { m_columnCount = c; } + void setAudioSampleRange(AudioSampleRange r) { m_audioSampleRange = r; } void setAllowQuoting(bool q) { m_allowQuoting = q; } QList getColumnPurposes() const { return m_columnPurposes; } @@ -116,12 +142,17 @@ void setColumnPurpose(int i, ColumnPurpose p); // read-only; only valid if format has been guessed: - QList getColumnQualities() const { return m_columnQualities; } + const QList &getColumnQualities() const { + return m_columnQualities; + } // read-only; only valid if format has been guessed: - QList getExample() const { return m_example; } + const QList &getExample() const { + return m_example; + } + int getMaxExampleCols() const { return m_maxExampleCols; } - + protected: ModelType m_modelType; TimingType m_timingType; @@ -136,6 +167,8 @@ QList m_columnQualities; QList m_columnPurposes; + AudioSampleRange m_audioSampleRange; + QList m_prevValues; bool m_allowQuoting; @@ -146,9 +179,7 @@ void guessSeparator(QString line); void guessQualities(QString line, int lineno); void guessPurposes(); - - void guessFormatFor_Old(QString path); - + void guessAudioSampleRange(); }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CSVStreamWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/CSVStreamWriter.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,150 @@ +/* -*- 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 2017 Queen Mary, University of London. + + 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 SV_CSV_STREAM_WRITER_H +#define SV_CSV_STREAM_WRITER_H + +#include "base/BaseTypes.h" +#include "base/Selection.h" +#include "base/ProgressReporter.h" +#include "base/DataExportOptions.h" +#include "data/model/Model.h" +#include +#include +#include + +namespace CSVStreamWriter +{ + +template +bool +writeInChunks(OutStream& oss, + const Model& model, + const MultiSelection& regions, + ProgressReporter* reporter = nullptr, + QString delimiter = ",", + DataExportOptions options = DataExportDefaults, + const sv_frame_t blockSize = 16384) +{ + const auto selections = regions.getSelections(); + if (blockSize <= 0 || selections.empty()) return false; + + // TODO, some form of checking validity of selections? + const auto nFramesToWrite = std::accumulate( + selections.begin(), + selections.end(), + 0, + [](sv_frame_t acc, const Selection& current) -> sv_frame_t { + return acc + (current.getEndFrame() - current.getStartFrame()); + } + ); + const auto finalFrameOfLastRegion = (*selections.crbegin()).getEndFrame(); + + const auto wasCancelled = [&reporter]() { + return reporter && reporter->wasCancelled(); + }; + + sv_frame_t nFramesWritten = 0; + int previousProgress = 0; + + for (const auto& extents : selections) { + const auto startFrame = extents.getStartFrame(); + const auto endFrame = extents.getEndFrame(); + auto readPtr = startFrame; + while (readPtr < endFrame) { + if (wasCancelled()) return false; + + const auto start = readPtr; + const auto end = std::min(start + blockSize, endFrame); + const auto data = model.toDelimitedDataStringSubsetWithOptions( + delimiter, + options, + start, + end + ).trimmed(); + + if ( data != "" ) { + oss << data << (end < finalFrameOfLastRegion ? "\n" : ""); + } + + nFramesWritten += end - start; + const int currentProgress = + int(100 * nFramesWritten / nFramesToWrite); + const bool hasIncreased = currentProgress > previousProgress; + if (hasIncreased) { + if (reporter) reporter->setProgress(currentProgress); + previousProgress = currentProgress; + } + readPtr = end; + } + } + return !wasCancelled(); // setProgress could process event loop +} + +template +bool +writeInChunks(OutStream& oss, + const Model& model, + const Selection& extents, + ProgressReporter* reporter = nullptr, + QString delimiter = ",", + DataExportOptions options = DataExportDefaults, + const sv_frame_t blockSize = 16384) +{ + const auto startFrame = extents.isEmpty() ? + model.getStartFrame() : extents.getStartFrame(); + const auto endFrame = extents.isEmpty() ? + model.getEndFrame() : extents.getEndFrame(); + const auto hasValidExtents = startFrame >= 0 && endFrame > startFrame; + if (!hasValidExtents) return false; + Selection all { + startFrame, + endFrame + }; + MultiSelection regions; + regions.addSelection(all); + return CSVStreamWriter::writeInChunks( + oss, + model, + regions, + reporter, + delimiter, + options, + blockSize + ); +} + +template +bool +writeInChunks(OutStream& oss, + const Model& model, + ProgressReporter* reporter = nullptr, + QString delimiter = ",", + DataExportOptions options = DataExportDefaults, + const sv_frame_t blockSize = 16384) +{ + const Selection empty; + return CSVStreamWriter::writeInChunks( + oss, + model, + empty, + reporter, + delimiter, + options, + blockSize + ); +} +} // namespace CSVStreamWriter +#endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CachedFile.cpp --- a/data/fileio/CachedFile.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CachedFile.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -157,7 +157,7 @@ SVDEBUG << "CachedFile::check: Retrieval succeeded" << endl; updateLastRetrieval(true); } else { - cerr << "CachedFile::check: Retrieval failed, will try again later (using existing file for now)" << endl; + SVCERR << "CachedFile::check: Retrieval failed, will try again later (using existing file for now)" << endl; } } } else { @@ -168,7 +168,7 @@ m_ok = true; updateLastRetrieval(true); } else { - cerr << "CachedFile::check: Retrieval failed, remaining in invalid state" << endl; + SVCERR << "CachedFile::check: Retrieval failed, remaining in invalid state" << endl; // again, we don't need to do anything here -- the last // retrieval timestamp is already invalid } @@ -212,7 +212,7 @@ QFile previous(m_localFilename); if (previous.exists()) { if (!previous.remove()) { - cerr << "CachedFile::retrieve: ERROR: Failed to remove previous copy of cached file at \"" << m_localFilename << "\"" << endl; + SVCERR << "CachedFile::retrieve: ERROR: Failed to remove previous copy of cached file at \"" << m_localFilename << "\"" << endl; return false; } } @@ -222,7 +222,7 @@ //!!! disk space left) if (!tempFile.copy(m_localFilename)) { - cerr << "CachedFile::retrieve: ERROR: Failed to copy newly retrieved file from \"" << tempName << "\" to \"" << m_localFilename << "\"" << endl; + SVCERR << "CachedFile::retrieve: ERROR: Failed to copy newly retrieved file from \"" << tempName << "\" to \"" << m_localFilename << "\"" << endl; return false; } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CodedAudioFileReader.cpp --- a/data/fileio/CodedAudioFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CodedAudioFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -136,7 +136,7 @@ if (m_fileRate == 0) { SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << endl; - throw FileOperationFailed("(coded file)", "File sample rate unknown (bug in subclass implementation?)"); + throw FileOperationFailed("(coded file)", "sample rate unknown (bug in subclass implementation?)"); } if (m_sampleRate == 0) { m_sampleRate = m_fileRate; @@ -144,10 +144,13 @@ } if (m_fileRate != m_sampleRate) { SVDEBUG << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << endl; - m_resampler = new breakfastquay::Resampler - (breakfastquay::Resampler::FastestTolerable, - m_channelCount, - int(m_cacheWriteBufferFrames)); + + breakfastquay::Resampler::Parameters params; + params.quality = breakfastquay::Resampler::FastestTolerable; + params.maxBufferSize = int(m_cacheWriteBufferFrames); + params.initialSampleRate = m_fileRate; + m_resampler = new breakfastquay::Resampler(params, m_channelCount); + double ratio = m_sampleRate / m_fileRate; m_resampleBufferFrames = int(ceil(double(m_cacheWriteBufferFrames) * ratio + 1)); @@ -161,7 +164,7 @@ try { QDir dir(TempDirectory::getInstance()->getPath()); - m_cacheFileName = dir.filePath(QString("decoded_%1.wav") + m_cacheFileName = dir.filePath(QString("decoded_%1.w64") .arg((intptr_t)this)); SF_INFO fileInfo; @@ -193,10 +196,15 @@ // tests.) // // So: now we write floats. - fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; - - m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(), - SFM_WRITE, &fileInfo); + fileInfo.format = SF_FORMAT_W64 | SF_FORMAT_FLOAT; + +#ifdef Q_OS_WIN + m_cacheFileWritePtr = sf_wchar_open + ((LPCWSTR)m_cacheFileName.utf16(), SFM_WRITE, &fileInfo); +#else + m_cacheFileWritePtr = sf_open + (m_cacheFileName.toLocal8Bit(), SFM_WRITE, &fileInfo); +#endif if (m_cacheFileWritePtr) { @@ -220,7 +228,7 @@ m_cacheMode = CacheInMemory; } - } catch (DirectoryCreationFailed f) { + } catch (const DirectoryCreationFailed &f) { SVDEBUG << "CodedAudioFileReader::initialiseDecodeCache: failed to create temporary directory! Falling back to in-memory cache" << endl; m_cacheMode = CacheInMemory; } @@ -289,7 +297,7 @@ } void -CodedAudioFileReader::addSamplesToDecodeCache(const vector &samples) +CodedAudioFileReader::addSamplesToDecodeCache(const floatvec_t &samples) { QMutexLocker locker(&m_cacheMutex); @@ -470,7 +478,14 @@ case CacheInMemory: m_dataLock.lock(); - m_data.insert(m_data.end(), buffer, buffer + count); + try { + m_data.insert(m_data.end(), buffer, buffer + count); + } catch (const std::bad_alloc &e) { + m_data.clear(); + SVCERR << "CodedAudioFileReader: Caught bad_alloc when trying to add " << count << " elements to buffer" << endl; + m_dataLock.unlock(); + throw e; + } m_dataLock.unlock(); break; } @@ -517,8 +532,12 @@ ratio, true); - if (m_frameCount + out > sv_frame_t(double(m_fileFrameCount) * ratio)) { - out = sv_frame_t(double(m_fileFrameCount) * ratio) - m_frameCount; + SVDEBUG << "CodedAudioFileReader::pushBufferResampling: resampled padFrames to " << out << " frames" << endl; + + sv_frame_t expected = sv_frame_t(round(double(m_fileFrameCount) * ratio)); + if (m_frameCount + out > expected) { + out = expected - m_frameCount; + SVDEBUG << "CodedAudioFileReader::pushBufferResampling: clipping that to " << out << " to avoid producing more samples than desired" << endl; } pushBufferNonResampling(m_resampleBuffer, out); @@ -526,7 +545,7 @@ } } -vector +floatvec_t CodedAudioFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const { // Lock is only required in CacheInMemory mode (the cache file @@ -538,7 +557,7 @@ return {}; } - vector frames; + floatvec_t frames; switch (m_cacheMode) { @@ -564,7 +583,7 @@ sv_frame_t n = sv_frame_t(m_data.size()); if (ix0 > n) ix0 = n; if (ix1 > n) ix1 = n; - frames = vector(m_data.begin() + ix0, m_data.begin() + ix1); + frames = floatvec_t(m_data.begin() + ix0, m_data.begin() + ix1); m_dataLock.unlock(); break; } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CodedAudioFileReader.h --- a/data/fileio/CodedAudioFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CodedAudioFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,15 +13,21 @@ COPYING included with this distribution for more information. */ -#ifndef _CODED_AUDIO_FILE_READER_H_ -#define _CODED_AUDIO_FILE_READER_H_ +#ifndef SV_CODED_AUDIO_FILE_READER_H +#define SV_CODED_AUDIO_FILE_READER_H #include "AudioFileReader.h" -#include #include #include +#ifdef Q_OS_WIN +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif + +#include + class WavFileReader; class Serialiser; @@ -46,7 +52,7 @@ DecodeThreaded // decode in a background thread after construction }; - virtual std::vector getInterleavedFrames(sv_frame_t start, sv_frame_t count) const; + virtual floatvec_t getInterleavedFrames(sv_frame_t start, sv_frame_t count) const; virtual sv_samplerate_t getNativeRate() const { return m_fileRate; } @@ -71,7 +77,7 @@ // may throw InsufficientDiscSpace: void addSamplesToDecodeCache(float **samples, sv_frame_t nframes); void addSamplesToDecodeCache(float *samplesInterleaved, sv_frame_t nframes); - void addSamplesToDecodeCache(const std::vector &interleaved); + void addSamplesToDecodeCache(const floatvec_t &interleaved); // may throw InsufficientDiscSpace: void finishDecodeCache(); @@ -95,7 +101,7 @@ protected: QMutex m_cacheMutex; CacheMode m_cacheMode; - std::vector m_data; + floatvec_t m_data; mutable QMutex m_dataLock; bool m_initialised; Serialiser *m_serialiser; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/CoreAudioFileReader.cpp --- a/data/fileio/CoreAudioFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/CoreAudioFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -114,14 +114,14 @@ UInt32 propsize = sizeof(AudioStreamBasicDescription); m_d->err = ExtAudioFileGetProperty - (m_d->file, kExtAudioFileProperty_FileDataFormat, &propsize, &m_d->asbd); + (m_d->file, kExtAudioFileProperty_FileDataFormat, &propsize, &m_d->asbd); if (m_d->err) { m_error = "CoreAudioReadStream: Error in getting basic description: code " + codestr(m_d->err); ExtAudioFileDispose(m_d->file); return; } - + m_channelCount = m_d->asbd.mChannelsPerFrame; m_fileRate = m_d->asbd.mSampleRate; @@ -137,9 +137,9 @@ m_d->asbd.mBytesPerPacket = sizeof(float) * m_channelCount; m_d->asbd.mFramesPerPacket = 1; m_d->asbd.mReserved = 0; - + m_d->err = ExtAudioFileSetProperty - (m_d->file, kExtAudioFileProperty_ClientDataFormat, propsize, &m_d->asbd); + (m_d->file, kExtAudioFileProperty_ClientDataFormat, propsize, &m_d->asbd); if (m_d->err) { m_error = "CoreAudioReadStream: Error in setting client format: code " + codestr(m_d->err); diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/DataFileReaderFactory.cpp --- a/data/fileio/DataFileReaderFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/DataFileReaderFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -32,21 +32,28 @@ bool csv, MIDIFileImportPreferenceAcquirer *acquirer, CSVFormat format, - sv_samplerate_t mainModelSampleRate) + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) { QString err; DataFileReader *reader = 0; if (!csv) { - reader = new MIDIFileReader(path, acquirer, mainModelSampleRate); + reader = new MIDIFileReader(path, + acquirer, + mainModelSampleRate, + reporter); if (reader->isOK()) return reader; if (reader->getError() != "") err = reader->getError(); delete reader; } if (csv) { - reader = new CSVFileReader(path, format, mainModelSampleRate); + reader = new CSVFileReader(path, + format, + mainModelSampleRate, + reporter); if (reader->isOK()) return reader; if (reader->getError() != "") err = reader->getError(); delete reader; @@ -58,14 +65,15 @@ DataFileReader * DataFileReaderFactory::createReader(QString path, MIDIFileImportPreferenceAcquirer *acquirer, - sv_samplerate_t mainModelSampleRate) + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) { DataFileReader *reader = createReader - (path, false, acquirer, CSVFormat(), mainModelSampleRate); + (path, false, acquirer, CSVFormat(), mainModelSampleRate, reporter); if (reader) return reader; reader = createReader - (path, true, acquirer, CSVFormat(path), mainModelSampleRate); + (path, true, acquirer, CSVFormat(path), mainModelSampleRate, reporter); if (reader) return reader; return 0; @@ -74,11 +82,13 @@ Model * DataFileReaderFactory::load(QString path, MIDIFileImportPreferenceAcquirer *acquirer, - sv_samplerate_t mainModelSampleRate) + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) { DataFileReader *reader = createReader(path, acquirer, - mainModelSampleRate); + mainModelSampleRate, + reporter); if (!reader) return NULL; try { @@ -94,12 +104,14 @@ Model * DataFileReaderFactory::loadNonCSV(QString path, MIDIFileImportPreferenceAcquirer *acquirer, - sv_samplerate_t mainModelSampleRate) + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) { DataFileReader *reader = createReader(path, false, acquirer, CSVFormat(), - mainModelSampleRate); + mainModelSampleRate, + reporter); if (!reader) return NULL; try { @@ -114,10 +126,12 @@ Model * DataFileReaderFactory::loadCSV(QString path, CSVFormat format, - sv_samplerate_t mainModelSampleRate) + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter) { DataFileReader *reader = createReader(path, true, 0, format, - mainModelSampleRate); + mainModelSampleRate, + reporter); if (!reader) return NULL; try { diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/DataFileReaderFactory.h --- a/data/fileio/DataFileReaderFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/DataFileReaderFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _DATA_FILE_READER_FACTORY_H_ -#define _DATA_FILE_READER_FACTORY_H_ +#ifndef SV_DATA_FILE_READER_FACTORY_H +#define SV_DATA_FILE_READER_FACTORY_H #include @@ -23,6 +23,7 @@ class DataFileReader; class Model; +class ProgressReporter; class DataFileReaderFactory { @@ -48,7 +49,8 @@ */ static DataFileReader *createReader(QString path, MIDIFileImportPreferenceAcquirer *, - sv_samplerate_t mainModelSampleRate); + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); /** * Read the given path, if a suitable reader is available. @@ -60,7 +62,8 @@ */ static Model *load(QString path, MIDIFileImportPreferenceAcquirer *acquirer, - sv_samplerate_t mainModelSampleRate); + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); /** * Read the given path, if a suitable reader is available. @@ -69,7 +72,8 @@ */ static Model *loadNonCSV(QString path, MIDIFileImportPreferenceAcquirer *acquirer, - sv_samplerate_t mainModelSampleRate); + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); /** * Read the given path using the CSV reader with the given format. @@ -77,13 +81,15 @@ */ static Model *loadCSV(QString path, CSVFormat format, - sv_samplerate_t mainModelSampleRate); + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); protected: static DataFileReader *createReader(QString path, bool csv, MIDIFileImportPreferenceAcquirer *, CSVFormat format, - sv_samplerate_t mainModelSampleRate); + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/DecodingWavFileReader.cpp --- a/data/fileio/DecodingWavFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/DecodingWavFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -71,7 +71,7 @@ sv_frame_t blockSize = 16384; sv_frame_t total = m_original->getFrameCount(); - vector block; + floatvec_t block; for (sv_frame_t i = 0; i < total; i += blockSize) { @@ -128,7 +128,7 @@ sv_frame_t blockSize = 16384; sv_frame_t total = m_reader->m_original->getFrameCount(); - vector block; + floatvec_t block; for (sv_frame_t i = 0; i < total; i += blockSize) { @@ -151,7 +151,7 @@ } void -DecodingWavFileReader::addBlock(const vector &frames) +DecodingWavFileReader::addBlock(const floatvec_t &frames) { addSamplesToDecodeCache(frames); diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/DecodingWavFileReader.h --- a/data/fileio/DecodingWavFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/DecodingWavFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -64,7 +64,7 @@ WavFileReader *m_original; ProgressReporter *m_reporter; - void addBlock(const std::vector &frames); + void addBlock(const floatvec_t &frames); class DecodeThread : public Thread { diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/FileFinder.h --- a/data/fileio/FileFinder.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/FileFinder.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _FILE_FINDER_H_ -#define _FILE_FINDER_H_ +#ifndef SV_FILE_FINDER_H +#define SV_FILE_FINDER_H #include @@ -28,6 +28,7 @@ LayerFileNoMidi, SessionOrAudioFile, ImageFile, + SVGFile, AnyFile, CSVFile, LayerFileNonSV, diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/FileSource.cpp --- a/data/fileio/FileSource.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/FileSource.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -884,7 +884,7 @@ QDir dir; try { dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); - } catch (DirectoryCreationFailed f) { + } catch (const DirectoryCreationFailed &f) { #ifdef DEBUG_FILE_SOURCE cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << endl; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/MIDIFileReader.cpp --- a/data/fileio/MIDIFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/MIDIFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -53,12 +53,13 @@ using namespace MIDIConstants; -//#define MIDI_SVDEBUG 1 +//#define MIDI_DEBUG 1 MIDIFileReader::MIDIFileReader(QString path, MIDIFileImportPreferenceAcquirer *acquirer, - sv_samplerate_t mainModelSampleRate) : + sv_samplerate_t mainModelSampleRate, + ProgressReporter *) : // we don't actually report progress m_smpte(false), m_timingDivision(0), m_fps(0), @@ -74,21 +75,21 @@ m_acquirer(acquirer) { if (parseFile()) { - m_error = ""; + m_error = ""; } } MIDIFileReader::~MIDIFileReader() { for (MIDIComposition::iterator i = m_midiComposition.begin(); - i != m_midiComposition.end(); ++i) { - - for (MIDITrack::iterator j = i->second.begin(); - j != i->second.end(); ++j) { - delete *j; - } + i != m_midiComposition.end(); ++i) { + + for (MIDITrack::iterator j = i->second.begin(); + j != i->second.end(); ++j) { + delete *j; + } - i->second.clear(); + i->second.clear(); } m_midiComposition.clear(); @@ -110,7 +111,7 @@ MIDIFileReader::midiBytesToLong(const string& bytes) { if (bytes.length() != 4) { - throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4)); + throw MIDIException(tr("Wrong length for long data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(4)); } long longRet = ((long)(((MIDIByte)bytes[0]) << 24)) | @@ -125,7 +126,7 @@ MIDIFileReader::midiBytesToInt(const string& bytes) { if (bytes.length() != 2) { - throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2)); + throw MIDIException(tr("Wrong length for int data in MIDI stream (%1, should be %2)").arg(bytes.length()).arg(2)); } int intRet = ((int)(((MIDIByte)bytes[0]) << 8)) | @@ -142,7 +143,7 @@ MIDIFileReader::getMIDIByte() { if (!m_midiFile) { - throw MIDIException(tr("getMIDIByte called but no MIDI file open")); + throw MIDIException(tr("getMIDIByte called but no MIDI file open")); } if (m_midiFile->eof()) { @@ -155,8 +156,8 @@ char byte; if (m_midiFile->read(&byte, 1)) { - --m_trackByteCount; - return (MIDIByte)byte; + --m_trackByteCount; + return (MIDIByte)byte; } throw MIDIException(tr("Attempt to read past MIDI file end")); @@ -171,7 +172,7 @@ MIDIFileReader::getMIDIBytes(unsigned long numberOfBytes) { if (!m_midiFile) { - throw MIDIException(tr("getMIDIBytes called but no MIDI file open")); + throw MIDIException(tr("getMIDIBytes called but no MIDI file open")); } if (m_midiFile->eof()) { @@ -212,27 +213,27 @@ MIDIFileReader::getNumberFromMIDIBytes(int firstByte) { if (!m_midiFile) { - throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open")); + throw MIDIException(tr("getNumberFromMIDIBytes called but no MIDI file open")); } long longRet = 0; MIDIByte midiByte; if (firstByte >= 0) { - midiByte = (MIDIByte)firstByte; + midiByte = (MIDIByte)firstByte; } else if (m_midiFile->eof()) { - return longRet; + return longRet; } else { - midiByte = getMIDIByte(); + midiByte = getMIDIByte(); } longRet = midiByte; if (midiByte & 0x80) { - longRet &= 0x7F; - do { - midiByte = getMIDIByte(); - longRet = (longRet << 7) + (midiByte & 0x7F); - } while (!m_midiFile->eof() && (midiByte & 0x80)); + longRet &= 0x7F; + do { + midiByte = getMIDIByte(); + longRet = (longRet << 7) + (midiByte & 0x7F); + } while (!m_midiFile->eof() && (midiByte & 0x80)); } return longRet; @@ -246,7 +247,7 @@ MIDIFileReader::skipToNextTrack() { if (!m_midiFile) { - throw MIDIException(tr("skipToNextTrack called but no MIDI file open")); + throw MIDIException(tr("skipToNextTrack called but no MIDI file open")); } string buffer, buffer2; @@ -255,10 +256,10 @@ while (!m_midiFile->eof() && (m_decrementCount == false)) { buffer = getMIDIBytes(4); - if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) { - m_trackByteCount = midiBytesToLong(getMIDIBytes(4)); - m_decrementCount = true; - } + if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0) { + m_trackByteCount = midiBytesToLong(getMIDIBytes(4)); + m_decrementCount = true; + } } if (m_trackByteCount == -1) { // we haven't found a track @@ -284,77 +285,77 @@ // Open the file m_midiFile = new ifstream(m_path.toLocal8Bit().data(), - ios::in | ios::binary); + ios::in | ios::binary); if (!*m_midiFile) { - m_error = "File not found or not readable."; - m_format = MIDI_FILE_BAD_FORMAT; - delete m_midiFile; + m_error = "File not found or not readable."; + m_format = MIDI_FILE_BAD_FORMAT; + delete m_midiFile; m_midiFile = 0; - return false; + return false; } bool retval = false; try { - // Set file size so we can count it off - // - m_midiFile->seekg(0, ios::end); + // Set file size so we can count it off + // + m_midiFile->seekg(0, ios::end); std::streamoff off = m_midiFile->tellg(); - m_fileSize = 0; + m_fileSize = 0; if (off > 0) m_fileSize = off; - m_midiFile->seekg(0, ios::beg); + m_midiFile->seekg(0, ios::beg); - // Parse the MIDI header first. The first 14 bytes of the file. - if (!parseHeader(getMIDIBytes(14))) { - m_format = MIDI_FILE_BAD_FORMAT; - m_error = "Not a MIDI file."; - goto done; - } + // Parse the MIDI header first. The first 14 bytes of the file. + if (!parseHeader(getMIDIBytes(14))) { + m_format = MIDI_FILE_BAD_FORMAT; + m_error = "Not a MIDI file."; + goto done; + } - unsigned int i = 0; + unsigned int i = 0; - for (unsigned int j = 0; j < m_numberOfTracks; ++j) { + for (unsigned int j = 0; j < m_numberOfTracks; ++j) { #ifdef MIDI_DEBUG - SVDEBUG << "Parsing Track " << j << endl; + SVDEBUG << "Parsing Track " << j << endl; #endif - if (!skipToNextTrack()) { + if (!skipToNextTrack()) { #ifdef MIDI_DEBUG - cerr << "Couldn't find Track " << j << endl; + SVDEBUG << "Couldn't find Track " << j << endl; #endif - m_error = "File corrupted or in non-standard format?"; - m_format = MIDI_FILE_BAD_FORMAT; - goto done; - } + m_error = "File corrupted or in non-standard format?"; + m_format = MIDI_FILE_BAD_FORMAT; + goto done; + } #ifdef MIDI_DEBUG - cerr << "Track has " << m_trackByteCount << " bytes" << endl; + SVDEBUG << "Track has " << m_trackByteCount << " bytes" << endl; #endif - // Run through the events taking them into our internal - // representation. - if (!parseTrack(i)) { + // Run through the events taking them into our internal + // representation. + if (!parseTrack(i)) { #ifdef MIDI_DEBUG - cerr << "Track " << j << " parsing failed" << endl; + SVDEBUG << "Track " << j << " parsing failed" << endl; #endif - m_error = "File corrupted or in non-standard format?"; - m_format = MIDI_FILE_BAD_FORMAT; - goto done; - } + m_error = "File corrupted or in non-standard format?"; + m_format = MIDI_FILE_BAD_FORMAT; + goto done; + } - ++i; // j is the source track number, i the destination - } - - m_numberOfTracks = i; - retval = true; + ++i; // j is the source track number, i the destination + } + + m_numberOfTracks = i; + retval = true; - } catch (MIDIException e) { + } catch (const MIDIException &e) { SVDEBUG << "MIDIFileReader::open() - caught exception - " << e.what() << endl; - m_error = e.what(); + m_error = e.what(); } done: @@ -367,7 +368,7 @@ // start. The addTime method returns the sum of the current // MIDI Event delta time plus the argument. - unsigned long acc = 0; + unsigned long acc = 0; for (MIDITrack::iterator i = m_midiComposition[track].begin(); i != m_midiComposition[track].end(); ++i) { @@ -375,8 +376,8 @@ } if (consolidateNoteOffEvents(track)) { // returns true if some notes exist - m_loadableTracks.insert(track); - } + m_loadableTracks.insert(track); + } } for (unsigned int track = 0; track < m_numberOfTracks; ++track) { @@ -402,18 +403,18 @@ if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0) { #ifdef MIDI_DEBUG - SVDEBUG << "MIDIFileReader::parseHeader()" - << "- file header not found or malformed" - << endl; + SVDEBUG << "MIDIFileReader::parseHeader()" + << "- file header not found or malformed" + << endl; #endif - return false; + return false; } if (midiBytesToLong(midiHeader.substr(4,4)) != 6L) { #ifdef MIDI_DEBUG SVDEBUG << "MIDIFileReader::parseHeader()" - << " - header length incorrect" - << endl; + << " - header length incorrect" + << endl; #endif return false; } @@ -474,18 +475,18 @@ while (!m_midiFile->eof() && (m_trackByteCount > 0)) { - if (eventCode < 0x80) { + if (eventCode < 0x80) { #ifdef MIDI_DEBUG - cerr << "WARNING: Invalid event code " << eventCode - << " in MIDI file" << endl; + SVDEBUG << "WARNING: Invalid event code " << eventCode + << " in MIDI file" << endl; #endif - throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode))); - } + throw MIDIException(tr("Invalid event code %1 found").arg(int(eventCode))); + } deltaTime = getNumberFromMIDIBytes(); #ifdef MIDI_DEBUG - cerr << "read delta time " << deltaTime << endl; + SVDEBUG << "read delta time " << deltaTime << endl; #endif // Get a single byte @@ -493,72 +494,72 @@ if (!(midiByte & MIDI_STATUS_BYTE_MASK)) { - if (runningStatus < 0) { - throw MIDIException(tr("Running status used for first event in track")); - } + if (runningStatus < 0) { + throw MIDIException(tr("Running status used for first event in track")); + } - eventCode = (MIDIByte)runningStatus; - data1 = midiByte; + eventCode = (MIDIByte)runningStatus; + data1 = midiByte; #ifdef MIDI_DEBUG - SVDEBUG << "using running status (byte " << int(midiByte) << " found)" << endl; + SVDEBUG << "using running status (byte " << int(midiByte) << " found)" << endl; #endif } else { #ifdef MIDI_DEBUG - cerr << "have new event code " << int(midiByte) << endl; + SVDEBUG << "have new event code " << int(midiByte) << endl; #endif eventCode = midiByte; - data1 = getMIDIByte(); - } + data1 = getMIDIByte(); + } if (eventCode == MIDI_FILE_META_EVENT) { - metaEventCode = data1; + metaEventCode = data1; messageLength = getNumberFromMIDIBytes(); //#ifdef MIDI_DEBUG - cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl; + SVDEBUG << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found, putting on track " << metaTrack << endl; //#endif metaMessage = getMIDIBytes(messageLength); - long gap = accumulatedTime - trackTimeMap[metaTrack]; - accumulatedTime += deltaTime; - deltaTime += gap; - trackTimeMap[metaTrack] = accumulatedTime; + long gap = accumulatedTime - trackTimeMap[metaTrack]; + accumulatedTime += deltaTime; + deltaTime += gap; + trackTimeMap[metaTrack] = accumulatedTime; MIDIEvent *e = new MIDIEvent(deltaTime, MIDI_FILE_META_EVENT, metaEventCode, metaMessage); - m_midiComposition[metaTrack].push_back(e); + m_midiComposition[metaTrack].push_back(e); - if (metaEventCode == MIDI_TRACK_NAME) { - m_trackNames[metaTrack] = metaMessage.c_str(); - } + if (metaEventCode == MIDI_TRACK_NAME) { + m_trackNames[metaTrack] = metaMessage.c_str(); + } } else { // non-meta events - runningStatus = eventCode; + runningStatus = eventCode; MIDIEvent *midiEvent; - int channel = (eventCode & MIDI_CHANNEL_NUM_MASK); - if (channelTrackMap[channel] == -1) { - if (!firstTrack) ++lastTrackNum; - else firstTrack = false; - channelTrackMap[channel] = lastTrackNum; - } + int channel = (eventCode & MIDI_CHANNEL_NUM_MASK); + if (channelTrackMap[channel] == -1) { + if (!firstTrack) ++lastTrackNum; + else firstTrack = false; + channelTrackMap[channel] = lastTrackNum; + } - unsigned int trackNum = channelTrackMap[channel]; - - // accumulatedTime is abs time of last event on any track; - // trackTimeMap[trackNum] is that of last event on this track - - long gap = accumulatedTime - trackTimeMap[trackNum]; - accumulatedTime += deltaTime; - deltaTime += gap; - trackTimeMap[trackNum] = accumulatedTime; + unsigned int trackNum = channelTrackMap[channel]; + + // accumulatedTime is abs time of last event on any track; + // trackTimeMap[trackNum] is that of last event on this track + + long gap = accumulatedTime - trackTimeMap[trackNum]; + accumulatedTime += deltaTime; + deltaTime += gap; + trackTimeMap[trackNum] = accumulatedTime; switch (eventCode & MIDI_MESSAGE_TYPE_MASK) { @@ -572,17 +573,17 @@ midiEvent = new MIDIEvent(deltaTime, eventCode, data1, data2); /* - cerr << "MIDI event for channel " << channel << " (track " - << trackNum << ")" << endl; - midiEvent->print(); + SVDEBUG << "MIDI event for channel " << channel << " (track " + << trackNum << ")" << endl; + midiEvent->print(); */ m_midiComposition[trackNum].push_back(midiEvent); - if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) { - m_percussionTracks.insert(trackNum); - } + if (midiEvent->getChannelNumber() == MIDI_PERCUSSION_CHANNEL) { + m_percussionTracks.insert(trackNum); + } break; @@ -605,7 +606,7 @@ messageLength = getNumberFromMIDIBytes(data1); #ifdef MIDI_DEBUG - cerr << "SysEx of " << messageLength << " bytes found" << endl; + SVDEBUG << "SysEx of " << messageLength << " bytes found" << endl; #endif metaMessage= getMIDIBytes(messageLength); @@ -644,10 +645,10 @@ } if (lastTrackNum > metaTrack) { - for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) { - m_trackNames[track] = QString("%1 <%2>") - .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1); - } + for (unsigned int track = metaTrack + 1; track <= lastTrackNum; ++track) { + m_trackNames[track] = QString("%1 <%2>") + .arg(m_trackNames[metaTrack]).arg(track - metaTrack + 1); + } } return true; @@ -664,18 +665,18 @@ bool noteOffFound; for (MIDITrack::iterator i = m_midiComposition[track].begin(); - i != m_midiComposition[track].end(); i++) { + i != m_midiComposition[track].end(); i++) { if ((*i)->getMessageType() == MIDI_NOTE_ON && (*i)->getVelocity() > 0) { - notesOnTrack = true; + notesOnTrack = true; noteOffFound = false; for (MIDITrack::iterator j = i; - j != m_midiComposition[track].end(); j++) { + j != m_midiComposition[track].end(); j++) { if (((*j)->getChannelNumber() == (*i)->getChannelNumber()) && - ((*j)->getPitch() == (*i)->getPitch()) && + ((*j)->getPitch() == (*i)->getPitch()) && ((*j)->getMessageType() == MIDI_NOTE_OFF || ((*j)->getMessageType() == MIDI_NOTE_ON && (*j)->getVelocity() == 0x00))) { @@ -694,10 +695,10 @@ // Event duration to length of track // if (!noteOffFound) { - MIDITrack::iterator j = m_midiComposition[track].end(); - --j; + MIDITrack::iterator j = m_midiComposition[track].end(); + --j; (*i)->setDuration((*j)->getTime() - (*i)->getTime()); - } + } } } @@ -709,27 +710,27 @@ void MIDIFileReader::updateTempoMap(unsigned int track) { - cerr << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << endl; + SVDEBUG << "updateTempoMap for track " << track << " (" << m_midiComposition[track].size() << " events)" << endl; for (MIDITrack::iterator i = m_midiComposition[track].begin(); - i != m_midiComposition[track].end(); ++i) { + i != m_midiComposition[track].end(); ++i) { if ((*i)->isMeta() && - (*i)->getMetaEventCode() == MIDI_SET_TEMPO) { + (*i)->getMetaEventCode() == MIDI_SET_TEMPO) { - MIDIByte m0 = (*i)->getMetaMessage()[0]; - MIDIByte m1 = (*i)->getMetaMessage()[1]; - MIDIByte m2 = (*i)->getMetaMessage()[2]; - - long tempo = (((m0 << 8) + m1) << 8) + m2; + MIDIByte m0 = (*i)->getMetaMessage()[0]; + MIDIByte m1 = (*i)->getMetaMessage()[1]; + MIDIByte m2 = (*i)->getMetaMessage()[2]; + + long tempo = (((m0 << 8) + m1) << 8) + m2; - cerr << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << endl; + SVDEBUG << "updateTempoMap: have tempo, it's " << tempo << " at " << (*i)->getTime() << endl; - if (tempo != 0) { - double qpm = 60000000.0 / double(tempo); - m_tempoMap[(*i)->getTime()] = - TempoChange(RealTime::zeroTime, qpm); - } + if (tempo != 0) { + double qpm = 60000000.0 / double(tempo); + m_tempoMap[(*i)->getTime()] = + TempoChange(RealTime::zeroTime, qpm); + } } } } @@ -744,19 +745,19 @@ if (td == 0) td = 96; for (TempoMap::iterator i = m_tempoMap.begin(); i != m_tempoMap.end(); ++i) { - - unsigned long mtime = i->first; - unsigned long melapsed = mtime - lastMIDITime; - double quarters = double(melapsed) / double(td); - double seconds = (60.0 * quarters) / tempo; + + unsigned long mtime = i->first; + unsigned long melapsed = mtime - lastMIDITime; + double quarters = double(melapsed) / double(td); + double seconds = (60.0 * quarters) / tempo; - RealTime t = lastRealTime + RealTime::fromSeconds(seconds); + RealTime t = lastRealTime + RealTime::fromSeconds(seconds); - i->second.first = t; + i->second.first = t; - lastRealTime = t; - lastMIDITime = mtime; - tempo = i->second.second; + lastRealTime = t; + lastMIDITime = mtime; + tempo = i->second.second; } } @@ -769,10 +770,10 @@ TempoMap::const_iterator i = m_tempoMap.lower_bound(midiTime); if (i != m_tempoMap.begin()) { - --i; - tempoMIDITime = i->first; - tempoRealTime = i->second.first; - tempo = i->second.second; + --i; + tempoMIDITime = i->first; + tempoRealTime = i->second.first; + tempo = i->second.second; } int td = m_timingDivision; @@ -784,13 +785,13 @@ /* SVDEBUG << "MIDIFileReader::getTimeForMIDITime(" << midiTime << ")" - << endl; + << endl; SVDEBUG << "timing division = " << td << endl; - cerr << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " (" - << tempoRealTime << ")" << endl; - cerr << "quarters since then = " << quarters << endl; - cerr << "tempo = " << tempo << " quarters per minute" << endl; - cerr << "seconds since then = " << seconds << endl; + SVDEBUG << "nearest tempo event (of " << m_tempoMap.size() << ") is at " << tempoMIDITime << " (" + << tempoRealTime << ")" << endl; + SVDEBUG << "quarters since then = " << quarters << endl; + SVDEBUG << "tempo = " << tempo << " quarters per minute" << endl; + SVDEBUG << "seconds since then = " << seconds << endl; SVDEBUG << "resulting time = " << (tempoRealTime + RealTime::fromSeconds(seconds)) << endl; */ @@ -807,40 +808,40 @@ m_acquirer->showError (tr("MIDI file \"%1\" has no notes in any track").arg(m_path)); } - return 0; + return 0; } std::set tracksToLoad; if (m_loadableTracks.size() == 1) { - tracksToLoad.insert(*m_loadableTracks.begin()); + tracksToLoad.insert(*m_loadableTracks.begin()); } else { QStringList displayNames; - for (set::iterator i = m_loadableTracks.begin(); - i != m_loadableTracks.end(); ++i) { + for (set::iterator i = m_loadableTracks.begin(); + i != m_loadableTracks.end(); ++i) { - unsigned int trackNo = *i; - QString label; + unsigned int trackNo = *i; + QString label; - QString perc; - if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) { - perc = tr(" - uses GM percussion channel"); - } + QString perc; + if (m_percussionTracks.find(trackNo) != m_percussionTracks.end()) { + perc = tr(" - uses GM percussion channel"); + } - if (m_trackNames.find(trackNo) != m_trackNames.end()) { - label = tr("Track %1 (%2)%3") - .arg(trackNo).arg(m_trackNames.find(trackNo)->second) - .arg(perc); - } else { - label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc); - } + if (m_trackNames.find(trackNo) != m_trackNames.end()) { + label = tr("Track %1 (%2)%3") + .arg(trackNo).arg(m_trackNames.find(trackNo)->second) + .arg(perc); + } else { + label = tr("Track %1 (untitled)%3").arg(trackNo).arg(perc); + } displayNames << label; - } + } QString singleTrack; @@ -866,28 +867,28 @@ for (set::iterator i = m_loadableTracks.begin(); i != m_loadableTracks.end(); ++i) { - if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks || - m_percussionTracks.find(*i) == m_percussionTracks.end()) { + if (pref == MIDIFileImportPreferenceAcquirer::MergeAllTracks || + m_percussionTracks.find(*i) == m_percussionTracks.end()) { - tracksToLoad.insert(*i); - } - } + tracksToLoad.insert(*i); + } + } - } else { - - int j = 0; + } else { + + int j = 0; - for (set::iterator i = m_loadableTracks.begin(); - i != m_loadableTracks.end(); ++i) { - - if (singleTrack == displayNames[j]) { - tracksToLoad.insert(*i); - break; - } - - ++j; - } - } + for (set::iterator i = m_loadableTracks.begin(); + i != m_loadableTracks.end(); ++i) { + + if (singleTrack == displayNames[j]) { + tracksToLoad.insert(*i); + break; + } + + ++j; + } + } } if (tracksToLoad.empty()) return 0; @@ -896,18 +897,18 @@ Model *model = 0; for (std::set::iterator i = tracksToLoad.begin(); - i != tracksToLoad.end(); ++i) { + i != tracksToLoad.end(); ++i) { - int minProgress = (100 * count) / n; - int progressAmount = 100 / n; + int minProgress = (100 * count) / n; + int progressAmount = 100 / n; - model = loadTrack(*i, model, minProgress, progressAmount); + model = loadTrack(*i, model, minProgress, progressAmount); - ++count; + ++count; } if (dynamic_cast(model)) { - dynamic_cast(model)->setCompletion(100); + dynamic_cast(model)->setCompletion(100); } return model; @@ -915,26 +916,26 @@ Model * MIDIFileReader::loadTrack(unsigned int trackToLoad, - Model *existingModel, - int minProgress, - int progressAmount) const + Model *existingModel, + int minProgress, + int progressAmount) const { if (m_midiComposition.find(trackToLoad) == m_midiComposition.end()) { - return 0; + return 0; } NoteModel *model = 0; if (existingModel) { - model = dynamic_cast(existingModel); - if (!model) { - cerr << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl; - } + model = dynamic_cast(existingModel); + if (!model) { + SVDEBUG << "WARNING: MIDIFileReader::loadTrack: Existing model given, but it isn't a NoteModel -- ignoring it" << endl; + } } if (!model) { - model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false); - model->setValueQuantization(1.0); + model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false); + model->setValueQuantization(1.0); model->setObjectName(QFileInfo(m_path).fileName()); } @@ -956,54 +957,54 @@ rt = getTimeForMIDITime(midiTime); } - // We ignore most of these event types for now, though in - // theory some of the text ones could usefully be incorporated + // We ignore most of these event types for now, though in + // theory some of the text ones could usefully be incorporated - if ((*i)->isMeta()) { + if ((*i)->isMeta()) { - switch((*i)->getMetaEventCode()) { + switch((*i)->getMetaEventCode()) { - case MIDI_KEY_SIGNATURE: - // minorKey = (int((*i)->getMetaMessage()[1]) != 0); - sharpKey = (int((*i)->getMetaMessage()[0]) >= 0); - break; + case MIDI_KEY_SIGNATURE: + // minorKey = (int((*i)->getMetaMessage()[1]) != 0); + sharpKey = (int((*i)->getMetaMessage()[0]) >= 0); + break; - case MIDI_TEXT_EVENT: - case MIDI_LYRIC: - case MIDI_TEXT_MARKER: - case MIDI_COPYRIGHT_NOTICE: - case MIDI_TRACK_NAME: - // The text events that we could potentially use - break; + case MIDI_TEXT_EVENT: + case MIDI_LYRIC: + case MIDI_TEXT_MARKER: + case MIDI_COPYRIGHT_NOTICE: + case MIDI_TRACK_NAME: + // The text events that we could potentially use + break; - case MIDI_SET_TEMPO: - // Already dealt with in a separate pass previously - break; + case MIDI_SET_TEMPO: + // Already dealt with in a separate pass previously + break; - case MIDI_TIME_SIGNATURE: - // Not yet! - break; + case MIDI_TIME_SIGNATURE: + // Not yet! + break; - case MIDI_SEQUENCE_NUMBER: - case MIDI_CHANNEL_PREFIX_OR_PORT: - case MIDI_INSTRUMENT_NAME: - case MIDI_CUE_POINT: - case MIDI_CHANNEL_PREFIX: - case MIDI_SEQUENCER_SPECIFIC: - case MIDI_SMPTE_OFFSET: - default: - break; - } + case MIDI_SEQUENCE_NUMBER: + case MIDI_CHANNEL_PREFIX_OR_PORT: + case MIDI_INSTRUMENT_NAME: + case MIDI_CUE_POINT: + case MIDI_CHANNEL_PREFIX: + case MIDI_SEQUENCER_SPECIFIC: + case MIDI_SMPTE_OFFSET: + default: + break; + } - } else { + } else { - switch ((*i)->getMessageType()) { + switch ((*i)->getMessageType()) { - case MIDI_NOTE_ON: + case MIDI_NOTE_ON: if ((*i)->getVelocity() == 0) break; // effective note-off - else { - RealTime endRT; + else { + RealTime endRT; unsigned long endMidiTime = (*i)->getTime() + (*i)->getDuration(); if (m_smpte) { endRT = RealTime::frame2RealTime(endMidiTime, m_fps * m_subframes); @@ -1011,32 +1012,32 @@ endRT = getTimeForMIDITime(endMidiTime); } - long startFrame = RealTime::realTime2Frame - (rt, model->getSampleRate()); + long startFrame = RealTime::realTime2Frame + (rt, model->getSampleRate()); - long endFrame = RealTime::realTime2Frame - (endRT, model->getSampleRate()); + long endFrame = RealTime::realTime2Frame + (endRT, model->getSampleRate()); - QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(), - 0, - !sharpKey); + QString pitchLabel = Pitch::getPitchLabel((*i)->getPitch(), + 0, + !sharpKey); - QString noteLabel = tr("%1 - vel %2") - .arg(pitchLabel).arg(int((*i)->getVelocity())); + 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, level, noteLabel); + Note note(startFrame, (*i)->getPitch(), + endFrame - startFrame, level, noteLabel); -// SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl; +// SVDEBUG << "Adding note " << startFrame << "," << (endFrame-startFrame) << " : " << int((*i)->getPitch()) << endl; - model->addPoint(note); - break; - } + model->addPoint(note); + break; + } case MIDI_PITCH_BEND: - // I guess we could make some use of this... + // I guess we could make some use of this... break; case MIDI_NOTE_OFF: @@ -1050,11 +1051,11 @@ default: break; } - } + } - model->setCompletion(minProgress + - (count * progressAmount) / totalEvents); - ++count; + model->setCompletion(minProgress + + (count * progressAmount) / totalEvents); + ++count; } return model; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/MIDIFileReader.h --- a/data/fileio/MIDIFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/MIDIFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -12,15 +12,14 @@ COPYING included with this distribution for more information. */ - /* This is a modified version of a source file from the Rosegarden MIDI and audio sequencer and notation editor. This file copyright 2000-2006 Richard Bown and Chris Cannam. */ -#ifndef _MIDI_FILE_READER_H_ -#define _MIDI_FILE_READER_H_ +#ifndef SV_MIDI_FILE_READER_H +#define SV_MIDI_FILE_READER_H #include "DataFileReader.h" #include "base/RealTime.h" @@ -32,6 +31,7 @@ #include class MIDIEvent; +class ProgressReporter; typedef unsigned char MIDIByte; @@ -61,8 +61,9 @@ public: MIDIFileReader(QString path, - MIDIFileImportPreferenceAcquirer *pref, - sv_samplerate_t mainModelSampleRate); + MIDIFileImportPreferenceAcquirer *pref, // may be null + sv_samplerate_t mainModelSampleRate, + ProgressReporter *reporter = 0); virtual ~MIDIFileReader(); virtual bool isOK() const; @@ -76,10 +77,10 @@ typedef std::map TempoMap; // key is MIDI time typedef enum { - MIDI_SINGLE_TRACK_FILE = 0x00, - MIDI_SIMULTANEOUS_TRACK_FILE = 0x01, - MIDI_SEQUENTIAL_TRACK_FILE = 0x02, - MIDI_FILE_BAD_FORMAT = 0xFF + MIDI_SINGLE_TRACK_FILE = 0x00, + MIDI_SIMULTANEOUS_TRACK_FILE = 0x01, + MIDI_SEQUENTIAL_TRACK_FILE = 0x02, + MIDI_FILE_BAD_FORMAT = 0xFF } MIDIFileFormatType; bool parseFile(); @@ -87,9 +88,9 @@ bool parseTrack(unsigned int &trackNum); Model *loadTrack(unsigned int trackNum, - Model *existingModel = 0, - int minProgress = 0, - int progressAmount = 100) const; + Model *existingModel = 0, + int minProgress = 0, + int progressAmount = 100) const; bool consolidateNoteOffEvents(unsigned int track); void updateTempoMap(unsigned int track); diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/MIDIFileWriter.cpp --- a/data/fileio/MIDIFileWriter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/MIDIFileWriter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -56,14 +56,14 @@ MIDIFileWriter::~MIDIFileWriter() { for (MIDIComposition::iterator i = m_midiComposition.begin(); - i != m_midiComposition.end(); ++i) { - - for (MIDITrack::iterator j = i->second.begin(); - j != i->second.end(); ++j) { - delete *j; - } + i != m_midiComposition.end(); ++i) { + + for (MIDITrack::iterator j = i->second.begin(); + j != i->second.end(); ++j) { + delete *j; + } - i->second.clear(); + i->second.clear(); } m_midiComposition.clear(); diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/MIDIFileWriter.h --- a/data/fileio/MIDIFileWriter.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/MIDIFileWriter.h Mon Sep 17 13:51:14 2018 +0100 @@ -60,10 +60,10 @@ typedef std::map MIDIComposition; typedef enum { - MIDI_SINGLE_TRACK_FILE = 0x00, - MIDI_SIMULTANEOUS_TRACK_FILE = 0x01, - MIDI_SEQUENTIAL_TRACK_FILE = 0x02, - MIDI_FILE_BAD_FORMAT = 0xFF + MIDI_SINGLE_TRACK_FILE = 0x00, + MIDI_SIMULTANEOUS_TRACK_FILE = 0x01, + MIDI_SEQUENTIAL_TRACK_FILE = 0x02, + MIDI_FILE_BAD_FORMAT = 0xFF } MIDIFileFormatType; std::string intToMIDIBytes(int number) const; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/MP3FileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -32,12 +32,17 @@ #include #endif +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + #include -#ifdef _MSC_VER -#include -#define open _open -#endif +#include using std::string; @@ -75,13 +80,7 @@ CodedAudioFileReader::setFramesToTrim(DEFAULT_DECODER_DELAY, 0); } - struct stat stat; - if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { - m_error = QString("File %1 does not exist.").arg(m_path); - return; - } - - m_fileSize = stat.st_size; + m_fileSize = 0; m_fileBuffer = 0; m_fileBufferSize = 0; @@ -89,53 +88,42 @@ m_sampleBuffer = 0; m_sampleBufferSize = 0; - int fd = -1; - if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY -#ifdef _WIN32 - | O_BINARY -#endif - , 0)) < 0) { - m_error = QString("Failed to open file %1 for reading.").arg(m_path); - return; - } + QFile qfile(m_path); + if (!qfile.open(QIODevice::ReadOnly)) { + m_error = QString("Failed to open file %1 for reading.").arg(m_path); + SVDEBUG << "MP3FileReader: " << m_error << endl; + return; + } + m_fileSize = qfile.size(); + try { // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at // end of input, to ensure libmad decodes the last frame // correctly. Otherwise the decoded audio is truncated. + SVDEBUG << "file size = " << m_fileSize << ", buffer guard = " << MAD_BUFFER_GUARD << endl; m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; m_fileBuffer = new unsigned char[m_fileBufferSize]; memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); } catch (...) { m_error = QString("Out of memory"); - ::close(fd); - return; - } - - ssize_t sz = 0; - ssize_t offset = 0; - while (offset < m_fileSize) { - sz = ::read(fd, m_fileBuffer + offset, m_fileSize - offset); - if (sz < 0) { - m_error = QString("Read error for file %1 (after %2 bytes)") - .arg(m_path).arg(offset); - delete[] m_fileBuffer; - ::close(fd); - return; - } else if (sz == 0) { - SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") - .arg(offset).arg(m_fileSize) << endl; - m_fileSize = offset; - m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; - memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); - break; - } - offset += sz; + SVDEBUG << "MP3FileReader: " << m_error << endl; + return; } - ::close(fd); + auto amountRead = qfile.read(reinterpret_cast(m_fileBuffer), + m_fileSize); - loadTags(); + if (amountRead < m_fileSize) { + SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") + .arg(amountRead).arg(m_fileSize) << endl; + memset(m_fileBuffer + amountRead, 0, m_fileSize - amountRead); + m_fileSize = amountRead; + } + + loadTags(qfile.handle()); + + qfile.close(); if (decodeMode == DecodeAtOnce) { @@ -148,6 +136,14 @@ if (!decode(m_fileBuffer, m_fileBufferSize)) { m_error = QString("Failed to decode file %1.").arg(m_path); } + + if (m_sampleBuffer) { + for (int c = 0; c < m_channelCount; ++c) { + delete[] m_sampleBuffer[c]; + } + delete[] m_sampleBuffer; + m_sampleBuffer = 0; + } delete[] m_fileBuffer; m_fileBuffer = 0; @@ -191,14 +187,19 @@ } void -MP3FileReader::loadTags() +MP3FileReader::loadTags(int fd) { m_title = ""; #ifdef HAVE_ID3TAG - id3_file *file = id3_file_open(m_path.toLocal8Bit().data(), - ID3_FILE_MODE_READONLY); +#ifdef _WIN32 + int id3fd = _dup(fd); +#else + int id3fd = dup(fd); +#endif + + id3_file *file = id3_file_fdopen(id3fd, ID3_FILE_MODE_READONLY); if (!file) return; // We can do this a lot more elegantly, but we'll leave that for @@ -207,7 +208,7 @@ id3_tag *tag = id3_file_tag(file); if (!tag) { SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl; - id3_file_close(file); + id3_file_close(file); // also closes our dup'd fd return; } @@ -228,7 +229,7 @@ } } - id3_file_close(file); + id3_file_close(file); // also closes our dup'd fd #else SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl; @@ -479,7 +480,7 @@ enum mad_flow MP3FileReader::accept(struct mad_header const *header, - struct mad_pcm *pcm) + struct mad_pcm *pcm) { int channels = pcm->channels; int frames = pcm->length; @@ -496,6 +497,10 @@ m_fileRate = pcm->samplerate; m_channelCount = channels; + SVDEBUG << "MP3FileReader::accept: file rate = " << pcm->samplerate + << ", channel count = " << channels << ", about to init " + << "decode cache" << endl; + initialiseDecodeCache(); if (m_cacheMode == CacheInTemporaryFile) { @@ -525,6 +530,9 @@ } if (!isDecodeCacheInitialised()) { + SVDEBUG << "MP3FileReader::accept: fallback case: file rate = " << pcm->samplerate + << ", channel count = " << channels << ", about to init " + << "decode cache" << endl; initialiseDecodeCache(); } @@ -548,14 +556,14 @@ for (int i = 0; i < frames; ++i) { - mad_fixed_t sample = 0; - if (ch < activeChannels) { - sample = pcm->samples[ch][i]; - } - float fsample = float(sample) / float(MAD_F_ONE); + mad_fixed_t sample = 0; + if (ch < activeChannels) { + sample = pcm->samples[ch][i]; + } + float fsample = float(sample) / float(MAD_F_ONE); m_sampleBuffer[ch][i] = fsample; - } + } } addSamplesToDecodeCache(m_sampleBuffer, frames); @@ -584,8 +592,8 @@ if (!data->reader->m_decodeErrorShown) { char buffer[256]; snprintf(buffer, 255, - "MP3 decoding error 0x%04x (%s) at byte offset %lu", - stream->error, mad_stream_errorstr(stream), ix); + "MP3 decoding error 0x%04x (%s) at byte offset %lld", + stream->error, mad_stream_errorstr(stream), (long long int)ix); SVCERR << "Warning: in file \"" << data->reader->m_path << "\": " << buffer << " (continuing; will not report any further decode errors for this file)" << endl; data->reader->m_decodeErrorShown = true; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/MP3FileReader.h --- a/data/fileio/MP3FileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/MP3FileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -122,10 +122,10 @@ bool m_decodeErrorShown; struct DecoderData { - unsigned char const *start; - sv_frame_t length; + unsigned char const *start; + sv_frame_t length; bool finished; - MP3FileReader *reader; + MP3FileReader *reader; }; bool decode(void *mm, sv_frame_t sz); @@ -152,7 +152,7 @@ DecodeThread *m_decodeThread; - void loadTags(); + void loadTags(int fd); QString loadTag(void *vtag, const char *name); }; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/OggVorbisFileReader.cpp --- a/data/fileio/OggVorbisFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -40,6 +40,10 @@ CodedAudioFileReader(mode, targetRate, normalised), m_source(source), m_path(source.getLocalFilename()), + m_qfile(0), + m_ffile(0), + m_oggz(0), + m_fishSound(0), m_reporter(reporter), m_fileSize(0), m_bytesRead(0), @@ -60,12 +64,36 @@ Profiler profiler("OggVorbisFileReader::OggVorbisFileReader"); - QFileInfo info(m_path); - m_fileSize = info.size(); + // These shenanigans are to avoid using oggz_open(..) with a local + // codepage on Windows (make sure proper filename encoding is used) + + m_qfile = new QFile(m_path); + if (!m_qfile->open(QIODevice::ReadOnly)) { + m_error = QString("Failed to open file %1 for reading.").arg(m_path); + SVDEBUG << "OggVorbisFileReader: " << m_error << endl; + delete m_qfile; + m_qfile = 0; + return; + } + + m_fileSize = m_qfile->size(); - if (!(m_oggz = oggz_open(m_path.toLocal8Bit().data(), OGGZ_READ))) { - m_error = QString("File %1 is not an OGG file.").arg(m_path); - return; + m_ffile = fdopen(dup(m_qfile->handle()), "rb"); + if (!m_ffile) { + m_error = QString("Failed to open file pointer for file %1").arg(m_path); + SVDEBUG << "OggVorbisFileReader: " << m_error << endl; + delete m_qfile; + m_qfile = 0; + return; + } + + if (!(m_oggz = oggz_open_stdio(m_ffile, OGGZ_READ))) { + m_error = QString("File %1 is not an OGG file.").arg(m_path); + fclose(m_ffile); + m_ffile = 0; + delete m_qfile; + m_qfile = 0; + return; } FishSoundInfo fsinfo; @@ -114,6 +142,11 @@ m_decodeThread->wait(); delete m_decodeThread; } + if (m_qfile) { + // don't fclose m_ffile; oggz_close did that + delete m_qfile; + m_qfile = 0; + } } void @@ -134,8 +167,14 @@ fish_sound_delete(m_reader->m_fishSound); m_reader->m_fishSound = 0; + oggz_close(m_reader->m_oggz); m_reader->m_oggz = 0; + + // don't fclose m_ffile; oggz_close did that + + delete m_reader->m_qfile; + m_reader->m_qfile = 0; if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); m_reader->m_completion = 100; @@ -172,7 +211,7 @@ int OggVorbisFileReader::acceptFrames(FishSound *fs, float **frames, long nframes, - void *data) + void *data) { OggVorbisFileReader *reader = (OggVorbisFileReader *)data; @@ -196,11 +235,11 @@ } if (reader->m_channelCount == 0) { - FishSoundInfo fsinfo; - fish_sound_command(fs, FISH_SOUND_GET_INFO, - &fsinfo, sizeof(FishSoundInfo)); - reader->m_fileRate = fsinfo.samplerate; - reader->m_channelCount = fsinfo.channels; + FishSoundInfo fsinfo; + fish_sound_command(fs, FISH_SOUND_GET_INFO, + &fsinfo, sizeof(FishSoundInfo)); + reader->m_fileRate = fsinfo.samplerate; + reader->m_channelCount = fsinfo.channels; reader->initialiseDecodeCache(); } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/OggVorbisFileReader.h --- a/data/fileio/OggVorbisFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/OggVorbisFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _OGG_VORBIS_FILE_READER_H_ -#define _OGG_VORBIS_FILE_READER_H_ +#ifndef SV_OGG_VORBIS_FILE_READER_H +#define SV_OGG_VORBIS_FILE_READER_H #ifdef HAVE_OGGZ #ifdef HAVE_FISHSOUND @@ -25,6 +25,8 @@ #include #include +#include + #include class ProgressReporter; @@ -71,6 +73,8 @@ QString m_maker; TagMap m_tags; + QFile *m_qfile; + FILE *m_ffile; OGGZ *m_oggz; FishSound *m_fishSound; ProgressReporter *m_reporter; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/PlaylistFileReader.cpp --- a/data/fileio/PlaylistFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/PlaylistFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -66,13 +66,13 @@ bool good = false; if (!m_file->exists()) { - m_error = QFile::tr("File \"%1\" does not exist") + m_error = QFile::tr("File \"%1\" does not exist") .arg(m_source.getLocation()); } else if (!m_file->open(QIODevice::ReadOnly | QIODevice::Text)) { - m_error = QFile::tr("Failed to open file \"%1\"") + m_error = QFile::tr("Failed to open file \"%1\"") .arg(m_source.getLocation()); } else { - good = true; + good = true; } if (good) { @@ -82,8 +82,8 @@ } if (!good) { - delete m_file; - m_file = 0; + delete m_file; + m_file = 0; } } diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/QuickTimeFileReader.cpp --- a/data/fileio/QuickTimeFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,383 +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-2007 Chris Cannam and QMUL. - - Based on QTAudioFile.cpp from SoundBite, copyright 2006 - Chris Sutton and Mark Levy. - - 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. -*/ - -#ifdef HAVE_QUICKTIME - -#include "QuickTimeFileReader.h" -#include "base/Profiler.h" -#include "base/ProgressReporter.h" -#include "system/System.h" - -#include - -#ifdef _WIN32 -#include -#include -#else -#include -#endif - -class QuickTimeFileReader::D -{ -public: - D() : data(0), blockSize(1024) { } - - MovieAudioExtractionRef extractionSessionRef; - AudioBufferList buffer; - float *data; - OSErr err; - AudioStreamBasicDescription asbd; - Movie movie; - int blockSize; -}; - - -QuickTimeFileReader::QuickTimeFileReader(FileSource source, - DecodeMode decodeMode, - CacheMode mode, - sv_samplerate_t targetRate, - bool normalised, - ProgressReporter *reporter) : - CodedAudioFileReader(mode, targetRate, normalised), - m_source(source), - m_path(source.getLocalFilename()), - m_d(new D), - m_reporter(reporter), - m_cancelled(false), - m_completion(0), - m_decodeThread(0) -{ - SVDEBUG << "QuickTimeFileReader: local path: \"" << m_path - << "\", decode mode: " << decodeMode << " (" - << (decodeMode == DecodeAtOnce ? "DecodeAtOnce" : "DecodeThreaded") - << ")" << endl; - - m_channelCount = 0; - m_fileRate = 0; - - Profiler profiler("QuickTimeFileReader::QuickTimeFileReader", true); - - long QTversion; - -#ifdef WIN32 - InitializeQTML(0); // FIXME should check QT version -#else - m_d->err = Gestalt(gestaltQuickTime,&QTversion); - if ((m_d->err != noErr) || (QTversion < 0x07000000)) { - m_error = QString("Failed to find compatible version of QuickTime (version 7 or above required)"); - return; - } -#endif - - EnterMovies(); - - Handle dataRef; - OSType dataRefType; - -// CFStringRef URLString = CFStringCreateWithCString - // (0, m_path.toLocal8Bit().data(), 0); - - - QByteArray ba = m_path.toLocal8Bit(); - - CFURLRef url = CFURLCreateFromFileSystemRepresentation - (kCFAllocatorDefault, - (const UInt8 *)ba.data(), - (CFIndex)ba.length(), - false); - - -// m_d->err = QTNewDataReferenceFromURLCFString - m_d->err = QTNewDataReferenceFromCFURL - (url, 0, &dataRef, &dataRefType); - - if (m_d->err) { - m_error = QString("Error creating data reference for QuickTime decoder: code %1").arg(m_d->err); - return; - } - - short fileID = movieInDataForkResID; - short flags = 0; - m_d->err = NewMovieFromDataRef - (&m_d->movie, flags, &fileID, dataRef, dataRefType); - - DisposeHandle(dataRef); - if (m_d->err) { - m_error = QString("Error creating new movie for QuickTime decoder: code %1").arg(m_d->err); - return; - } - - Boolean isProtected = 0; - Track aTrack = GetMovieIndTrackType - (m_d->movie, 1, SoundMediaType, - movieTrackMediaType | movieTrackEnabledOnly); - - if (aTrack) { - Media aMedia = GetTrackMedia(aTrack); // get the track media - if (aMedia) { - MediaHandler mh = GetMediaHandler(aMedia); // get the media handler we can query - if (mh) { - m_d->err = QTGetComponentProperty(mh, - kQTPropertyClass_DRM, - kQTDRMPropertyID_IsProtected, - sizeof(Boolean), &isProtected,nil); - } else { - m_d->err = 1; - } - } else { - m_d->err = 1; - } - } else { - m_d->err = 1; - } - - if (m_d->err && m_d->err != kQTPropertyNotSupportedErr) { - m_error = QString("Error checking for DRM in QuickTime decoder: code %1").arg(m_d->err); - return; - } else if (!m_d->err && isProtected) { - m_error = QString("File is protected with DRM"); - return; - } else if (m_d->err == kQTPropertyNotSupportedErr && !isProtected) { - cerr << "QuickTime: File is not protected with DRM" << endl; - } - - if (m_d->movie) { - SetMovieActive(m_d->movie, TRUE); - m_d->err = GetMoviesError(); - if (m_d->err) { - m_error = QString("Error in QuickTime decoder activation: code %1").arg(m_d->err); - return; - } - } else { - m_error = QString("Error in QuickTime decoder: Movie object not valid"); - return; - } - - m_d->err = MovieAudioExtractionBegin - (m_d->movie, 0, &m_d->extractionSessionRef); - if (m_d->err) { - m_error = QString("Error in QuickTime decoder extraction init: code %1").arg(m_d->err); - return; - } - - m_d->err = MovieAudioExtractionGetProperty - (m_d->extractionSessionRef, - kQTPropertyClass_MovieAudioExtraction_Audio, kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, - sizeof(m_d->asbd), - &m_d->asbd, - nil); - - if (m_d->err) { - m_error = QString("Error in QuickTime decoder property get: code %1").arg(m_d->err); - return; - } - - m_channelCount = m_d->asbd.mChannelsPerFrame; - m_fileRate = m_d->asbd.mSampleRate; - - cerr << "QuickTime: " << m_channelCount << " channels, " << m_fileRate << " kHz" << endl; - - m_d->asbd.mFormatFlags = - kAudioFormatFlagIsFloat | - kAudioFormatFlagIsPacked | - kAudioFormatFlagsNativeEndian; - m_d->asbd.mBitsPerChannel = sizeof(float) * 8; - m_d->asbd.mBytesPerFrame = sizeof(float) * m_d->asbd.mChannelsPerFrame; - m_d->asbd.mBytesPerPacket = m_d->asbd.mBytesPerFrame; - - m_d->err = MovieAudioExtractionSetProperty - (m_d->extractionSessionRef, - kQTPropertyClass_MovieAudioExtraction_Audio, - kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, - sizeof(m_d->asbd), - &m_d->asbd); - - if (m_d->err) { - m_error = QString("Error in QuickTime decoder property set: code %1").arg(m_d->err); - m_channelCount = 0; - return; - } - m_d->buffer.mNumberBuffers = 1; - m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount; - m_d->buffer.mBuffers[0].mDataByteSize = - sizeof(float) * m_channelCount * m_d->blockSize; - m_d->data = new float[m_channelCount * m_d->blockSize]; - m_d->buffer.mBuffers[0].mData = m_d->data; - - initialiseDecodeCache(); - - if (decodeMode == DecodeAtOnce) { - - if (m_reporter) { - connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); - m_reporter->setMessage - (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); - } - - while (1) { - - UInt32 framesRead = m_d->blockSize; - UInt32 extractionFlags = 0; - m_d->err = MovieAudioExtractionFillBuffer - (m_d->extractionSessionRef, &framesRead, &m_d->buffer, - &extractionFlags); - if (m_d->err) { - m_error = QString("Error in QuickTime decoding: code %1") - .arg(m_d->err); - break; - } - - //!!! progress? - -// cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << endl; - - // QuickTime buffers are interleaved unless specified otherwise - addSamplesToDecodeCache(m_d->data, framesRead); - - if (framesRead < m_d->blockSize) break; - } - - finishDecodeCache(); - endSerialised(); - - m_d->err = MovieAudioExtractionEnd(m_d->extractionSessionRef); - if (m_d->err) { - m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_d->err); - } - - m_completion = 100; - - } else { - if (m_reporter) m_reporter->setProgress(100); - - if (m_channelCount > 0) { - m_decodeThread = new DecodeThread(this); - m_decodeThread->start(); - } - } - - cerr << "QuickTimeFileReader::QuickTimeFileReader: frame count is now " << getFrameCount() << ", error is \"\"" << m_error << "\"" << endl; -} - -QuickTimeFileReader::~QuickTimeFileReader() -{ - SVDEBUG << "QuickTimeFileReader::~QuickTimeFileReader" << endl; - - if (m_decodeThread) { - m_cancelled = true; - m_decodeThread->wait(); - delete m_decodeThread; - } - - SetMovieActive(m_d->movie, FALSE); - DisposeMovie(m_d->movie); - - delete[] m_d->data; - delete m_d; -} - -void -QuickTimeFileReader::cancelled() -{ - m_cancelled = true; -} - -void -QuickTimeFileReader::DecodeThread::run() -{ - if (m_reader->m_cacheMode == CacheInTemporaryFile) { - m_reader->m_completion = 1; - m_reader->startSerialised("QuickTimeFileReader::Decode"); - } - - while (1) { - - UInt32 framesRead = m_reader->m_d->blockSize; - UInt32 extractionFlags = 0; - m_reader->m_d->err = MovieAudioExtractionFillBuffer - (m_reader->m_d->extractionSessionRef, &framesRead, - &m_reader->m_d->buffer, &extractionFlags); - if (m_reader->m_d->err) { - m_reader->m_error = QString("Error in QuickTime decoding: code %1") - .arg(m_reader->m_d->err); - break; - } - - // QuickTime buffers are interleaved unless specified otherwise - m_reader->addSamplesToDecodeCache(m_reader->m_d->data, framesRead); - - if (framesRead < m_reader->m_d->blockSize) break; - } - - m_reader->finishDecodeCache(); - - m_reader->m_d->err = MovieAudioExtractionEnd(m_reader->m_d->extractionSessionRef); - if (m_reader->m_d->err) { - m_reader->m_error = QString("Error ending QuickTime extraction session: code %1").arg(m_reader->m_d->err); - } - - m_reader->m_completion = 100; - m_reader->endSerialised(); -} - -void -QuickTimeFileReader::getSupportedExtensions(std::set &extensions) -{ - extensions.insert("aiff"); - extensions.insert("aif"); - extensions.insert("au"); - extensions.insert("avi"); - extensions.insert("m4a"); - extensions.insert("m4b"); - extensions.insert("m4p"); - extensions.insert("m4v"); - extensions.insert("mov"); - extensions.insert("mp3"); - extensions.insert("mp4"); - extensions.insert("wav"); -} - -bool -QuickTimeFileReader::supportsExtension(QString extension) -{ - std::set extensions; - getSupportedExtensions(extensions); - return (extensions.find(extension.toLower()) != extensions.end()); -} - -bool -QuickTimeFileReader::supportsContentType(QString type) -{ - return (type == "audio/x-aiff" || - type == "audio/x-wav" || - type == "audio/mpeg" || - type == "audio/basic" || - type == "audio/x-aac" || - type == "video/mp4" || - type == "video/quicktime"); -} - -bool -QuickTimeFileReader::supports(FileSource &source) -{ - return (supportsExtension(source.getExtension()) || - supportsContentType(source.getContentType())); -} - -#endif - diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/QuickTimeFileReader.h --- a/data/fileio/QuickTimeFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +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-2007 Chris Cannam and QMUL. - - Based in part on QTAudioFile.h from SoundBite, copyright 2006 - Chris Sutton and Mark Levy. - - 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 _QUICKTIME_FILE_READER_H_ -#define _QUICKTIME_FILE_READER_H_ - -#ifdef HAVE_QUICKTIME - -#include "CodedAudioFileReader.h" - -#include "base/Thread.h" - -#include - -class ProgressReporter; - -class QuickTimeFileReader : public CodedAudioFileReader -{ - Q_OBJECT - -public: - enum DecodeMode { - DecodeAtOnce, // decode the file on construction, with progress - DecodeThreaded // decode in a background thread after construction - }; - - QuickTimeFileReader(FileSource source, - DecodeMode decodeMode, - CacheMode cacheMode, - sv_samplerate_t targetRate = 0, - bool normalised = false, - ProgressReporter *reporter = 0); - 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); - static bool supportsExtension(QString ext); - static bool supportsContentType(QString type); - static bool supports(FileSource &source); - - virtual int getDecodeCompletion() const { return m_completion; } - - virtual bool isUpdating() const { - return m_decodeThread && m_decodeThread->isRunning(); - } - -public slots: - void cancelled(); - -protected: - FileSource m_source; - QString m_path; - QString m_error; - QString m_title; - - class D; - D *m_d; - - ProgressReporter *m_reporter; - bool m_cancelled; - int m_completion; - - class DecodeThread : public Thread - { - public: - DecodeThread(QuickTimeFileReader *reader) : m_reader(reader) { } - virtual void run(); - - protected: - QuickTimeFileReader *m_reader; - }; - - DecodeThread *m_decodeThread; -}; - -#endif - -#endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/WavFileReader.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -25,13 +25,17 @@ using namespace std; -WavFileReader::WavFileReader(FileSource source, bool fileUpdating) : +WavFileReader::WavFileReader(FileSource source, + bool fileUpdating, + Normalisation normalisation) : m_file(0), m_source(source), m_path(source.getLocalFilename()), m_seekable(false), m_lastStart(0), m_lastCount(0), + m_normalisation(normalisation), + m_max(0.f), m_updating(fileUpdating) { m_frameCount = 0; @@ -40,21 +44,26 @@ m_fileInfo.format = 0; m_fileInfo.frames = 0; + +#ifdef Q_OS_WIN + m_file = sf_wchar_open((LPCWSTR)m_path.utf16(), SFM_READ, &m_fileInfo); +#else m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo); +#endif if (!m_file || (!fileUpdating && m_fileInfo.channels <= 0)) { - SVDEBUG << "WavFileReader::initialize: Failed to open file at \"" + SVDEBUG << "WavFileReader::initialize: Failed to open file at \"" << m_path << "\" (" << sf_strerror(m_file) << ")" << endl; - if (m_file) { - m_error = QString("Couldn't load audio file '%1':\n%2") - .arg(m_path).arg(sf_strerror(m_file)); - } else { - m_error = QString("Failed to open audio file '%1'") - .arg(m_path); - } - return; + if (m_file) { + m_error = QString("Couldn't load audio file '%1':\n%2") + .arg(m_path).arg(sf_strerror(m_file)); + } else { + m_error = QString("Failed to open audio file '%1'") + .arg(m_path); + } + return; } if (m_fileInfo.channels > 0) { @@ -82,9 +91,13 @@ // and mark those (basically only non-adaptive WAVs). m_seekable = true; } + + if (m_normalisation != Normalisation::None && !m_updating) { + m_max = getMax(); + } } - SVDEBUG << "WavFileReader: Filename " << m_path << ", frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", format " << m_fileInfo.format << ", seekable " << m_fileInfo.seekable << " adjusted to " << m_seekable << endl; + SVDEBUG << "WavFileReader: Filename " << m_path << ", frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", format " << m_fileInfo.format << ", seekable " << m_fileInfo.seekable << " adjusted to " << m_seekable << ", normalisation " << int(m_normalisation) << endl; } WavFileReader::~WavFileReader() @@ -101,7 +114,11 @@ if (m_file) { sf_close(m_file); +#ifdef Q_OS_WIN + m_file = sf_wchar_open((LPCWSTR)m_path.utf16(), SFM_READ, &m_fileInfo); +#else m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo); +#endif if (!m_file || m_fileInfo.channels <= 0) { SVDEBUG << "WavFileReader::updateFrameCount: Failed to open file at \"" << m_path << "\" (" << sf_strerror(m_file) << ")" << endl; @@ -127,11 +144,31 @@ { updateFrameCount(); m_updating = false; + if (m_normalisation != Normalisation::None) { + m_max = getMax(); + } } -vector +floatvec_t WavFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const { + floatvec_t frames = getInterleavedFramesUnnormalised(start, count); + + if (m_normalisation == Normalisation::None || m_max == 0.f) { + return frames; + } + + for (int i = 0; in_range_for(frames, i); ++i) { + frames[i] /= m_max; + } + + return frames; +} + +floatvec_t +WavFileReader::getInterleavedFramesUnnormalised(sv_frame_t start, + sv_frame_t count) const +{ static HitCount lastRead("WavFileReader: last read"); if (count == 0) return {}; @@ -147,11 +184,11 @@ if (start >= m_fileInfo.frames) { // SVDEBUG << "WavFileReader::getInterleavedFrames: " << start // << " > " << m_fileInfo.frames << endl; - return {}; + return {}; } if (start + count > m_fileInfo.frames) { - count = m_fileInfo.frames - start; + count = m_fileInfo.frames - start; } // Because WaveFileModel::getSummaries() is called separately for @@ -175,7 +212,7 @@ return {}; } - vector data; + floatvec_t data; sv_frame_t n = count * m_fileInfo.channels; data.resize(n); @@ -191,6 +228,43 @@ return data; } +float +WavFileReader::getMax() const +{ + if (!m_file || !m_channelCount) { + return 0.f; + } + + // First try for a PEAK chunk + + double sfpeak = 0.0; + if (sf_command(m_file, SFC_GET_SIGNAL_MAX, &sfpeak, sizeof(sfpeak)) + == SF_TRUE) { + SVDEBUG << "File has a PEAK chunk reporting max level " << sfpeak + << endl; + return float(fabs(sfpeak)); + } + + // Failing that, read all the samples + + float peak = 0.f; + sv_frame_t ix = 0, chunk = 65536; + + while (ix < m_frameCount) { + auto frames = getInterleavedFrames(ix, chunk); + for (float x: frames) { + float level = fabsf(x); + if (level > peak) { + peak = level; + } + } + ix += chunk; + } + + SVDEBUG << "Measured file peak max level as " << peak << endl; + return peak; +} + void WavFileReader::getSupportedExtensions(set &extensions) { diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/WavFileReader.h --- a/data/fileio/WavFileReader.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/WavFileReader.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,11 +13,16 @@ COPYING included with this distribution for more information. */ -#ifndef _WAV_FILE_READER_H_ -#define _WAV_FILE_READER_H_ +#ifndef SV_WAV_FILE_READER_H +#define SV_WAV_FILE_READER_H #include "AudioFileReader.h" +#ifdef Q_OS_WIN +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif + #include #include @@ -36,7 +41,11 @@ class WavFileReader : public AudioFileReader { public: - WavFileReader(FileSource source, bool fileUpdating = false); + enum class Normalisation { None, Peak }; + + WavFileReader(FileSource source, + bool fileUpdating = false, + Normalisation normalise = Normalisation::None); virtual ~WavFileReader(); virtual QString getLocation() const { return m_source.getLocation(); } @@ -50,7 +59,8 @@ * Must be safe to call from multiple threads with different * arguments on the same object at the same time. */ - virtual std::vector getInterleavedFrames(sv_frame_t start, sv_frame_t count) const; + virtual floatvec_t getInterleavedFrames(sv_frame_t start, sv_frame_t count) + const; static void getSupportedExtensions(std::set &extensions); static bool supportsExtension(QString ext); @@ -75,11 +85,18 @@ bool m_seekable; mutable QMutex m_mutex; - mutable std::vector m_buffer; + mutable floatvec_t m_buffer; mutable sv_frame_t m_lastStart; mutable sv_frame_t m_lastCount; + Normalisation m_normalisation; + float m_max; + bool m_updating; + + floatvec_t getInterleavedFramesUnnormalised(sv_frame_t start, + sv_frame_t count) const; + float getMax() const; }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/WavFileWriter.cpp --- a/data/fileio/WavFileWriter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/WavFileWriter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -19,16 +19,21 @@ #include "base/Selection.h" #include "base/TempWriteFile.h" #include "base/Exceptions.h" +#include "base/Debug.h" + +#include +#include #include #include #include +#include using namespace std; WavFileWriter::WavFileWriter(QString path, - sv_samplerate_t sampleRate, + sv_samplerate_t sampleRate, int channels, FileWriteMode mode) : m_path(path), @@ -41,7 +46,7 @@ int fileRate = int(round(m_sampleRate)); if (m_sampleRate != sv_samplerate_t(fileRate)) { - cerr << "WavFileWriter: WARNING: Non-integer sample rate " + SVCERR << "WavFileWriter: WARNING: Non-integer sample rate " << m_sampleRate << " presented, rounding to " << fileRate << endl; } @@ -50,25 +55,27 @@ fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; try { + QString writePath = m_path; if (mode == WriteToTemporary) { m_temp = new TempWriteFile(m_path); - m_file = sf_open(m_temp->getTemporaryFilename().toLocal8Bit(), - SFM_WRITE, &fileInfo); - if (!m_file) { - cerr << "WavFileWriter: Failed to open file (" - << sf_strerror(m_file) << ")" << endl; - m_error = QString("Failed to open audio file '%1' for writing") - .arg(m_temp->getTemporaryFilename()); + writePath = m_temp->getTemporaryFilename(); + } +#ifdef Q_OS_WIN + m_file = sf_wchar_open((LPCWSTR)writePath.utf16(), SFM_WRITE, &fileInfo); +#else + m_file = sf_open(writePath.toLocal8Bit(), SFM_WRITE, &fileInfo); +#endif + if (!m_file) { + SVCERR << "WavFileWriter: Failed to create float-WAV file of " + << m_channels << " channels at rate " << fileRate << " (" + << sf_strerror(m_file) << ")" << endl; + m_error = QString("Failed to open audio file '%1' for writing") + .arg(writePath); + if (m_temp) { + delete m_temp; + m_temp = 0; } - } else { - m_file = sf_open(m_path.toLocal8Bit(), SFM_WRITE, &fileInfo); - if (!m_file) { - cerr << "WavFileWriter: Failed to open file (" - << sf_strerror(m_file) << ")" << endl; - m_error = QString("Failed to open audio file '%1' for writing") - .arg(m_path); - } - } + } } catch (FileOperationFailed &f) { m_error = f.what(); m_temp = 0; @@ -119,59 +126,59 @@ if (!m_file) { m_error = QString("Failed to write model to audio file '%1': File not open") .arg(getWriteFilename()); - return false; + return false; } bool ownSelection = false; if (!selection) { - selection = new MultiSelection; - selection->setSelection(Selection(source->getStartFrame(), - source->getEndFrame())); + selection = new MultiSelection; + selection->setSelection(Selection(source->getStartFrame(), + source->getEndFrame())); ownSelection = true; } sv_frame_t bs = 2048; for (MultiSelection::SelectionList::iterator i = - selection->getSelections().begin(); - i != selection->getSelections().end(); ++i) { - - sv_frame_t f0(i->getStartFrame()), f1(i->getEndFrame()); + selection->getSelections().begin(); + i != selection->getSelections().end(); ++i) { + + sv_frame_t f0(i->getStartFrame()), f1(i->getEndFrame()); - for (sv_frame_t f = f0; f < f1; f += bs) { - - sv_frame_t n = min(bs, f1 - f); - vector interleaved(n * m_channels, 0.f); + for (sv_frame_t f = f0; f < f1; f += bs) { + + sv_frame_t n = min(bs, f1 - f); + floatvec_t interleaved(n * m_channels, 0.f); - for (int c = 0; c < int(m_channels); ++c) { - vector chanbuf = source->getData(c, f, n); - for (int i = 0; in_range_for(chanbuf, i); ++i) { - interleaved[i * m_channels + c] = chanbuf[i]; - } - } + for (int c = 0; c < int(m_channels); ++c) { + auto chanbuf = source->getData(c, f, n); + for (int i = 0; in_range_for(chanbuf, i); ++i) { + interleaved[i * m_channels + c] = chanbuf[i]; + } + } - sf_count_t written = sf_writef_float(m_file, interleaved.data(), n); + sf_count_t written = sf_writef_float(m_file, interleaved.data(), n); - if (written < n) { - m_error = QString("Only wrote %1 of %2 frames at file frame %3") - .arg(written).arg(n).arg(f); - break; - } - } + if (written < n) { + m_error = QString("Only wrote %1 of %2 frames at file frame %3") + .arg(written).arg(n).arg(f); + break; + } + } } if (ownSelection) delete selection; return isOK(); } - + bool -WavFileWriter::writeSamples(float **samples, sv_frame_t count) +WavFileWriter::writeSamples(const float *const *samples, sv_frame_t count) { if (!m_file) { m_error = QString("Failed to write model to audio file '%1': File not open") .arg(getWriteFilename()); - return false; + return false; } float *b = new float[count * m_channels]; @@ -192,7 +199,20 @@ return isOK(); } - + +bool +WavFileWriter::putInterleavedFrames(const floatvec_t &frames) +{ + sv_frame_t count = frames.size() / m_channels; + float **samples = + breakfastquay::allocate_channels(m_channels, count); + breakfastquay::v_deinterleave + (samples, frames.data(), m_channels, int(count)); + bool result = writeSamples(samples, count); + breakfastquay::deallocate_channels(samples, m_channels); + return result; +} + bool WavFileWriter::close() { diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/WavFileWriter.h --- a/data/fileio/WavFileWriter.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/WavFileWriter.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,11 +13,16 @@ COPYING included with this distribution for more information. */ -#ifndef _WAV_FILE_WRITER_H_ -#define _WAV_FILE_WRITER_H_ +#ifndef SV_WAV_FILE_WRITER_H +#define SV_WAV_FILE_WRITER_H #include +#ifdef Q_OS_WIN +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif + #include #include "base/BaseTypes.h" @@ -59,7 +64,11 @@ bool writeModel(DenseTimeValueModel *source, MultiSelection *selection = 0); - bool writeSamples(float **samples, sv_frame_t count); // count per channel + /// Write samples from raw arrays; count is per-channel + bool writeSamples(const float *const *samples, sv_frame_t count); + + /// As writeSamples, but compatible with WavFileReader api. More expensive. + bool putInterleavedFrames(const floatvec_t &frames); bool close(); diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/AudioFileReaderTest.h --- a/data/fileio/test/AudioFileReaderTest.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/test/AudioFileReaderTest.h Mon Sep 17 13:51:14 2018 +0100 @@ -32,13 +32,26 @@ using namespace std; -static QString audioDir = "svcore/data/fileio/test/testfiles"; -static QString diffDir = "svcore/data/fileio/test/diffs"; - class AudioFileReaderTest : public QObject { Q_OBJECT +private: + QString testDirBase; + QString audioDir; + QString diffDir; + +public: + AudioFileReaderTest(QString base) { + if (base == "") { + base = "svcore/data/fileio/test"; + } + testDirBase = base; + audioDir = base + "/audio"; + diffDir = base + "/diffs"; + } + +private: const char *strOf(QString s) { return strdup(s.toLocal8Bit().data()); } @@ -174,11 +187,12 @@ void init() { if (!QDir(audioDir).exists()) { - cerr << "ERROR: Audio test file directory \"" << audioDir << "\" does not exist" << endl; + QString cwd = QDir::currentPath(); + SVCERR << "ERROR: Audio test file directory \"" << audioDir << "\" does not exist (cwd = " << cwd << ")" << endl; QVERIFY2(QDir(audioDir).exists(), "Audio test file directory not found"); } if (!QDir(diffDir).exists() && !QDir().mkpath(diffDir)) { - cerr << "ERROR: Audio diff directory \"" << diffDir << "\" does not exist and could not be created" << endl; + SVCERR << "ERROR: Audio diff directory \"" << diffDir << "\" does not exist and could not be created" << endl; QVERIFY2(QDir(diffDir).exists(), "Audio diff directory not found and could not be created"); } } @@ -240,17 +254,17 @@ AudioFileReaderFactory::GaplessMode::Gapless : AudioFileReaderFactory::GaplessMode::Gappy); - AudioFileReader *reader = - AudioFileReaderFactory::createReader - (audioDir + "/" + format + "/" + audiofile, params); + AudioFileReader *reader = + AudioFileReaderFactory::createReader + (audioDir + "/" + format + "/" + audiofile, params); - if (!reader) { + if (!reader) { #if ( QT_VERSION >= 0x050000 ) - QSKIP("Unsupported file, skipping"); + QSKIP("Unsupported file, skipping"); #else - QSKIP("Unsupported file, skipping", SkipSingle); + QSKIP("Unsupported file, skipping", SkipSingle); #endif - } + } QString extension; sv_samplerate_t fileRate; @@ -262,18 +276,22 @@ QCOMPARE(reader->getNativeRate(), fileRate); QCOMPARE(reader->getSampleRate(), readRate); - AudioTestData tdata(readRate, channels); - - float *reference = tdata.getInterleavedData(); + AudioTestData tdata(readRate, channels); + + float *reference = tdata.getInterleavedData(); sv_frame_t refFrames = tdata.getFrameCount(); - - // The reader should give us exactly the expected number of - // frames, except for mp3/aac files. We ask for quite a lot - // more, though, so we can (a) check that we only get the - // expected number back (if this is not mp3/aac) or (b) take - // into account silence at beginning and end (if it is). - vector test = reader->getInterleavedFrames(0, refFrames + 5000); - sv_frame_t read = test.size() / channels; + + // The reader should give us exactly the expected number of + // frames, except for mp3/aac files. We ask for quite a lot + // more, though, so we can (a) check that we only get the + // expected number back (if this is not mp3/aac) or (b) take + // into account silence at beginning and end (if it is). + floatvec_t test = reader->getInterleavedFrames(0, refFrames + 5000); + + delete reader; + reader = 0; + + sv_frame_t read = test.size() / channels; bool perceptual = (extension == "mp3" || extension == "aac" || @@ -375,7 +393,7 @@ diffFile += ".wav"; diffFile = QDir(diffDir).filePath(diffFile); WavFileWriter diffWriter(diffFile, readRate, channels, - WavFileWriter::WriteToTarget); //!!! NB WriteToTemporary not working, why? + WavFileWriter::WriteToTemporary); QVERIFY(diffWriter.isOK()); vector> diffs(channels); @@ -398,17 +416,17 @@ delete[] ptrs; } - for (int c = 0; c < channels; ++c) { + for (int c = 0; c < channels; ++c) { double maxDiff = 0.0; double totalDiff = 0.0; double totalSqrDiff = 0.0; - int maxIndex = 0; + int maxIndex = 0; - for (int i = 0; i < refFrames; ++i) { + for (int i = 0; i < refFrames; ++i) { int ix = i + offset; if (ix >= read) { - cerr << "ERROR: audiofile " << audiofile << " reads truncated (read-rate reference frames " << i << " onward, of " << refFrames << ", are lost)" << endl; + SVCERR << "ERROR: audiofile " << audiofile << " reads truncated (read-rate reference frames " << i << " onward, of " << refFrames << ", are lost)" << endl; QVERIFY(ix < read); } @@ -418,10 +436,10 @@ continue; } - double diff = fabs(test[ix * channels + c] - + double diff = fabs(test[ix * channels + c] - reference[i * channels + c]); - totalDiff += diff; + totalDiff += diff; totalSqrDiff += diff * diff; // in edge areas, record this only if it exceeds edgeLimit @@ -435,25 +453,25 @@ maxDiff = diff; maxIndex = i; } - } - } + } + } - double meanDiff = totalDiff / double(refFrames); + double meanDiff = totalDiff / double(refFrames); double rmsDiff = sqrt(totalSqrDiff / double(refFrames)); /* - cerr << "channel " << c << ": mean diff " << meanDiff << endl; - cerr << "channel " << c << ": rms diff " << rmsDiff << endl; - cerr << "channel " << c << ": max diff " << maxDiff << " at " << maxIndex << endl; + cerr << "channel " << c << ": mean diff " << meanDiff << endl; + cerr << "channel " << c << ": rms diff " << rmsDiff << endl; + cerr << "channel " << c << ": max diff " << maxDiff << " at " << maxIndex << endl; */ if (rmsDiff >= rmsLimit) { - cerr << "ERROR: for audiofile " << audiofile << ": RMS diff = " << rmsDiff << " for channel " << c << " (limit = " << rmsLimit << ")" << endl; + SVCERR << "ERROR: for audiofile " << audiofile << ": RMS diff = " << rmsDiff << " for channel " << c << " (limit = " << rmsLimit << ")" << endl; QVERIFY(rmsDiff < rmsLimit); } - if (maxDiff >= maxLimit) { - cerr << "ERROR: for audiofile " << audiofile << ": max diff = " << maxDiff << " at frame " << maxIndex << " of " << read << " on channel " << c << " (limit = " << maxLimit << ", edge limit = " << edgeLimit << ", mean diff = " << meanDiff << ", rms = " << rmsDiff << ")" << endl; - QVERIFY(maxDiff < maxLimit); - } + if (maxDiff >= maxLimit) { + SVCERR << "ERROR: for audiofile " << audiofile << ": max diff = " << maxDiff << " at frame " << maxIndex << " of " << read << " on channel " << c << " (limit = " << maxLimit << ", edge limit = " << edgeLimit << ", mean diff = " << meanDiff << ", rms = " << rmsDiff << ")" << endl; + QVERIFY(maxDiff < maxLimit); + } // and check for spurious material at end @@ -462,11 +480,11 @@ float quiet = 0.1f; //!!! allow some ringing - but let's come back to this, it should tail off float mag = fabsf(test[ix * channels + c]); if (mag > quiet) { - cerr << "ERROR: audiofile " << audiofile << " contains spurious data after end of reference (found sample " << test[ix * channels + c] << " at index " << ix << " of channel " << c << " after reference+offset ended at " << refFrames+offset << ")" << endl; + SVCERR << "ERROR: audiofile " << audiofile << " contains spurious data after end of reference (found sample " << test[ix * channels + c] << " at index " << ix << " of channel " << c << " after reference+offset ended at " << refFrames+offset << ")" << endl; QVERIFY(mag < quiet); } } - } + } } }; diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/AudioFileWriterTest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/AudioFileWriterTest.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,135 @@ +/* -*- 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 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 TEST_AUDIO_FILE_WRITER_H +#define TEST_AUDIO_FILE_WRITER_H + +#include "../AudioFileReaderFactory.h" +#include "../AudioFileReader.h" +#include "../WavFileWriter.h" + +#include "AudioTestData.h" + +#include "bqvec/VectorOps.h" +#include "bqvec/Allocators.h" + +#include + +#include +#include +#include + +#include + +using namespace std; +using namespace breakfastquay; + +class AudioFileWriterTest : public QObject +{ + Q_OBJECT + +private: + QString testDirBase; + QString outDir; + + static const int rate = 44100; + +public: + AudioFileWriterTest(QString base) { + if (base == "") { + base = "svcore/data/fileio/test"; + } + testDirBase = base; + outDir = base + "/outfiles"; + } + + const char *strOf(QString s) { + return strdup(s.toLocal8Bit().data()); + } + + QString testName(bool direct, int channels) { + return QString("%1 %2 %3") + .arg(channels) + .arg(channels > 1 ? "channels" : "channel") + .arg(direct ? "direct" : "via temporary"); + } + +private slots: + void init() + { + if (!QDir(outDir).exists() && !QDir().mkpath(outDir)) { + SVCERR << "ERROR: Audio out directory \"" << outDir << "\" does not exist and could not be created" << endl; + QVERIFY2(QDir(outDir).exists(), "Audio out directory not found and could not be created"); + } + } + + void write_data() + { + QTest::addColumn("direct"); + QTest::addColumn("channels"); + for (int direct = 0; direct <= 1; ++direct) { + for (int channels = 1; channels < 8; ++channels) { + if (channels == 1 || channels == 2 || + channels == 5 || channels == 8) { + QString desc = testName(direct, channels); + QTest::newRow(strOf(desc)) << (bool)direct << channels; + } + } + } + } + + void write() + { + QFETCH(bool, direct); + QFETCH(int, channels); + + QString outfile = QString("%1/out-%2ch-%3.wav") + .arg(outDir).arg(channels).arg(direct ? "direct" : "via-temporary"); + + WavFileWriter writer(outfile, + rate, + channels, + direct ? + WavFileWriter::WriteToTarget : + WavFileWriter::WriteToTemporary); + QVERIFY(writer.isOK()); + + AudioTestData data(rate, channels); + data.generate(); + + sv_frame_t frameCount = data.getFrameCount(); + float *interleaved = data.getInterleavedData(); + float **nonInterleaved = allocate_channels(channels, frameCount); + v_deinterleave(nonInterleaved, interleaved, channels, int(frameCount)); + bool ok = writer.writeSamples(nonInterleaved, frameCount); + deallocate_channels(nonInterleaved, channels); + QVERIFY(ok); + + ok = writer.close(); + QVERIFY(ok); + + AudioFileReaderFactory::Parameters params; + AudioFileReader *rereader = + AudioFileReaderFactory::createReader(outfile, params); + QVERIFY(rereader != nullptr); + + floatvec_t readFrames = rereader->getInterleavedFrames(0, frameCount); + floatvec_t expected(interleaved, interleaved + frameCount * channels); + QCOMPARE(readFrames, expected); + + delete rereader; + } +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/AudioTestData.h --- a/data/fileio/test/AudioTestData.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/test/AudioTestData.h Mon Sep 17 13:51:14 2018 +0100 @@ -38,74 +38,74 @@ { public: AudioTestData(double rate, int channels) : - m_channelCount(channels), - m_duration(2.0), - m_sampleRate(rate), - m_sinFreq(600.0), - m_pulseFreq(2) + m_channelCount(channels), + m_duration(2.0), + m_sampleRate(rate), + m_sinFreq(600.0), + m_pulseFreq(2) { - m_frameCount = lrint(m_duration * m_sampleRate); - m_data = new float[m_frameCount * m_channelCount]; - m_pulseWidth = 0.01 * m_sampleRate; - generate(); + m_frameCount = lrint(m_duration * m_sampleRate); + m_data = new float[m_frameCount * m_channelCount]; + m_pulseWidth = 0.01 * m_sampleRate; + generate(); } ~AudioTestData() { - delete[] m_data; + delete[] m_data; } void generate() { - double hpw = m_pulseWidth / 2.0; + double hpw = m_pulseWidth / 2.0; - for (int i = 0; i < m_frameCount; ++i) { - for (int c = 0; c < m_channelCount; ++c) { + for (int i = 0; i < m_frameCount; ++i) { + for (int c = 0; c < m_channelCount; ++c) { - double s = 0.0; + double s = 0.0; - if (c == 0) { + if (c == 0) { - double phase = (i * m_sinFreq * 2.0 * M_PI) / m_sampleRate; - s = sin(phase); + double phase = (i * m_sinFreq * 2.0 * M_PI) / m_sampleRate; + s = sin(phase); - } else if (c == 1) { + } else if (c == 1) { - int pulseNo = int((i * m_pulseFreq) / m_sampleRate); - int index = int(round((i * m_pulseFreq) - + int pulseNo = int((i * m_pulseFreq) / m_sampleRate); + int index = int(round((i * m_pulseFreq) - (m_sampleRate * pulseNo))); - if (index < m_pulseWidth) { - s = 1.0 - fabs(hpw - index) / hpw; - if (pulseNo % 2) s = -s; - } + if (index < m_pulseWidth) { + s = 1.0 - fabs(hpw - index) / hpw; + if (pulseNo % 2) s = -s; + } - } else { + } else { - s = c / 20.0; - } + s = c / 20.0; + } - m_data[i * m_channelCount + c] = float(s); - } - } + m_data[i * m_channelCount + c] = float(s); + } + } } float *getInterleavedData() const { - return m_data; + return m_data; } sv_frame_t getFrameCount() const { - return m_frameCount; + return m_frameCount; } int getChannelCount() const { - return m_channelCount; + return m_channelCount; } sv_samplerate_t getSampleRate () const { - return m_sampleRate; + return m_sampleRate; } double getDuration() const { // seconds - return m_duration; + return m_duration; } private: diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/CSVFormatTest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/CSVFormatTest.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,276 @@ +/* -*- 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 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 TEST_CSV_FORMAT_H +#define TEST_CSV_FORMAT_H + +// Tests for the code that guesses the most likely format for parsing a CSV file + +#include "../CSVFormat.h" + +#include "base/Debug.h" + +#include + +#include +#include +#include + +#include + +using namespace std; + +class CSVFormatTest : public QObject +{ + Q_OBJECT + +private: + QDir csvDir; + +public: + CSVFormatTest(QString base) { + if (base == "") { + base = "svcore/data/fileio/test"; + } + csvDir = QDir(base + "/csv"); + } + +private slots: + void init() { + if (!csvDir.exists()) { + SVCERR << "ERROR: CSV test file directory \"" << csvDir.absolutePath() << "\" does not exist" << endl; + QVERIFY2(csvDir.exists(), "CSV test file directory not found"); + } + } + + void separatorComma() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("separator-comma.csv"))); + QCOMPARE(f.getSeparator(), QChar(',')); + QCOMPARE(f.getColumnCount(), 3); + } + + void separatorTab() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("separator-tab.csv"))); + QCOMPARE(f.getSeparator(), QChar('\t')); + QCOMPARE(f.getColumnCount(), 3); + } + + void separatorPipe() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("separator-pipe.csv"))); + QCOMPARE(f.getSeparator(), QChar('|')); + // differs from the others + QCOMPARE(f.getColumnCount(), 4); + } + + void separatorSpace() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("separator-space.csv"))); + QCOMPARE(f.getSeparator(), QChar(' ')); + // NB fields are separated by 1 or more spaces, not necessarily exactly 1 + QCOMPARE(f.getColumnCount(), 3); + } + + void separatorColon() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("separator-colon.csv"))); + QCOMPARE(f.getSeparator(), QChar(':')); + QCOMPARE(f.getColumnCount(), 3); + } + + void comment() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("comment.csv"))); + QCOMPARE(f.getSeparator(), QChar(',')); + QCOMPARE(f.getColumnCount(), 4); + } + + void qualities() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("column-qualities.csv"))); + QCOMPARE(f.getSeparator(), QChar(',')); + QCOMPARE(f.getColumnCount(), 7); + QList q = f.getColumnQualities(); + QList expected; + expected << 0; + expected << CSVFormat::ColumnQualities(CSVFormat::ColumnNumeric | + CSVFormat::ColumnIntegral | + CSVFormat::ColumnIncreasing); + expected << CSVFormat::ColumnQualities(CSVFormat::ColumnNumeric | + CSVFormat::ColumnIntegral | + CSVFormat::ColumnIncreasing | + CSVFormat::ColumnLarge); + expected << CSVFormat::ColumnQualities(CSVFormat::ColumnNumeric); + expected << CSVFormat::ColumnQualities(CSVFormat::ColumnNumeric | + CSVFormat::ColumnIncreasing); + expected << CSVFormat::ColumnQualities(CSVFormat::ColumnNumeric | + CSVFormat::ColumnSmall | + CSVFormat::ColumnSigned); + expected << CSVFormat::ColumnQualities(CSVFormat::ColumnNumeric | + CSVFormat::ColumnIntegral | + CSVFormat::ColumnIncreasing | + CSVFormat::ColumnNearEmpty); + QCOMPARE(q, expected); + } + + void modelType1DSamples() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-1d-samples.csv"))); + QCOMPARE(f.getColumnCount(), 1); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeAudioFrames); + QCOMPARE(f.getModelType(), CSVFormat::OneDimensionalModel); + } + + void modelType1DSeconds() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-1d-seconds.csv"))); + QCOMPARE(f.getColumnCount(), 2); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnLabel); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeSeconds); + QCOMPARE(f.getModelType(), CSVFormat::OneDimensionalModel); + } + + void modelType2DSamples() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-samples.csv"))); + QCOMPARE(f.getColumnCount(), 2); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeAudioFrames); + QCOMPARE(f.getModelType(), CSVFormat::TwoDimensionalModel); + } + + void modelType2DSeconds() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-seconds.csv"))); + QCOMPARE(f.getColumnCount(), 2); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeSeconds); + QCOMPARE(f.getModelType(), CSVFormat::TwoDimensionalModel); + } + + void modelType2DImplicit() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-implicit.csv"))); + QCOMPARE(f.getColumnCount(), 1); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ImplicitTiming); + } + + void modelType2DEndTimeSamples() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-endtime-samples.csv"))); + QCOMPARE(f.getColumnCount(), 3); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnEndTime); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeAudioFrames); + QCOMPARE(f.getModelType(), CSVFormat::TwoDimensionalModelWithDuration); + } + + void modelType2DEndTimeSeconds() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-endtime-seconds.csv"))); + QCOMPARE(f.getColumnCount(), 3); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnEndTime); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeSeconds); + QCOMPARE(f.getModelType(), CSVFormat::TwoDimensionalModelWithDuration); + } + + void modelType2DDurationSamples() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-duration-samples.csv"))); + QCOMPARE(f.getColumnCount(), 3); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnDuration); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeAudioFrames); + QCOMPARE(f.getModelType(), CSVFormat::TwoDimensionalModelWithDuration); + } + + void modelType2DDurationSeconds() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-2d-duration-seconds.csv"))); + QCOMPARE(f.getColumnCount(), 3); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnDuration); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeSeconds); + QCOMPARE(f.getModelType(), CSVFormat::TwoDimensionalModelWithDuration); + } + + void modelType3DSamples() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-3d-samples.csv"))); + QCOMPARE(f.getColumnCount(), 7); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(3), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(4), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(5), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(6), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeAudioFrames); + QCOMPARE(f.getModelType(), CSVFormat::ThreeDimensionalModel); + } + + void modelType3DSeconds() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-3d-seconds.csv"))); + QCOMPARE(f.getColumnCount(), 7); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnStartTime); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(3), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(4), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(5), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(6), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ExplicitTiming); + QCOMPARE(f.getTimeUnits(), CSVFormat::TimeSeconds); + QCOMPARE(f.getModelType(), CSVFormat::ThreeDimensionalModel); + } + + void modelType3DImplicit() { + CSVFormat f; + QVERIFY(f.guessFormatFor(csvDir.filePath("model-type-3d-implicit.csv"))); + QCOMPARE(f.getColumnCount(), 6); + QCOMPARE(f.getColumnPurpose(0), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(1), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(2), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(3), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(4), CSVFormat::ColumnValue); + QCOMPARE(f.getColumnPurpose(5), CSVFormat::ColumnValue); + QCOMPARE(f.getTimingType(), CSVFormat::ImplicitTiming); + QCOMPARE(f.getModelType(), CSVFormat::ThreeDimensionalModel); + } + +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/CSVStreamWriterTest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/CSVStreamWriterTest.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,328 @@ +/* -*- 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 2017 Queen Mary, University of London. + + 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 TEST_CSV_STREAM_H +#define TEST_CSV_STREAM_H + +#include +#include +#include +#include + +#include "base/ProgressReporter.h" +#include "base/DataExportOptions.h" +#include "base/Selection.h" +#include "data/model/NoteModel.h" +#include "../CSVStreamWriter.h" +#include "../../model/test/MockWaveModel.h" + +class StubReporter : public ProgressReporter +{ +public: + StubReporter( std::function isCancelled ) + : m_isCancelled(isCancelled) {} + bool isDefinite() const override { return true; } + void setDefinite(bool) override {} + bool wasCancelled() const override { return m_isCancelled(); } + void setMessage(QString) override {} + void setProgress(int p) override + { + ++m_calls; + m_percentageLog.push_back(p); + } + + size_t getCallCount() const { return m_calls; } + std::vector getPercentageLog() const { return m_percentageLog; } + void reset() { m_calls = 0; } +private: + size_t m_calls = 0; + std::function m_isCancelled; + std::vector m_percentageLog; +}; + +class CSVStreamWriterTest : public QObject +{ + Q_OBJECT +public: + std::string getExpectedString() + { + return + { + "0,0,0\n" + "1,0,0\n" + "2,0,0\n" + "3,0,0\n" + "4,1,1\n" + "5,1,1\n" + "6,1,1\n" + "7,1,1\n" + "8,1,1\n" + "9,1,1\n" + "10,1,1\n" + "11,1,1\n" + "12,1,1\n" + "13,1,1\n" + "14,1,1\n" + "15,1,1\n" + "16,1,1\n" + "17,1,1\n" + "18,1,1\n" + "19,1,1\n" + "20,0,0\n" + "21,0,0\n" + "22,0,0\n" + "23,0,0" + }; + } + +private slots: + void simpleValidOutput() + { + MockWaveModel mwm({ DC, DC }, 16, 4); + + std::ostringstream oss; + const auto result = CSVStreamWriter::writeInChunks(oss, mwm); + QVERIFY( oss.str() == getExpectedString() ); + QVERIFY( result ); + } + + void callsReporterCorrectTimes() + { + MockWaveModel mwm({ DC, DC }, 16, 4); + StubReporter reporter { []() -> bool { return false; } }; + const auto expected = getExpectedString(); + + std::ostringstream oss; + const auto writeStreamWithBlockSize = [&](int blockSize) { + return CSVStreamWriter::writeInChunks( + oss, + mwm, + &reporter, + ",", + DataExportDefaults, + blockSize + ); + }; + + const auto reset = [&]() { + oss.str({}); + reporter.reset(); + }; + + const auto nonIntegerMultipleResult = writeStreamWithBlockSize(5); + QVERIFY( nonIntegerMultipleResult ); + QVERIFY( reporter.getCallCount() == 5 /* 4.8 rounded up */ ); + QVERIFY( oss.str() == expected ); + reset(); + + const auto integerMultiple = writeStreamWithBlockSize(2); + QVERIFY( integerMultiple ); + QVERIFY( reporter.getCallCount() == 12 ); + QVERIFY( oss.str() == expected ); + reset(); + + const auto largerThanNumberOfSamples = writeStreamWithBlockSize(100); + QVERIFY( largerThanNumberOfSamples ); + QVERIFY( reporter.getCallCount() == 1 ); + QVERIFY( oss.str() == expected ); + reset(); + + const auto zero = writeStreamWithBlockSize(0); + QVERIFY( zero == false ); + QVERIFY( reporter.getCallCount() == 0 ); + } + + void isCancellable() + { + MockWaveModel mwm({ DC, DC }, 16, 4); + StubReporter reporter { []() -> bool { return true; } }; + + std::ostringstream oss; + const auto cancelImmediately = CSVStreamWriter::writeInChunks( + oss, + mwm, + &reporter, + ",", + DataExportDefaults, + 4 + ); + QVERIFY( cancelImmediately == false ); + QVERIFY( reporter.getCallCount() == 0 ); + + StubReporter cancelMidway { + [&]() { return cancelMidway.getCallCount() == 3; } + }; + const auto cancelledMidway = CSVStreamWriter::writeInChunks( + oss, + mwm, + &cancelMidway, + ",", + DataExportDefaults, + 4 + ); + QVERIFY( cancelMidway.getCallCount() == 3 ); + QVERIFY( cancelledMidway == false ); + } + + void zeroStartTimeReportsPercentageCorrectly() + { + MockWaveModel mwm({ DC, DC }, 16, 4); + StubReporter reporter { []() -> bool { return false; } }; + std::ostringstream oss; + const auto succeeded = CSVStreamWriter::writeInChunks( + oss, + mwm, + &reporter, + ",", + DataExportDefaults, + 4 + ); + QVERIFY( succeeded == true ); + QVERIFY( reporter.getCallCount() == 6 ); + const std::vector expectedCallLog { + 16, + 33, + 50, + 66, + 83, + 100 + }; + QVERIFY( reporter.getPercentageLog() == expectedCallLog ); + QVERIFY( oss.str() == getExpectedString() ); + } + + void nonZeroStartTimeReportsPercentageCorrectly() + { + MockWaveModel mwm({ DC, DC }, 16, 4); + StubReporter reporter { []() -> bool { return false; } }; + std::ostringstream oss; + const auto writeSubSection = CSVStreamWriter::writeInChunks( + oss, + mwm, + {4, 20}, + &reporter, + ",", + DataExportDefaults, + 4 + ); + QVERIFY( reporter.getCallCount() == 4 ); + const std::vector expectedCallLog { + 25, + 50, + 75, + 100 + }; + QVERIFY( reporter.getPercentageLog() == expectedCallLog ); + QVERIFY( writeSubSection == true ); + const std::string expectedOutput { + "4,1,1\n" + "5,1,1\n" + "6,1,1\n" + "7,1,1\n" + "8,1,1\n" + "9,1,1\n" + "10,1,1\n" + "11,1,1\n" + "12,1,1\n" + "13,1,1\n" + "14,1,1\n" + "15,1,1\n" + "16,1,1\n" + "17,1,1\n" + "18,1,1\n" + "19,1,1" + }; + QVERIFY( oss.str() == expectedOutput ); + } + + void multipleSelectionOutput() + { + MockWaveModel mwm({ DC, DC }, 16, 4); + StubReporter reporter { []() -> bool { return false; } }; + std::ostringstream oss; + MultiSelection regions; + regions.addSelection({0, 2}); + regions.addSelection({4, 6}); + regions.addSelection({16, 18}); +// qDebug("End frame: %lld", (long long int)mwm.getEndFrame()); + const std::string expectedOutput { + "0,0,0\n" + "1,0,0\n" + "4,1,1\n" + "5,1,1\n" + "16,1,1\n" + "17,1,1" + }; + const auto wroteMultiSection = CSVStreamWriter::writeInChunks( + oss, + mwm, + regions, + &reporter, + ",", + DataExportDefaults, + 2 + ); + QVERIFY( wroteMultiSection == true ); + QVERIFY( reporter.getCallCount() == 3 ); + const std::vector expectedCallLog { 33, 66, 100 }; + QVERIFY( reporter.getPercentageLog() == expectedCallLog ); +// qDebug("%s", oss.str().c_str()); + QVERIFY( oss.str() == expectedOutput ); + } + + void writeSparseModel() + { + const auto pentatonicFromRoot = [](float midiPitch) { + return std::vector { + 0 + midiPitch, + 2 + midiPitch, + 4 + midiPitch, + 7 + midiPitch, + 9 + midiPitch + }; + }; + const auto cMajorPentatonic = pentatonicFromRoot(60.0); + NoteModel notes(8 /* sampleRate */, 4 /* resolution */); + sv_frame_t startFrame = 0; + for (const auto& note : cMajorPentatonic) { + notes.addPoint({startFrame, note, 4, 1.f, ""}); + startFrame += 8; + } +// qDebug("Create Expected Output\n"); + + // NB. removed end line break + const auto expectedOutput = notes.toDelimitedDataString(",").trimmed(); + + StubReporter reporter { []() -> bool { return false; } }; + std::ostringstream oss; +// qDebug("End frame: %lld", (long long int)notes.getEndFrame()); +// qDebug("Write streaming\n"); + const auto wroteSparseModel = CSVStreamWriter::writeInChunks( + oss, + notes, + &reporter, + ",", + DataExportDefaults, + 2 + ); + +// qDebug("\n%s\n", expectedOutput.toLocal8Bit().data()); +// qDebug("\n%s\n", oss.str().c_str()); + QVERIFY( wroteSparseModel == true ); + QVERIFY( oss.str() == expectedOutput.toStdString() ); + } +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/EncodingTest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/EncodingTest.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,267 @@ +/* -*- 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 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 TEST_AUDIO_ENCODINGS_H +#define TEST_AUDIO_ENCODINGS_H + +// Quick tests for filename encodings and encoding of ID3 data. Not a +// test of audio codecs. + +#include "../AudioFileReaderFactory.h" +#include "../AudioFileReader.h" +#include "../WavFileWriter.h" + +#include + +#include +#include +#include + +#include + +using namespace std; + +const char utf8_name_cdp_1[] = "Caf\303\251 de Paris"; +const char utf8_name_cdp_2[] = "Caf\303\251 de \351\207\215\345\272\206"; +const char utf8_name_tsprk[] = "T\303\253mple of Sp\303\266rks"; +const char utf8_name_sprkt[] = "\343\202\271\343\203\235\343\203\274\343\202\257\343\201\256\345\257\272\351\231\242"; + +// Mapping between filename and expected title metadata field +static const char *mapping[][2] = { + { "id3v2-iso-8859-1", utf8_name_cdp_1 }, + { "id3v2-ucs-2", utf8_name_cdp_2 }, + { utf8_name_tsprk, utf8_name_tsprk }, + { utf8_name_sprkt, utf8_name_sprkt }, +}; +static const int mappingCount = 4; + +class EncodingTest : public QObject +{ + Q_OBJECT + +private: + QString testDirBase; + QString encodingDir; + QString outDir; + +public: + EncodingTest(QString base) { + if (base == "") { + base = "svcore/data/fileio/test"; + } + testDirBase = base; + encodingDir = base + "/encodings"; + outDir = base + "/outfiles"; + } + +private: + const char *strOf(QString s) { + return strdup(s.toLocal8Bit().data()); + } + + void addAudioFiles() { + QTest::addColumn("audiofile"); + QStringList files = QDir(encodingDir).entryList(QDir::Files); + foreach (QString filename, files) { + QTest::newRow(strOf(filename)) << filename; + } + } + +private slots: + void init() + { + if (!QDir(encodingDir).exists()) { + SVCERR << "ERROR: Audio encoding file directory \"" << encodingDir << "\" does not exist" << endl; + QVERIFY2(QDir(encodingDir).exists(), "Audio encoding file directory not found"); + } + if (!QDir(outDir).exists() && !QDir().mkpath(outDir)) { + SVCERR << "ERROR: Audio out directory \"" << outDir << "\" does not exist and could not be created" << endl; + QVERIFY2(QDir(outDir).exists(), "Audio out directory not found and could not be created"); + } + } + + void readAudio_data() { + addAudioFiles(); + } + + void readAudio() { + + // Ensure that we can open all the files + + QFETCH(QString, audiofile); + + AudioFileReaderFactory::Parameters params; + AudioFileReader *reader = + AudioFileReaderFactory::createReader + (encodingDir + "/" + audiofile, params); + + QVERIFY(reader != nullptr); + + delete reader; + } + + void readMetadata_data() { + addAudioFiles(); + } + + void readMetadata() { + + // All files other than WAVs should have title metadata; check + // that the title matches whatever is in our mapping structure + // defined at the top + + QFETCH(QString, audiofile); + + AudioFileReaderFactory::Parameters params; + AudioFileReader *reader = + AudioFileReaderFactory::createReader + (encodingDir + "/" + audiofile, params); + + QVERIFY(reader != nullptr); + + QStringList fileAndExt = audiofile.split("."); + QString file = fileAndExt[0]; + QString extension = fileAndExt[1]; + + if (extension == "wav") { + + // Nothing + + delete reader; + + } else { + +#if (!defined (HAVE_OGGZ) || !defined(HAVE_FISHSOUND)) + if (extension == "ogg") { + QSKIP("Lack native Ogg Vorbis reader, so won't be getting metadata"); + } +#endif + + auto blah = reader->getInterleavedFrames(0, 10); + + QString title = reader->getTitle(); + QVERIFY(title != QString()); + + delete reader; + + bool found = false; + for (int m = 0; m < mappingCount; ++m) { + if (file == QString::fromUtf8(mapping[m][0])) { + found = true; + QString expected = QString::fromUtf8(mapping[m][1]); + if (title != expected) { + SVCERR << "Title does not match expected: codepoints are" << endl; + SVCERR << "Title (" << title.length() << "ch): "; + for (int i = 0; i < title.length(); ++i) { + SVCERR << title[i].unicode() << " "; + } + SVCERR << endl; + SVCERR << "Expected (" << expected.length() << "ch): "; + for (int i = 0; i < expected.length(); ++i) { + SVCERR << expected[i].unicode() << " "; + } + SVCERR << endl; + } + QCOMPARE(title, expected); + break; + } + } + + if (!found) { + // Note that this can happen legitimately on Windows, + // where (for annoying VCS-related reasons) the test + // files may have a different filename encoding from + // the expected UTF-16. We check this properly in + // readWriteAudio below, by saving out the file to a + // name matching the metadata + SVCERR << "Couldn't find filename \"" + << file << "\" in title mapping array" << endl; + QSKIP("Couldn't find filename in title mapping array"); + } + } + } + + void readWriteAudio_data() { + addAudioFiles(); + } + + void readWriteAudio() + { + // For those files that have title metadata (i.e. all of them + // except the WAVs), read the title metadata and write a wav + // file (of arbitrary content) whose name matches that. Then + // check that we can re-read it. This is intended to exercise + // systems on which the original test filename is miscoded (as + // can happen on Windows). + + QFETCH(QString, audiofile); + + QStringList fileAndExt = audiofile.split("."); + QString file = fileAndExt[0]; + QString extension = fileAndExt[1]; + + if (extension == "wav") { + return; + } + +#if (!defined (HAVE_OGGZ) || !defined(HAVE_FISHSOUND)) + if (extension == "ogg") { + QSKIP("Lack native Ogg Vorbis reader, so won't be getting metadata"); + } +#endif + + AudioFileReaderFactory::Parameters params; + AudioFileReader *reader = + AudioFileReaderFactory::createReader + (encodingDir + "/" + audiofile, params); + QVERIFY(reader != nullptr); + + QString title = reader->getTitle(); + QVERIFY(title != QString()); + + for (int useTemporary = 0; useTemporary <= 1; ++useTemporary) { + + QString outfile = outDir + "/" + file + ".wav"; + WavFileWriter writer(outfile, + reader->getSampleRate(), + 1, + useTemporary ? + WavFileWriter::WriteToTemporary : + WavFileWriter::WriteToTarget); + + QVERIFY(writer.isOK()); + + floatvec_t data { 0.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, -1.0 }; + const float *samples = data.data(); + bool ok = writer.writeSamples(&samples, 8); + QVERIFY(ok); + + ok = writer.close(); + QVERIFY(ok); + + AudioFileReader *rereader = + AudioFileReaderFactory::createReader(outfile, params); + QVERIFY(rereader != nullptr); + + floatvec_t readFrames = rereader->getInterleavedFrames(0, 8); + QCOMPARE(readFrames, data); + + delete rereader; + } + + delete reader; + } +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/MIDIFileReaderTest.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/MIDIFileReaderTest.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,88 @@ +/* -*- 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 2013 Chris Cannam. + + 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 TEST_MIDI_FILE_READER_H +#define TEST_MIDI_FILE_READER_H + +#include "../MIDIFileReader.h" + +#include + +#include +#include +#include + +#include "base/Debug.h" + +#include + +using namespace std; + +class MIDIFileReaderTest : public QObject +{ + Q_OBJECT + +private: + QString testDirBase; + QString midiDir; + + const char *strOf(QString s) { + return strdup(s.toLocal8Bit().data()); + } + +public: + MIDIFileReaderTest(QString base) { + if (base == "") { + base = "svcore/data/fileio/test"; + } + testDirBase = base; + midiDir = base + "/midi"; + } + +private slots: + void init() + { + if (!QDir(midiDir).exists()) { + SVCERR << "ERROR: MIDI file directory \"" << midiDir << "\" does not exist" << endl; + QVERIFY2(QDir(midiDir).exists(), "MIDI file directory not found"); + } + } + + void read_data() + { + QTest::addColumn("filename"); + QStringList files = QDir(midiDir).entryList(QDir::Files); + foreach (QString filename, files) { + QTest::newRow(strOf(filename)) << filename; + } + } + + void read() + { + QFETCH(QString, filename); + QString path = midiDir + "/" + filename; + MIDIFileReader reader(path, nullptr, 44100); + Model *m = reader.load(); + if (!m) { + SVCERR << "MIDI load failed for path: \"" << path << "\"" << endl; + } + QVERIFY(m != nullptr); + //!!! Ah, now here we could do something a bit more informative + } + +}; + +#endif + diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/aac/32000-1.m4a Binary file data/fileio/test/audio/aac/32000-1.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/aac/44100-2.m4a Binary file data/fileio/test/audio/aac/44100-2.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/aiff/12000-6-16.aiff Binary file data/fileio/test/audio/aiff/12000-6-16.aiff has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/aiff/48000-1-24.aiff Binary file data/fileio/test/audio/aiff/48000-1-24.aiff has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/apple_lossless/32000-1.m4a Binary file data/fileio/test/audio/apple_lossless/32000-1.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/apple_lossless/44100-2.m4a Binary file data/fileio/test/audio/apple_lossless/44100-2.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/flac/44100-2.flac Binary file data/fileio/test/audio/flac/44100-2.flac has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/mp3/32000-1.mp3 Binary file data/fileio/test/audio/mp3/32000-1.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/mp3/44100-2.mp3 Binary file data/fileio/test/audio/mp3/44100-2.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/ogg/32000-1.ogg Binary file data/fileio/test/audio/ogg/32000-1.ogg has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/ogg/44100-2.ogg Binary file data/fileio/test/audio/ogg/44100-2.ogg has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/32000-1-16.wav Binary file data/fileio/test/audio/wav/32000-1-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/44100-1-32.wav Binary file data/fileio/test/audio/wav/44100-1-32.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/44100-2-16.wav Binary file data/fileio/test/audio/wav/44100-2-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/44100-2-8.wav Binary file data/fileio/test/audio/wav/44100-2-8.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/48000-1-16.wav Binary file data/fileio/test/audio/wav/48000-1-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/8000-1-8.wav Binary file data/fileio/test/audio/wav/8000-1-8.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/8000-2-16.wav Binary file data/fileio/test/audio/wav/8000-2-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/audio/wav/8000-6-16.wav Binary file data/fileio/test/audio/wav/8000-6-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/column-qualities.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/column-qualities.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,9 @@ +Text only,4,1024,45.6,45.7,-0.001,987 +Blah,5,2048,45.1,45.9,0.0123, + +# Include the odd blank line, space, and comment + + +Parp, 6, 3072 , 44.7 ,52.1, 0.26, + +Toot,7,4096,42.2,57.9,0.0, diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/comment.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/comment.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +# This is a comment +# This is a comment with various | possible | but not real separators in it +This is,the first,of the,real data lines +# This,is,one,that,would,cause,more,columns,to,be,counted,if,it,were,real +This is the,second diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-1d-samples.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-1d-samples.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +45678 +123239 +320130 +452103 +620301 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-1d-seconds.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-1d-seconds.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +3.2 First thing +4.4 Second thing +5.5 Third thing +6.3 Fourth thing +7.8 Fifth thing diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-duration-samples.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-duration-samples.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,9 @@ +# Here we see something that looks like time - value - value, but the +# time column is integral and so is exactly one of the value columns, +# so we deduce that that one is actually duration. We can only do this +# if the values are non-integral +45678,123,4 +123239,4214,4.2 +320130,12312,0.4 +452103,4123,3.8 +620301,987654,-2.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-duration-seconds.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-duration-seconds.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,8 @@ +# There are only very limited circumstances in which we deduce that we +# have a time+duration 2d model with units of seconds - basically only +# when the values column is entirely integer values +1.1,4,620 +2.2,4.2,880 +3.3,0.4,440 +4.4,3.8,213 +5.5,-2.3,123 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-endtime-samples.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-endtime-samples.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +45678,49000,4 +123239,330123,4.2 +320130,350000,0.4 +452103,540325,3.8 +620301,850000,-2.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-endtime-seconds.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-endtime-seconds.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +1.1,1.4,4 +2.2,3.1,4.2 +3.3,4.5,0.4 +4.4,4.6,3.8 +5.5,5.51,-2.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-implicit.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-implicit.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +4 +4.2 +0.4 +3.8 +-2.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-samples.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-samples.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +45678,4 +123239,4.2 +320130,0.4 +452103,3.8 +620301,-2.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-2d-seconds.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-2d-seconds.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +1.1,4 +2.2,4.2 +3.3,0.4 +4.4,3.8 +5.5,-2.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-3d-implicit.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-3d-implicit.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,6 @@ +143.0,2.0,-1.3,0.0,0.0,1.0 +0.2,0.1,-3.0,0.0,0.1,0.143 +0.143,0.2,-3.1,0.0,0.0,0.1 +2.0,1.0,-0.3,0.0,1.0,143.0 +0.0,0.0,0.1,0.143,0.2,-3.1 +0.0,1.0,143.0,2.0,1.0,-0.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-3d-samples.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-3d-samples.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,6 @@ +22050,143.0,2.0,-1.3,0.0,0.0,1.0 +44100,0.2,0.1,-3.0,0.0,0.1,0.143 +66150,0.143,0.2,-3.1,0.0,0.0,0.1 +88200,2.0,1.0,-0.3,0.0,1.0,143.0 +110250,0.0,0.0,0.1,0.143,0.2,-3.1 +132300,0.0,1.0,143.0,2.0,1.0,-0.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/model-type-3d-seconds.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/model-type-3d-seconds.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,6 @@ +1.1,143.0,2.0,-1.3,0.0,0.0,1.0 +2.2,0.2,0.1,-3.0,0.0,0.1,0.143 +3.3,0.143,0.2,-3.1,0.0,0.0,0.1 +4.4,2.0,1.0,-0.3,0.0,1.0,143.0 +5.5,0.0,0.0,0.1,0.143,0.2,-3.1 +6.6,0.0,1.0,143.0,2.0,1.0,-0.3 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/separator-colon.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/separator-colon.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,4 @@ +"This thing":"That thing":"The other thing" +1:12,4:16,3 +2:14,2 +3:16,1:1901 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/separator-comma.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/separator-comma.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,4 @@ +This thing,That thing,The other thing +1,12.4,16.3 +2,14.2 +3,16.1,"This, that\", and the other" diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/separator-pipe.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/separator-pipe.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,4 @@ +This thing|That thing|The other thing +1|12,4|16,3 +2|14,2|And another|column +3|16,1|1901|"Not another|column - we have four columns, not five" diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/separator-space.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/separator-space.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,4 @@ +"This thing" "That thing" "The other thing" +1 12,4 16,3 +2 14,2 +3 16,1 1901 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/csv/separator-tab.csv --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/test/csv/separator-tab.csv Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,4 @@ +This thing That thing The other thing +1 12,4 16,3 +2 14,2 +3 16,1 1901 diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/encodings/Tëmple of Spörks.mp3 Binary file data/fileio/test/encodings/Tëmple of Spörks.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/encodings/Tëmple of Spörks.ogg Binary file data/fileio/test/encodings/Tëmple of Spörks.ogg has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/encodings/id3v2-iso-8859-1.mp3 Binary file data/fileio/test/encodings/id3v2-iso-8859-1.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/encodings/id3v2-ucs-2.mp3 Binary file data/fileio/test/encodings/id3v2-ucs-2.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/encodings/スポークの寺院.mp3 Binary file data/fileio/test/encodings/スポークの寺院.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/encodings/スポークの寺院.ogg Binary file data/fileio/test/encodings/スポークの寺院.ogg has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/files.pri --- a/data/fileio/test/files.pri Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/test/files.pri Mon Sep 17 13:51:14 2018 +0100 @@ -1,7 +1,14 @@ TEST_HEADERS += \ - AudioFileReaderTest.h \ - AudioTestData.h - + ../../model/test/MockWaveModel.h \ + AudioFileReaderTest.h \ + AudioFileWriterTest.h \ + AudioTestData.h \ + EncodingTest.h \ + MIDIFileReaderTest.h \ + CSVFormatTest.h \ + CSVStreamWriterTest.h + TEST_SOURCES += \ - svcore-data-fileio-test.cpp + ../../model/test/MockWaveModel.cpp \ + svcore-data-fileio-test.cpp diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/midi/scale.mid Binary file data/fileio/test/midi/scale.mid has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/midi/아브라카다브라.mid Binary file data/fileio/test/midi/아브라카다브라.mid has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/svcore-data-fileio-test.cpp --- a/data/fileio/test/svcore-data-fileio-test.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/fileio/test/svcore-data-fileio-test.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -13,6 +13,11 @@ */ #include "AudioFileReaderTest.h" +#include "AudioFileWriterTest.h" +#include "EncodingTest.h" +#include "MIDIFileReaderTest.h" +#include "CSVFormatTest.h" +#include "CSVStreamWriterTest.h" #include @@ -22,22 +27,67 @@ { int good = 0, bad = 0; + QString testDir; + +#ifdef Q_OS_WIN + // incredible to have to hardcode this, but I just can't figure out how to + // get QMAKE_POST_LINK to add an arg to its command successfully on Windows + testDir = "../sonic-visualiser/svcore/data/fileio/test"; +#endif + + if (argc > 1) { + testDir = argv[1]; + } + QCoreApplication app(argc, argv); - app.setOrganizationName("Sonic Visualiser"); - app.setApplicationName("test-fileio"); + app.setOrganizationName("sonic-visualiser"); + app.setApplicationName("test-svcore-data-fileio"); + + if (testDir != "") { + SVCERR << "Setting test directory base path to \"" << testDir << "\"" << endl; + } { - AudioFileReaderTest t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + AudioFileReaderTest t(testDir); + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + { + AudioFileWriterTest t(testDir); + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + { + EncodingTest t(testDir); + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + { + MIDIFileReaderTest t(testDir); + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + { + CSVFormatTest t(testDir); + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + { + CSVStreamWriterTest t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } if (bad > 0) { - cerr << "\n********* " << bad << " test suite(s) failed!\n" << endl; - return 1; + SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; + return 1; } else { - cerr << "All tests passed" << endl; - return 0; + SVCERR << "All tests passed" << endl; + return 0; } } - diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/aac/32000-1.m4a Binary file data/fileio/test/testfiles/aac/32000-1.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/aac/44100-2.m4a Binary file data/fileio/test/testfiles/aac/44100-2.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/aiff/12000-6-16.aiff Binary file data/fileio/test/testfiles/aiff/12000-6-16.aiff has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/aiff/48000-1-24.aiff Binary file data/fileio/test/testfiles/aiff/48000-1-24.aiff has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/apple_lossless/32000-1.m4a Binary file data/fileio/test/testfiles/apple_lossless/32000-1.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/apple_lossless/44100-2.m4a Binary file data/fileio/test/testfiles/apple_lossless/44100-2.m4a has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/flac/44100-2.flac Binary file data/fileio/test/testfiles/flac/44100-2.flac has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/mp3/32000-1.mp3 Binary file data/fileio/test/testfiles/mp3/32000-1.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/mp3/44100-2.mp3 Binary file data/fileio/test/testfiles/mp3/44100-2.mp3 has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/ogg/32000-1.ogg Binary file data/fileio/test/testfiles/ogg/32000-1.ogg has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/ogg/44100-2.ogg Binary file data/fileio/test/testfiles/ogg/44100-2.ogg has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/32000-1-16.wav Binary file data/fileio/test/testfiles/wav/32000-1-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/44100-1-32.wav Binary file data/fileio/test/testfiles/wav/44100-1-32.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/44100-2-16.wav Binary file data/fileio/test/testfiles/wav/44100-2-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/44100-2-8.wav Binary file data/fileio/test/testfiles/wav/44100-2-8.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/48000-1-16.wav Binary file data/fileio/test/testfiles/wav/48000-1-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/8000-1-8.wav Binary file data/fileio/test/testfiles/wav/8000-1-8.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/8000-2-16.wav Binary file data/fileio/test/testfiles/wav/8000-2-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/fileio/test/testfiles/wav/8000-6-16.wav Binary file data/fileio/test/testfiles/wav/8000-6-16.wav has changed diff -r d4a28d1479a8 -r 710e6250a401 data/midi/MIDIEvent.h --- a/data/midi/MIDIEvent.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/midi/MIDIEvent.h Mon Sep 17 13:51:14 2018 +0100 @@ -123,9 +123,9 @@ int eventCode, int data1 = 0, int data2 = 0) : - m_deltaTime(deltaTime), - m_duration(0), - m_metaEventCode(0) + m_deltaTime(deltaTime), + m_duration(0), + m_metaEventCode(0) { if (eventCode < 0 || eventCode > 0xff || data1 < 0 || data1 > 0xff || @@ -141,25 +141,25 @@ MIDIByte eventCode, MIDIByte metaEventCode, const std::string &metaMessage) : - m_deltaTime(deltaTime), - m_duration(0), - m_eventCode(eventCode), - m_data1(0), - m_data2(0), - m_metaEventCode(metaEventCode), - m_metaMessage(metaMessage) + m_deltaTime(deltaTime), + m_duration(0), + m_eventCode(eventCode), + m_data1(0), + m_data2(0), + m_metaEventCode(metaEventCode), + m_metaMessage(metaMessage) { } MIDIEvent(unsigned long deltaTime, MIDIByte eventCode, const std::string &sysEx) : - m_deltaTime(deltaTime), - m_duration(0), - m_eventCode(eventCode), - m_data1(0), - m_data2(0), - m_metaEventCode(0), - m_metaMessage(sysEx) + m_deltaTime(deltaTime), + m_duration(0), + m_eventCode(eventCode), + m_data1(0), + m_data2(0), + m_metaEventCode(0), + m_metaMessage(sysEx) { } ~MIDIEvent() { } @@ -167,8 +167,8 @@ void setTime(const unsigned long &time) { m_deltaTime = time; } void setDuration(const unsigned long& duration) { m_duration = duration;} unsigned long addTime(const unsigned long &time) { - m_deltaTime += time; - return m_deltaTime; + m_deltaTime += time; + return m_deltaTime; } MIDIByte getMessageType() const @@ -222,12 +222,12 @@ public: MIDIException(QString message) throw() : m_message(message) { std::cerr << "WARNING: MIDI exception: " - << message.toLocal8Bit().data() << std::endl; + << message.toLocal8Bit().data() << std::endl; } virtual ~MIDIException() throw() { } virtual const char *what() const throw() { - return m_message.toLocal8Bit().data(); + return m_message.toLocal8Bit().data(); } protected: diff -r d4a28d1479a8 -r 710e6250a401 data/midi/MIDIInput.cpp --- a/data/midi/MIDIInput.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/midi/MIDIInput.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -20,16 +20,52 @@ #include "system/System.h" MIDIInput::MIDIInput(QString name, FrameTimer *timer) : - m_rtmidi(), + m_rtmidi(0), m_frameTimer(timer), m_buffer(1023) { try { - m_rtmidi = new RtMidiIn(name.toStdString()); - m_rtmidi->setCallback(staticCallback, this); - m_rtmidi->openPort(0, tr("Input").toStdString()); - } catch (RtError e) { - e.printMessage(); + std::vector apis; + RtMidi::getCompiledApi(apis); + RtMidi::Api preferredApi = RtMidi::UNSPECIFIED; + for (auto a: apis) { + if (a == RtMidi::UNSPECIFIED || a == RtMidi::RTMIDI_DUMMY) { + continue; + } + preferredApi = a; + break; + } + if (preferredApi == RtMidi::UNSPECIFIED) { + SVCERR << "ERROR: MIDIInput: No RtMidi APIs compiled in" << endl; + } else { + + m_rtmidi = new RtMidiIn(preferredApi, name.toStdString()); + + int n = m_rtmidi->getPortCount(); + + if (n == 0) { + + SVDEBUG << "NOTE: MIDIInput: No input ports available" << endl; + delete m_rtmidi; + m_rtmidi = 0; + + } else { + + m_rtmidi->setCallback(staticCallback, this); + + SVDEBUG << "MIDIInput: Available ports are:" << endl; + for (int i = 0; i < n; ++i) { + SVDEBUG << i << ". " << m_rtmidi->getPortName(i) << endl; + } + SVDEBUG << "MIDIInput: Using first port (\"" + << m_rtmidi->getPortName(0) << "\")" << endl; + + m_rtmidi->openPort(0, tr("Input").toStdString()); + } + } + + } catch (const RtMidiError &e) { + SVCERR << "ERROR: RtMidi error: " << e.getMessage() << endl; delete m_rtmidi; m_rtmidi = 0; } @@ -80,10 +116,10 @@ int count = 0, max = 5; while (m_buffer.getWriteSpace() == 0) { if (count == max) { - cerr << "ERROR: MIDIInput::postEvent: MIDI event queue is full and not clearing -- abandoning incoming event" << endl; + SVCERR << "ERROR: MIDIInput::postEvent: MIDI event queue is full and not clearing -- abandoning incoming event" << endl; return; } - cerr << "WARNING: MIDIInput::postEvent: MIDI event queue (capacity " << m_buffer.getSize() << " is full!" << endl; + SVCERR << "WARNING: MIDIInput::postEvent: MIDI event queue (capacity " << m_buffer.getSize() << " is full!" << endl; SVDEBUG << "Waiting for something to be processed" << endl; #ifdef _WIN32 Sleep(1); diff -r d4a28d1479a8 -r 710e6250a401 data/midi/rtmidi/RtError.h --- a/data/midi/rtmidi/RtError.h Mon Dec 12 15:18:52 2016 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/************************************************************************/ -/*! \class RtError - \brief Exception handling class for RtAudio & RtMidi. - - The RtError class is quite simple but it does allow errors to be - "caught" by RtError::Type. See the RtAudio and RtMidi - documentation to know which methods can throw an RtError. - -*/ -/************************************************************************/ - -#ifndef RTERROR_H -#define RTERROR_H - -#include -#include - -class RtError -{ -public: - //! Defined RtError types. - enum Type { - WARNING, /*!< A non-critical error. */ - DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ - UNSPECIFIED, /*!< The default, unspecified error type. */ - NO_DEVICES_FOUND, /*!< No devices found on system. */ - INVALID_DEVICE, /*!< An invalid device ID was specified. */ - INVALID_STREAM, /*!< An invalid stream ID was specified. */ - MEMORY_ERROR, /*!< An error occured during memory allocation. */ - INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ - DRIVER_ERROR, /*!< A system driver error occured. */ - SYSTEM_ERROR, /*!< A system error occured. */ - THREAD_ERROR /*!< A thread error occured. */ - }; - -protected: - std::string message_; - Type type_; - -public: - //! The constructor. - RtError(const std::string& message, Type type = RtError::UNSPECIFIED) : message_(message), type_(type) {} - - //! The destructor. - virtual ~RtError(void) {}; - - //! Prints thrown error message to stderr. - virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; } - - //! Returns the thrown error message type. - virtual const Type& getType(void) { return type_; } - - //! Returns the thrown error message string. - virtual const std::string& getMessage(void) { return message_; } - - //! Returns the thrown error message as a C string. - virtual const char *getMessageString(void) { return message_.c_str(); } -}; - -#endif diff -r d4a28d1479a8 -r 710e6250a401 data/midi/rtmidi/RtMidi.cpp --- a/data/midi/rtmidi/RtMidi.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/midi/rtmidi/RtMidi.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -8,7 +8,7 @@ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2009 Gary P. Scavone + Copyright (c) 2003-2016 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -22,8 +22,9 @@ included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is - requested to send the modifications to the original developer so that - they can be incorporated into the canonical version. + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF @@ -35,72 +36,290 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.8 - #include "RtMidi.h" #include -using std::cerr; -using std::endl; +// CC be gung-ho about this here, assume upstream has it in hand +#pragma GCC diagnostic ignored "-Wconversion" + +#if defined(__MACOSX_CORE__) + #if TARGET_OS_IPHONE + #define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime + #define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos + #endif +#endif //*********************************************************************// -// Common RtMidi Definitions +// RtMidi Definitions //*********************************************************************// RtMidi :: RtMidi() - : apiData_( 0 ), connected_( false ) + : rtapi_(0) { } -void RtMidi :: error( RtError::Type type ) +RtMidi :: ~RtMidi() { - if (type == RtError::WARNING) { - cerr << '\n' << errorString_ << "\n\n"; + if ( rtapi_ ) + delete rtapi_; + rtapi_ = 0; +} + +std::string RtMidi :: getVersion( void ) throw() +{ + return std::string( RTMIDI_VERSION ); +} + +void RtMidi :: getCompiledApi( std::vector &apis ) throw() +{ + apis.clear(); + + // The order here will control the order of RtMidi's API search in + // the constructor. +#if defined(__MACOSX_CORE__) + apis.push_back( MACOSX_CORE ); +#endif +#if defined(__LINUX_ALSA__) + apis.push_back( LINUX_ALSA ); +#endif +#if defined(__UNIX_JACK__) + apis.push_back( UNIX_JACK ); +#endif +#if defined(__WINDOWS_MM__) + apis.push_back( WINDOWS_MM ); +#endif +#if defined(__RTMIDI_DUMMY__) + apis.push_back( RTMIDI_DUMMY ); +#endif +} + +//*********************************************************************// +// RtMidiIn Definitions +//*********************************************************************// + +void RtMidiIn :: openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) +{ + if ( rtapi_ ) + delete rtapi_; + rtapi_ = 0; + +#if defined(__UNIX_JACK__) + if ( api == UNIX_JACK ) + rtapi_ = new MidiInJack( clientName, queueSizeLimit ); +#endif +#if defined(__LINUX_ALSA__) + if ( api == LINUX_ALSA ) + rtapi_ = new MidiInAlsa( clientName, queueSizeLimit ); +#endif +#if defined(__WINDOWS_MM__) + if ( api == WINDOWS_MM ) + rtapi_ = new MidiInWinMM( clientName, queueSizeLimit ); +#endif +#if defined(__MACOSX_CORE__) + if ( api == MACOSX_CORE ) + rtapi_ = new MidiInCore( clientName, queueSizeLimit ); +#endif +#if defined(__RTMIDI_DUMMY__) + if ( api == RTMIDI_DUMMY ) + rtapi_ = new MidiInDummy( clientName, queueSizeLimit ); +#endif +} + +RtMidiIn :: RtMidiIn( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ) + : RtMidi() +{ + if ( api != UNSPECIFIED ) { + // Attempt to open the specified API. + openMidiApi( api, clientName, queueSizeLimit ); + if ( rtapi_ ) return; + + // No compiled support for specified API value. Issue a warning + // and continue as if no API was specified. + std::cerr << "\nRtMidiIn: no compiled support for specified API argument!\n\n" << std::endl; } - else if (type == RtError::DEBUG_WARNING) { + + // Iterate through the compiled APIs and return as soon as we find + // one with at least one port or we reach the end of the list. + std::vector< RtMidi::Api > apis; + getCompiledApi( apis ); + for ( unsigned int i=0; igetPortCount() ) break; + } + + if ( rtapi_ ) return; + + // It should not be possible to get here because the preprocessor + // definition __RTMIDI_DUMMY__ is automatically defined if no + // API-specific definitions are passed to the compiler. But just in + // case something weird happens, we'll throw an error. + std::string errorText = "RtMidiIn: no compiled API support found ... critical error!!"; + throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); +} + +RtMidiIn :: ~RtMidiIn() throw() +{ +} + + +//*********************************************************************// +// RtMidiOut Definitions +//*********************************************************************// + +void RtMidiOut :: openMidiApi( RtMidi::Api api, const std::string clientName ) +{ + if ( rtapi_ ) + delete rtapi_; + rtapi_ = 0; + +#if defined(__UNIX_JACK__) + if ( api == UNIX_JACK ) + rtapi_ = new MidiOutJack( clientName ); +#endif +#if defined(__LINUX_ALSA__) + if ( api == LINUX_ALSA ) + rtapi_ = new MidiOutAlsa( clientName ); +#endif +#if defined(__WINDOWS_MM__) + if ( api == WINDOWS_MM ) + rtapi_ = new MidiOutWinMM( clientName ); +#endif +#if defined(__MACOSX_CORE__) + if ( api == MACOSX_CORE ) + rtapi_ = new MidiOutCore( clientName ); +#endif +#if defined(__RTMIDI_DUMMY__) + if ( api == RTMIDI_DUMMY ) + rtapi_ = new MidiOutDummy( clientName ); +#endif +} + +RtMidiOut :: RtMidiOut( RtMidi::Api api, const std::string clientName ) +{ + if ( api != UNSPECIFIED ) { + // Attempt to open the specified API. + openMidiApi( api, clientName ); + if ( rtapi_ ) return; + + // No compiled support for specified API value. Issue a warning + // and continue as if no API was specified. + std::cerr << "\nRtMidiOut: no compiled support for specified API argument!\n\n" << std::endl; + } + + // Iterate through the compiled APIs and return as soon as we find + // one with at least one port or we reach the end of the list. + std::vector< RtMidi::Api > apis; + getCompiledApi( apis ); + for ( unsigned int i=0; igetPortCount() ) break; + } + + if ( rtapi_ ) return; + + // It should not be possible to get here because the preprocessor + // definition __RTMIDI_DUMMY__ is automatically defined if no + // API-specific definitions are passed to the compiler. But just in + // case something weird happens, we'll thrown an error. + std::string errorText = "RtMidiOut: no compiled API support found ... critical error!!"; + throw( RtMidiError( errorText, RtMidiError::UNSPECIFIED ) ); +} + +RtMidiOut :: ~RtMidiOut() throw() +{ +} + +//*********************************************************************// +// Common MidiApi Definitions +//*********************************************************************// + +MidiApi :: MidiApi( void ) + : apiData_( 0 ), connected_( false ), errorCallback_(0), errorCallbackUserData_(0) +{ +} + +MidiApi :: ~MidiApi( void ) +{ +} + +void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 ) +{ + errorCallback_ = errorCallback; + errorCallbackUserData_ = userData; +} + +void MidiApi :: error( RtMidiError::Type type, std::string errorString ) +{ + if ( errorCallback_ ) { + + if ( firstErrorOccurred_ ) + return; + + firstErrorOccurred_ = true; + const std::string errorMessage = errorString; + + errorCallback_( type, errorMessage, errorCallbackUserData_); + firstErrorOccurred_ = false; + return; + } + + if ( type == RtMidiError::WARNING ) { + std::cerr << '\n' << errorString << "\n\n"; + } + else if ( type == RtMidiError::DEBUG_WARNING ) { #if defined(__RTMIDI_DEBUG__) - cerr << '\n' << errorString_ << "\n\n"; + std::cerr << '\n' << errorString << "\n\n"; #endif } else { - cerr << "\nRtMidi error: " << errorString_ << "\n\n"; - throw RtError( errorString_, type ); + std::cerr << '\n' << errorString << "\n\n"; + throw RtMidiError( errorString, type ); } } //*********************************************************************// -// Common RtMidiIn Definitions +// Common MidiInApi Definitions //*********************************************************************// -RtMidiIn :: RtMidiIn( const std::string clientName ) : RtMidi() +MidiInApi :: MidiInApi( unsigned int queueSizeLimit ) + : MidiApi() { - this->initialize( clientName ); + // Allocate the MIDI queue. + inputData_.queue.ringSize = queueSizeLimit; + if ( inputData_.queue.ringSize > 0 ) + inputData_.queue.ring = new MidiMessage[ inputData_.queue.ringSize ]; } -void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) +MidiInApi :: ~MidiInApi( void ) +{ + // Delete the MIDI queue. + if ( inputData_.queue.ringSize > 0 ) delete [] inputData_.queue.ring; +} + +void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData ) { if ( inputData_.usingCallback ) { - errorString_ = "RtMidiIn::setCallback: a callback function is already set!"; - error( RtError::WARNING ); + errorString_ = "MidiInApi::setCallback: a callback function is already set!"; + error( RtMidiError::WARNING, errorString_ ); return; } if ( !callback ) { errorString_ = "RtMidiIn::setCallback: callback function value is invalid!"; - error( RtError::WARNING ); + error( RtMidiError::WARNING, errorString_ ); return; } - inputData_.userCallback = (void *) callback; + inputData_.userCallback = callback; inputData_.userData = userData; inputData_.usingCallback = true; } -void RtMidiIn :: cancelCallback() +void MidiInApi :: cancelCallback() { if ( !inputData_.usingCallback ) { errorString_ = "RtMidiIn::cancelCallback: no callback function was set!"; - error( RtError::WARNING ); + error( RtMidiError::WARNING, errorString_ ); return; } @@ -109,12 +328,7 @@ inputData_.usingCallback = false; } -void RtMidiIn :: setQueueSizeLimit( unsigned int queueSize ) -{ - inputData_.queueLimit = queueSize; -} - -void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) +void MidiInApi :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { inputData_.ignoreFlags = 0; if ( midiSysex ) inputData_.ignoreFlags = 0x01; @@ -122,43 +336,48 @@ if ( midiSense ) inputData_.ignoreFlags |= 0x04; } -double RtMidiIn :: getMessage( std::vector *message ) +double MidiInApi :: getMessage( std::vector *message ) { message->clear(); if ( inputData_.usingCallback ) { errorString_ = "RtMidiIn::getNextMessage: a user callback is currently set for this port."; - error( RtError::WARNING ); + error( RtMidiError::WARNING, errorString_ ); return 0.0; } - if ( inputData_.queue.size() == 0 ) return 0.0; + if ( inputData_.queue.size == 0 ) return 0.0; // Copy queued message to the vector pointer argument and then "pop" it. - std::vector *bytes = &(inputData_.queue.front().bytes); + std::vector *bytes = &(inputData_.queue.ring[inputData_.queue.front].bytes); message->assign( bytes->begin(), bytes->end() ); - double deltaTime = inputData_.queue.front().timeStamp; - inputData_.queue.pop(); + double deltaTime = inputData_.queue.ring[inputData_.queue.front].timeStamp; + inputData_.queue.size--; + inputData_.queue.front++; + if ( inputData_.queue.front == inputData_.queue.ringSize ) + inputData_.queue.front = 0; return deltaTime; } //*********************************************************************// -// Common RtMidiOut Definitions +// Common MidiOutApi Definitions //*********************************************************************// -RtMidiOut :: RtMidiOut( const std::string clientName ) : RtMidi() +MidiOutApi :: MidiOutApi( void ) + : MidiApi() { - this->initialize( clientName ); } - -//*********************************************************************// -// API: Macintosh OS-X -//*********************************************************************// - -// API information found at: -// - http://developer. apple .com/audio/pdf/coreaudio.pdf +MidiOutApi :: ~MidiOutApi( void ) +{ +} + +// *************************************************** // +// +// OS/API-specific methods. +// +// *************************************************** // #if defined(__MACOSX_CORE__) @@ -169,6 +388,7 @@ // OS-X CoreMIDI header files. #include #include +#include // A structure to hold variables related to the CoreMIDI API // implementation. @@ -178,16 +398,17 @@ MIDIEndpointRef endpoint; MIDIEndpointRef destinationId; unsigned long long lastTime; + MIDISysexSendRequest sysexreq; }; //*********************************************************************// // API: OS-X -// Class Definitions: RtMidiIn +// Class Definitions: MidiInCore //*********************************************************************// -void midiInputCallback( const MIDIPacketList *list, void *procRef, void *srcRef ) +static void midiInputCallback( const MIDIPacketList *list, void *procRef, void */*srcRef*/ ) { - RtMidiIn::RtMidiInData *data = static_cast (procRef); + MidiInApi::RtMidiInData *data = static_cast (procRef); CoreMidiData *apiData = static_cast (data->apiData); unsigned char status; @@ -195,7 +416,7 @@ unsigned long long time; bool& continueSysex = data->continueSysex; - RtMidiIn::MidiMessage& message = data->message; + MidiInApi::MidiMessage& message = data->message; const MIDIPacket *packet = &list->packet[0]; for ( unsigned int i=0; inumPackets; ++i ) { @@ -213,39 +434,53 @@ if ( nBytes == 0 ) continue; // Calculate time stamp. - message.timeStamp = 0.0; - if ( data->firstMessage ) + + if ( data->firstMessage ) { + message.timeStamp = 0.0; data->firstMessage = false; + } else { time = packet->timeStamp; + if ( time == 0 ) { // this happens when receiving asynchronous sysex messages + time = AudioGetCurrentHostTime(); + } time -= apiData->lastTime; time = AudioConvertHostTimeToNanos( time ); - message.timeStamp = time * 0.000000001; + if ( !continueSysex ) + message.timeStamp = time * 0.000000001; } apiData->lastTime = packet->timeStamp; + if ( apiData->lastTime == 0 ) { // this happens when receiving asynchronous sysex messages + apiData->lastTime = AudioGetCurrentHostTime(); + } + //std::cout << "TimeStamp = " << packet->timeStamp << std::endl; iByte = 0; if ( continueSysex ) { // We have a continuing, segmented sysex message. if ( !( data->ignoreFlags & 0x01 ) ) { // If we're not ignoring sysex messages, copy the entire packet. - for ( unsigned int j=0; jdata[j] ); } continueSysex = packet->data[nBytes-1] != 0xF7; - if ( !continueSysex ) { + if ( !( data->ignoreFlags & 0x01 ) && !continueSysex ) { // If not a continuing sysex message, invoke the user callback function or queue the message. - if ( data->usingCallback && message.bytes.size() > 0 ) { + if ( data->usingCallback ) { RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; callback( message.timeStamp, &message.bytes, data->userData ); } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queueLimit > data->queue.size() ) - data->queue.push( message ); + if ( data->queue.size < data->queue.ringSize ) { + data->queue.ring[data->queue.back++] = message; + if ( data->queue.back == data->queue.ringSize ) + data->queue.back = 0; + data->queue.size++; + } else - cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; } message.bytes.clear(); } @@ -267,26 +502,24 @@ iByte = nBytes; } else size = nBytes - iByte; - continueSysex = packet->data[nBytes-1] != 0xF7; + continueSysex = packet->data[nBytes-1] != 0xF7; } - else if ( status < 0xF3 ) { - if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) { - // A MIDI time code message and we're ignoring it. + else if ( status == 0xF1 ) { + // A MIDI time code message + if ( data->ignoreFlags & 0x02 ) { size = 0; - iByte += 3; - } - else size = 3; + iByte += 2; + } + else size = 2; } + else if ( status == 0xF2 ) size = 3; else if ( status == 0xF3 ) size = 2; - else if ( status == 0xF8 ) { - size = 1; - if ( data->ignoreFlags & 0x02 ) { - // A MIDI timing tick message and we're ignoring it. - size = 0; - iByte += 3; - } + else if ( status == 0xF8 && ( data->ignoreFlags & 0x02 ) ) { + // A MIDI timing tick message and we're ignoring it. + size = 0; + iByte += 1; } - else if ( status == 0xFE && (data->ignoreFlags & 0x04) ) { + else if ( status == 0xFE && ( data->ignoreFlags & 0x04 ) ) { // A MIDI active sensing message and we're ignoring it. size = 0; iByte += 1; @@ -304,10 +537,14 @@ } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queueLimit > data->queue.size() ) - data->queue.push( message ); + if ( data->queue.size < data->queue.ringSize ) { + data->queue.ring[data->queue.back++] = message; + if ( data->queue.back == data->queue.ringSize ) + data->queue.back = 0; + data->queue.size++; + } else - cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + std::cerr << "\nMidiInCore: message queue limit reached!!\n\n"; } message.bytes.clear(); } @@ -319,14 +556,33 @@ } } -void RtMidiIn :: initialize( const std::string& clientName ) +MidiInCore :: MidiInCore( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +{ + initialize( clientName ); +} + +MidiInCore :: ~MidiInCore( void ) +{ + // Close a connection if it exists. + closePort(); + + // Cleanup. + CoreMidiData *data = static_cast (apiData_); + MIDIClientDispose( data->client ); + if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); + delete data; +} + +void MidiInCore :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); + CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); if ( result != noErr ) { - errorString_ = "RtMidiIn::initialize: error creating OS-X MIDI client object."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Save our api-specific connection information. @@ -335,27 +591,31 @@ data->endpoint = 0; apiData_ = (void *) data; inputData_.apiData = (void *) data; + CFRelease(name); } -void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) +void MidiInCore :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { - errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; - error( RtError::WARNING ); + errorString_ = "MidiInCore::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); unsigned int nSrc = MIDIGetNumberOfSources(); if (nSrc < 1) { - errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; - error( RtError::NO_DEVICES_FOUND ); + errorString_ = "MidiInCore::openPort: no MIDI input sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; } - std::ostringstream ost; if ( portNumber >= nSrc ) { - ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + std::ostringstream ost; + ost << "MidiInCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; } MIDIPortRef port; @@ -365,17 +625,19 @@ midiInputCallback, (void *)&inputData_, &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); - errorString_ = "RtMidiIn::openPort: error creating OS-X MIDI input port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInCore::openPort: error creating OS-X MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Get the desired input source identifier. MIDIEndpointRef endpoint = MIDIGetSource( portNumber ); - if ( endpoint == NULL ) { + if ( endpoint == 0 ) { MIDIPortDispose( port ); MIDIClientDispose( data->client ); - errorString_ = "RtMidiIn::openPort: error getting MIDI input source reference."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInCore::openPort: error getting MIDI input source reference."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Make the connection. @@ -383,8 +645,9 @@ if ( result != noErr ) { MIDIPortDispose( port ); MIDIClientDispose( data->client ); - errorString_ = "RtMidiIn::openPort: error connecting OS-X MIDI input port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInCore::openPort: error connecting OS-X MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Save our api-specific port information. @@ -393,7 +656,7 @@ connected_ = true; } -void RtMidiIn :: openVirtualPort( const std::string portName ) +void MidiInCore :: openVirtualPort( const std::string portName ) { CoreMidiData *data = static_cast (apiData_); @@ -403,24 +666,197 @@ CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), midiInputCallback, (void *)&inputData_, &endpoint ); if ( result != noErr ) { - errorString_ = "RtMidiIn::openVirtualPort: error creating virtual OS-X MIDI destination."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInCore::openVirtualPort: error creating virtual OS-X MIDI destination."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Save our api-specific connection information. data->endpoint = endpoint; } -void RtMidiIn :: closePort( void ) +void MidiInCore :: closePort( void ) { - if ( connected_ ) { - CoreMidiData *data = static_cast (apiData_); + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + MIDIEndpointDispose( data->endpoint ); + } + + if ( data->port ) { MIDIPortDispose( data->port ); - connected_ = false; } + + connected_ = false; } -RtMidiIn :: ~RtMidiIn() +unsigned int MidiInCore :: getPortCount() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + return MIDIGetNumberOfSources(); +} + +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +CFStringRef EndpointName( MIDIEndpointRef endpoint, bool isExternal ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + + // Begin with the endpoint's name. + str = NULL; + MIDIObjectGetStringProperty( endpoint, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + + MIDIEntityRef entity = 0; + MIDIEndpointGetEntity( endpoint, &entity ); + if ( entity == 0 ) + // probably virtual + return result; + + if ( CFStringGetLength( result ) == 0 ) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty( entity, kMIDIPropertyName, &str ); + if ( str != NULL ) { + CFStringAppend( result, str ); + CFRelease( str ); + } + } + // now consider the device's name + MIDIDeviceRef device = 0; + MIDIEntityGetDevice( entity, &device ); + if ( device == 0 ) + return result; + + str = NULL; + MIDIObjectGetStringProperty( device, kMIDIPropertyName, &str ); + if ( CFStringGetLength( result ) == 0 ) { + CFRelease( result ); + return str; + } + if ( str != NULL ) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if ( isExternal && MIDIDeviceGetNumberOfEntities( device ) < 2 ) { + CFRelease( result ); + return str; + } else { + if ( CFStringGetLength( str ) == 0 ) { + CFRelease( str ); + return result; + } + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + if ( CFStringCompareWithOptions( result, /* endpoint name */ + str /* device name */, + CFRangeMake(0, CFStringGetLength( str ) ), 0 ) != kCFCompareEqualTo ) { + // prepend the device name to the entity name + if ( CFStringGetLength( result ) > 0 ) + CFStringInsert( result, 0, CFSTR(" ") ); + CFStringInsert( result, 0, str ); + } + CFRelease( str ); + } + } + return result; +} + +// This function was submitted by Douglas Casey Tucker and apparently +// derived largely from PortMidi. +static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint ) +{ + CFMutableStringRef result = CFStringCreateMutable( NULL, 0 ); + CFStringRef str; + OSStatus err; + int i; + + // Does the endpoint have connections? + CFDataRef connections = NULL; + int nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty( endpoint, kMIDIPropertyConnectionUniqueID, &connections ); + if ( connections != NULL ) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength( connections ) / sizeof(MIDIUniqueID); + if ( nConnected ) { + const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); + for ( i=0; i= MIDIGetNumberOfSources() ) { + std::ostringstream ost; + ost << "MidiInCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + portRef = MIDIGetSource( portNumber ); + nameRef = ConnectedEndpointName(portRef); + CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding()); + CFRelease( nameRef ); + + return stringName = name; +} + +//*********************************************************************// +// API: OS-X +// Class Definitions: MidiOutCore +//*********************************************************************// + +MidiOutCore :: MidiOutCore( const std::string clientName ) : MidiOutApi() +{ + initialize( clientName ); +} + +MidiOutCore :: ~MidiOutCore( void ) { // Close a connection if it exists. closePort(); @@ -432,71 +868,16 @@ delete data; } -unsigned int RtMidiIn :: getPortCount() -{ - return MIDIGetNumberOfSources(); -} - -std::string RtMidiIn :: getPortName( unsigned int portNumber ) -{ - CFStringRef nameRef; - MIDIEndpointRef portRef; - std::ostringstream ost; - char name[128]; - - if ( portNumber >= MIDIGetNumberOfSources() ) { - ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - portRef = MIDIGetSource( portNumber ); - - MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); - CFStringGetCString( nameRef, name, sizeof(name), 0); - CFRelease( nameRef ); - std::string stringName = name; - return stringName; -} - -//*********************************************************************// -// API: OS-X -// Class Definitions: RtMidiOut -//*********************************************************************// - -unsigned int RtMidiOut :: getPortCount() -{ - return MIDIGetNumberOfDestinations(); -} - -std::string RtMidiOut :: getPortName( unsigned int portNumber ) -{ - CFStringRef nameRef; - MIDIEndpointRef portRef; - std::ostringstream ost; - char name[128]; - - if ( portNumber >= MIDIGetNumberOfDestinations() ) { - ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - portRef = MIDIGetDestination( portNumber ); - - MIDIObjectGetStringProperty( portRef, kMIDIPropertyName, &nameRef ); - CFStringGetCString( nameRef, name, sizeof(name), 0); - CFRelease( nameRef ); - std::string stringName = name; - return stringName; -} - -void RtMidiOut :: initialize( const std::string& clientName ) +void MidiOutCore :: initialize( const std::string& clientName ) { // Set up our client. MIDIClientRef client; - OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); + CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ); + OSStatus result = MIDIClientCreate(name, NULL, NULL, &client ); if ( result != noErr ) { - errorString_ = "RtMidiOut::initialize: error creating OS-X MIDI client object."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Save our api-specific connection information. @@ -504,27 +885,61 @@ data->client = client; data->endpoint = 0; apiData_ = (void *) data; + CFRelease( name ); } -void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) +unsigned int MidiOutCore :: getPortCount() +{ + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + return MIDIGetNumberOfDestinations(); +} + +std::string MidiOutCore :: getPortName( unsigned int portNumber ) +{ + CFStringRef nameRef; + MIDIEndpointRef portRef; + char name[128]; + + std::string stringName; + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); + if ( portNumber >= MIDIGetNumberOfDestinations() ) { + std::ostringstream ost; + ost << "MidiOutCore::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + portRef = MIDIGetDestination( portNumber ); + nameRef = ConnectedEndpointName(portRef); + CFStringGetCString( nameRef, name, sizeof(name), CFStringGetSystemEncoding()); + CFRelease( nameRef ); + + return stringName = name; +} + +void MidiOutCore :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { - errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; - error( RtError::WARNING ); + errorString_ = "MidiOutCore::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } + CFRunLoopRunInMode( kCFRunLoopDefaultMode, 0, false ); unsigned int nDest = MIDIGetNumberOfDestinations(); if (nDest < 1) { - errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; - error( RtError::NO_DEVICES_FOUND ); + errorString_ = "MidiOutCore::openPort: no MIDI output destinations found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; } - std::ostringstream ost; if ( portNumber >= nDest ) { - ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + std::ostringstream ost; + ost << "MidiOutCore::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; } MIDIPortRef port; @@ -534,17 +949,19 @@ &port ); if ( result != noErr ) { MIDIClientDispose( data->client ); - errorString_ = "RtMidiOut::openPort: error creating OS-X MIDI output port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutCore::openPort: error creating OS-X MIDI output port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Get the desired output port identifier. MIDIEndpointRef destination = MIDIGetDestination( portNumber ); - if ( destination == NULL ) { + if ( destination == 0 ) { MIDIPortDispose( port ); MIDIClientDispose( data->client ); - errorString_ = "RtMidiOut::openPort: error getting MIDI output destination reference."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutCore::openPort: error getting MIDI output destination reference."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Save our api-specific connection information. @@ -553,22 +970,28 @@ connected_ = true; } -void RtMidiOut :: closePort( void ) -{ - if ( connected_ ) { - CoreMidiData *data = static_cast (apiData_); - MIDIPortDispose( data->port ); - connected_ = false; - } -} - -void RtMidiOut :: openVirtualPort( std::string portName ) +void MidiOutCore :: closePort( void ) { CoreMidiData *data = static_cast (apiData_); if ( data->endpoint ) { - errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!"; - error( RtError::WARNING ); + MIDIEndpointDispose( data->endpoint ); + } + + if ( data->port ) { + MIDIPortDispose( data->port ); + } + + connected_ = false; +} + +void MidiOutCore :: openVirtualPort( std::string portName ) +{ + CoreMidiData *data = static_cast (apiData_); + + if ( data->endpoint ) { + errorString_ = "MidiOutCore::openVirtualPort: a virtual output port already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } @@ -578,81 +1001,70 @@ CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ), &endpoint ); if ( result != noErr ) { - errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutCore::initialize: error creating OS-X virtual MIDI source."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Save our api-specific connection information. data->endpoint = endpoint; } -RtMidiOut :: ~RtMidiOut() +void MidiOutCore :: sendMessage( std::vector *message ) { - // Close a connection if it exists. - closePort(); - - // Cleanup. - CoreMidiData *data = static_cast (apiData_); - MIDIClientDispose( data->client ); - if ( data->endpoint ) MIDIEndpointDispose( data->endpoint ); - delete data; -} - -void RtMidiOut :: sendMessage( std::vector *message ) -{ - // The CoreMidi documentation indicates a maximum PackList size of - // 64K, so we may need to break long sysex messages into pieces and - // send via separate lists. + // We use the MIDISendSysex() function to asynchronously send sysex + // messages. Otherwise, we use a single CoreMidi MIDIPacket. unsigned int nBytes = message->size(); if ( nBytes == 0 ) { - errorString_ = "RtMidiOut::sendMessage: no data in message argument!"; - error( RtError::WARNING ); + errorString_ = "MidiOutCore::sendMessage: no data in message argument!"; + error( RtMidiError::WARNING, errorString_ ); return; } - if ( nBytes > 3 && ( message->at(0) != 0xF0 ) ) { - errorString_ = "RtMidiOut::sendMessage: message format problem ... not sysex but > 3 bytes?"; - error( RtError::WARNING ); + MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); + CoreMidiData *data = static_cast (apiData_); + OSStatus result; + + if ( message->at(0) != 0xF0 && nBytes > 3 ) { + errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; + error( RtMidiError::WARNING, errorString_ ); return; } - unsigned int packetBytes, bytesLeft = nBytes; - unsigned int messageIndex = 0; - MIDITimeStamp timeStamp = 0; - CoreMidiData *data = static_cast (apiData_); - - while ( bytesLeft > 0 ) { - - packetBytes = ( bytesLeft > 32736 ) ? 32736 : bytesLeft; - Byte buffer[packetBytes + 32]; // extra memory for other structure variables - MIDIPacketList *packetList = (MIDIPacketList *) buffer; - MIDIPacket *curPacket = MIDIPacketListInit( packetList ); - - curPacket = MIDIPacketListAdd( packetList, packetBytes+32, curPacket, timeStamp, packetBytes, (const Byte *) &message->at( messageIndex ) ); - if ( !curPacket ) { - errorString_ = "RtMidiOut::sendMessage: could not allocate packet list"; - error( RtError::DRIVER_ERROR ); + Byte buffer[nBytes+(sizeof(MIDIPacketList))]; + ByteCount listSize = sizeof(buffer); + MIDIPacketList *packetList = (MIDIPacketList*)buffer; + MIDIPacket *packet = MIDIPacketListInit( packetList ); + + ByteCount remainingBytes = nBytes; + while (remainingBytes && packet) { + ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket + const Byte* dataStartPtr = (const Byte *) &message->at( nBytes - remainingBytes ); + packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr); + remainingBytes -= bytesForPacket; + } + + if ( !packet ) { + errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Send to any destinations that may have connected to us. + if ( data->endpoint ) { + result = MIDIReceived( data->endpoint, packetList ); + if ( result != noErr ) { + errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; + error( RtMidiError::WARNING, errorString_ ); } - messageIndex += packetBytes; - bytesLeft -= packetBytes; - - // Send to any destinations that may have connected to us. - OSStatus result; - if ( data->endpoint ) { - result = MIDIReceived( data->endpoint, packetList ); - if ( result != noErr ) { - errorString_ = "RtMidiOut::sendMessage: error sending MIDI to virtual destinations."; - error( RtError::WARNING ); - } - } - - // And send to an explicit destination port if we're connected. - if ( connected_ ) { - result = MIDISend( data->port, data->destinationId, packetList ); - if ( result != noErr ) { - errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; - error( RtError::WARNING ); - } + } + + // And send to an explicit destination port if we're connected. + if ( connected_ ) { + result = MIDISend( data->port, data->destinationId, packetList ); + if ( result != noErr ) { + errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; + error( RtMidiError::WARNING, errorString_ ); } } } @@ -667,7 +1079,7 @@ // API information found at: // - http://www.alsa-project.org/documentation.php#Library -#if defined(__LINUX_ALSASEQ__) +#if defined(__LINUX_ALSA__) // The ALSA Sequencer API is based on the use of a callback function for // MIDI input. @@ -675,6 +1087,10 @@ // Thanks to Pedro Lopez-Cabanillas for help with the ALSA sequencer // time stamps and other assorted fixes!!! +// If you don't need timestamping for incoming MIDI events, define the +// preprocessor definition AVOID_TIMESTAMPING to save resources +// associated with the ALSA sequencer queues. + #include #include @@ -685,32 +1101,38 @@ // implementation. struct AlsaMidiData { snd_seq_t *seq; + unsigned int portNum; int vport; snd_seq_port_subscribe_t *subscription; snd_midi_event_t *coder; unsigned int bufferSize; unsigned char *buffer; pthread_t thread; + pthread_t dummy_thread_id; unsigned long long lastTime; int queue_id; // an input queue is needed to get timestamped events + int trigger_fds[2]; }; #define PORT_TYPE( pinfo, bits ) ((snd_seq_port_info_get_capability(pinfo) & (bits)) == (bits)) //*********************************************************************// // API: LINUX ALSA -// Class Definitions: RtMidiIn +// Class Definitions: MidiInAlsa //*********************************************************************// -extern "C" void *alsaMidiHandler( void *ptr ) +static void *alsaMidiHandler( void *ptr ) { - RtMidiIn::RtMidiInData *data = static_cast (ptr); + MidiInApi::RtMidiInData *data = static_cast (ptr); AlsaMidiData *apiData = static_cast (data->apiData); long nBytes; unsigned long long time, lastTime; bool continueSysex = false; - RtMidiIn::MidiMessage message; + bool doDecode = false; + MidiInApi::MidiMessage message; + int poll_fd_count; + struct pollfd *poll_fds; snd_seq_event_t *ev; int result; @@ -718,72 +1140,93 @@ result = snd_midi_event_new( 0, &apiData->coder ); if ( result < 0 ) { data->doInput = false; - cerr << "\nRtMidiIn::alsaMidiHandler: error initializing MIDI event parser!\n\n"; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing MIDI event parser!\n\n"; return 0; } unsigned char *buffer = (unsigned char *) malloc( apiData->bufferSize ); if ( buffer == NULL ) { data->doInput = false; - cerr << "\nRtMidiIn::alsaMidiHandler: error initializing buffer memory!\n\n"; + snd_midi_event_free( apiData->coder ); + apiData->coder = 0; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: error initializing buffer memory!\n\n"; return 0; } snd_midi_event_init( apiData->coder ); snd_midi_event_no_status( apiData->coder, 1 ); // suppress running status messages + poll_fd_count = snd_seq_poll_descriptors_count( apiData->seq, POLLIN ) + 1; + poll_fds = (struct pollfd*)alloca( poll_fd_count * sizeof( struct pollfd )); + snd_seq_poll_descriptors( apiData->seq, poll_fds + 1, poll_fd_count - 1, POLLIN ); + poll_fds[0].fd = apiData->trigger_fds[0]; + poll_fds[0].events = POLLIN; + while ( data->doInput ) { if ( snd_seq_event_input_pending( apiData->seq, 1 ) == 0 ) { - // No data pending ... sleep a bit. - usleep( 1000 ); + // No data pending + if ( poll( poll_fds, poll_fd_count, -1) >= 0 ) { + if ( poll_fds[0].revents & POLLIN ) { + bool dummy; + int res = read( poll_fds[0].fd, &dummy, sizeof(dummy) ); + (void) res; + } + } continue; } // If here, there should be data. result = snd_seq_event_input( apiData->seq, &ev ); if ( result == -ENOSPC ) { - cerr << "\nRtMidiIn::alsaMidiHandler: MIDI input buffer overrun!\n\n"; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: MIDI input buffer overrun!\n\n"; continue; } else if ( result <= 0 ) { - cerr << "RtMidiIn::alsaMidiHandler: unknown MIDI input error!\n"; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: unknown MIDI input error!\n"; + perror("System reports"); continue; } // This is a bit weird, but we now have to decode an ALSA MIDI // event (back) into MIDI bytes. We'll ignore non-MIDI types. - if ( !continueSysex ) - message.bytes.clear(); - + if ( !continueSysex ) message.bytes.clear(); + + doDecode = false; switch ( ev->type ) { - case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_SUBSCRIBED: #if defined(__RTMIDI_DEBUG__) - cout << "RtMidiIn::alsaMidiHandler: port connection made!\n"; + std::cout << "MidiInAlsa::alsaMidiHandler: port connection made!\n"; #endif break; - case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: #if defined(__RTMIDI_DEBUG__) - SVDEBUG << "RtMidiIn::alsaMidiHandler: port connection has closed!\n"; - // FIXME: this is called for all unsubscribe events, even ones - //not related to this particular connection. As it stands, I - //see no data provided in the "source" and "dest" fields so - //there is nothing we can do about this at this time. - // cout << "sender = " << ev->source.client << ", dest = " << ev->dest.port << endl; + std::cerr << "MidiInAlsa::alsaMidiHandler: port connection has closed!\n"; + std::cout << "sender = " << (int) ev->data.connect.sender.client << ":" + << (int) ev->data.connect.sender.port + << ", dest = " << (int) ev->data.connect.dest.client << ":" + << (int) ev->data.connect.dest.port + << std::endl; #endif - //data->doInput = false; break; case SND_SEQ_EVENT_QFRAME: // MIDI time code - if ( data->ignoreFlags & 0x02 ) break; - - case SND_SEQ_EVENT_TICK: // MIDI timing tick - if ( data->ignoreFlags & 0x02 ) break; + if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_TICK: // 0xF9 ... MIDI timing tick + if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_CLOCK: // 0xF8 ... MIDI timing (clock) tick + if ( !( data->ignoreFlags & 0x02 ) ) doDecode = true; + break; case SND_SEQ_EVENT_SENSING: // Active sensing - if ( data->ignoreFlags & 0x04 ) break; - - case SND_SEQ_EVENT_SYSEX: + if ( !( data->ignoreFlags & 0x04 ) ) doDecode = true; + break; + + case SND_SEQ_EVENT_SYSEX: if ( (data->ignoreFlags & 0x01) ) break; if ( ev->data.ext.len > apiData->bufferSize ) { apiData->bufferSize = ev->data.ext.len; @@ -791,84 +1234,128 @@ buffer = (unsigned char *) malloc( apiData->bufferSize ); if ( buffer == NULL ) { data->doInput = false; - cerr << "\nRtMidiIn::alsaMidiHandler: error resizing buffer memory!\n\n"; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: error resizing buffer memory!\n\n"; break; } } + doDecode = true; + break; default: + doDecode = true; + } + + if ( doDecode ) { + nBytes = snd_midi_event_decode( apiData->coder, buffer, apiData->bufferSize, ev ); - if ( nBytes <= 0 ) { + if ( nBytes > 0 ) { + // The ALSA sequencer has a maximum buffer size for MIDI sysex + // events of 256 bytes. If a device sends sysex messages larger + // than this, they are segmented into 256 byte chunks. So, + // we'll watch for this and concatenate sysex chunks into a + // single sysex message if necessary. + if ( !continueSysex ) + message.bytes.assign( buffer, &buffer[nBytes] ); + else + message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); + + continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); + if ( !continueSysex ) { + + // Calculate the time stamp: + message.timeStamp = 0.0; + + // Method 1: Use the system time. + //(void)gettimeofday(&tv, (struct timezone *)NULL); + //time = (tv.tv_sec * 1000000) + tv.tv_usec; + + // Method 2: Use the ALSA sequencer event time data. + // (thanks to Pedro Lopez-Cabanillas!). + time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); + lastTime = time; + time -= apiData->lastTime; + apiData->lastTime = lastTime; + if ( data->firstMessage == true ) + data->firstMessage = false; + else + message.timeStamp = time * 0.000001; + } + else { #if defined(__RTMIDI_DEBUG__) - cerr << "\nRtMidiIn::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; + std::cerr << "\nMidiInAlsa::alsaMidiHandler: event parsing error or not a MIDI event!\n\n"; #endif - break; + } } - - // The ALSA sequencer has a maximum buffer size for MIDI sysex - // events of 256 bytes. If a device sends sysex messages larger - // than this, they are segmented into 256 byte chunks. So, - // we'll watch for this and concatenate sysex chunks into a - // single sysex message if necessary. - if ( !continueSysex ) - message.bytes.assign( buffer, &buffer[nBytes] ); - else - message.bytes.insert( message.bytes.end(), buffer, &buffer[nBytes] ); - - continueSysex = ( ( ev->type == SND_SEQ_EVENT_SYSEX ) && ( message.bytes.back() != 0xF7 ) ); - if ( continueSysex ) - break; - - // Calculate the time stamp: - message.timeStamp = 0.0; - - // Method 1: Use the system time. - //(void)gettimeofday(&tv, (struct timezone *)NULL); - //time = (tv.tv_sec * 1000000) + tv.tv_usec; - - // Method 2: Use the ALSA sequencer event time data. - // (thanks to Pedro Lopez-Cabanillas!). - time = ( ev->time.time.tv_sec * 1000000 ) + ( ev->time.time.tv_nsec/1000 ); - lastTime = time; - time -= apiData->lastTime; - apiData->lastTime = lastTime; - if ( data->firstMessage == true ) - data->firstMessage = false; - else - message.timeStamp = double(time) * 0.000001; } - snd_seq_free_event(ev); - if ( message.bytes.size() == 0 ) continue; - - if ( data->usingCallback && !continueSysex ) { + snd_seq_free_event( ev ); + if ( message.bytes.size() == 0 || continueSysex ) continue; + + if ( data->usingCallback ) { RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; callback( message.timeStamp, &message.bytes, data->userData ); } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queueLimit > data->queue.size() ) - data->queue.push( message ); + if ( data->queue.size < data->queue.ringSize ) { + data->queue.ring[data->queue.back++] = message; + if ( data->queue.back == data->queue.ringSize ) + data->queue.back = 0; + data->queue.size++; + } else - cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + std::cerr << "\nMidiInAlsa: message queue limit reached!!\n\n"; } } if ( buffer ) free( buffer ); snd_midi_event_free( apiData->coder ); apiData->coder = 0; + apiData->thread = apiData->dummy_thread_id; return 0; } -void RtMidiIn :: initialize( const std::string& clientName ) +MidiInAlsa :: MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +{ + initialize( clientName ); +} + +MidiInAlsa :: ~MidiInAlsa() +{ + // Close a connection if it exists. + closePort(); + + // Shutdown the input thread. + AlsaMidiData *data = static_cast (apiData_); + if ( inputData_.doInput ) { + inputData_.doInput = false; + int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); + (void) res; + if ( !pthread_equal(data->thread, data->dummy_thread_id) ) + pthread_join( data->thread, NULL ); + } + + // Cleanup. + close ( data->trigger_fds[0] ); + close ( data->trigger_fds[1] ); + if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); +#ifndef AVOID_TIMESTAMPING + snd_seq_free_queue( data->seq, data->queue_id ); +#endif + snd_seq_close( data->seq ); + delete data; +} + +void MidiInAlsa :: initialize( const std::string& clientName ) { // Set up the ALSA sequencer client. snd_seq_t *seq; int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); if ( result < 0 ) { - errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; - error( RtError::DRIVER_ERROR ); - } + errorString_ = "MidiInAlsa::initialize: error creating ALSA sequencer client object."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } // Set client name. snd_seq_set_client_name( seq, clientName.c_str() ); @@ -876,11 +1363,24 @@ // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; data->seq = seq; + data->portNum = -1; data->vport = -1; + data->subscription = 0; + data->dummy_thread_id = pthread_self(); + data->thread = data->dummy_thread_id; + data->trigger_fds[0] = -1; + data->trigger_fds[1] = -1; apiData_ = (void *) data; inputData_.apiData = (void *) data; + if ( pipe(data->trigger_fds) == -1 ) { + errorString_ = "MidiInAlsa::initialize: error creating pipe objects."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + // Create the input queue +#ifndef AVOID_TIMESTAMPING data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); // Set arbitrary tempo (mm=100) and resolution (240) snd_seq_queue_tempo_t *qtempo; @@ -889,67 +1389,110 @@ snd_seq_queue_tempo_set_ppq(qtempo, 240); snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); snd_seq_drain_output(data->seq); +#endif } // This function is used to count or get the pinfo structure for a given port number. unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ) { - snd_seq_client_info_t *cinfo; + snd_seq_client_info_t *cinfo; int client; int count = 0; - snd_seq_client_info_alloca( &cinfo ); - - snd_seq_client_info_set_client( cinfo, -1 ); - while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { + snd_seq_client_info_alloca( &cinfo ); + + snd_seq_client_info_set_client( cinfo, -1 ); + while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { client = snd_seq_client_info_get_client( cinfo ); if ( client == 0 ) continue; - // Reset query info - snd_seq_port_info_set_client( pinfo, client ); - snd_seq_port_info_set_port( pinfo, -1 ); - while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { + // Reset query info + snd_seq_port_info_set_client( pinfo, client ); + snd_seq_port_info_set_port( pinfo, -1 ); + while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { unsigned int atyp = snd_seq_port_info_get_type( pinfo ); - if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; + if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) && + ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue; unsigned int caps = snd_seq_port_info_get_capability( pinfo ); if ( ( caps & type ) != type ) continue; if ( count == portNumber ) return 1; - count++; - } - } + ++count; + } + } // If a negative portNumber was used, return the port count. if ( portNumber < 0 ) return count; return 0; } -void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) +unsigned int MidiInAlsa :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); + + AlsaMidiData *data = static_cast (apiData_); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); +} + +std::string MidiInAlsa :: getPortName( unsigned int portNumber ) +{ + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_client_info_alloca( &cinfo ); + snd_seq_port_info_alloca( &pinfo ); + + std::string stringName; + AlsaMidiData *data = static_cast (apiData_); + if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { + int cnum = snd_seq_port_info_get_client( pinfo ); + snd_seq_get_any_client_info( data->seq, cnum, cinfo ); + std::ostringstream os; + os << snd_seq_client_info_get_name( cinfo ); + os << " "; // These lines added to make sure devices are listed + os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names + os << ":"; + os << snd_seq_port_info_get_port( pinfo ); + stringName = os.str(); + return stringName; + } + + // If we get here, we didn't find a match. + errorString_ = "MidiInAlsa::getPortName: error looking for port name!"; + error( RtMidiError::WARNING, errorString_ ); + return stringName; +} + +void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { - errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; - error( RtError::WARNING ); + errorString_ = "MidiInAlsa::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } unsigned int nSrc = this->getPortCount(); - if (nSrc < 1) { - errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; - error( RtError::NO_DEVICES_FOUND ); + if ( nSrc < 1 ) { + errorString_ = "MidiInAlsa::openPort: no MIDI input sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; } - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - std::ostringstream ost; + snd_seq_port_info_t *src_pinfo; + snd_seq_port_info_alloca( &src_pinfo ); AlsaMidiData *data = static_cast (apiData_); - if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { - ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + if ( portInfo( data->seq, src_pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { + std::ostringstream ost; + ost << "MidiInAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; } - snd_seq_addr_t sender, receiver; - sender.client = (unsigned char)snd_seq_port_info_get_client( pinfo ); - sender.port = (unsigned char)snd_seq_port_info_get_port( pinfo ); - receiver.client = (unsigned char)snd_seq_client_id( data->seq ); + sender.client = snd_seq_port_info_get_client( src_pinfo ); + sender.port = snd_seq_port_info_get_port( src_pinfo ); + receiver.client = snd_seq_client_id( data->seq ); + + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); if ( data->vport < 0 ) { snd_seq_port_info_set_client( pinfo, 0 ); snd_seq_port_info_set_port( pinfo, 0 ); @@ -960,33 +1503,48 @@ SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); +#endif snd_seq_port_info_set_name(pinfo, portName.c_str() ); data->vport = snd_seq_create_port(data->seq, pinfo); if ( data->vport < 0 ) { - errorString_ = "RtMidiIn::openPort: ALSA error creating input port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInAlsa::openPort: ALSA error creating input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + data->vport = snd_seq_port_info_get_port(pinfo); + } + + receiver.port = data->vport; + + if ( !data->subscription ) { + // Make subscription + if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) { + errorString_ = "MidiInAlsa::openPort: ALSA error allocation port subscription."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + snd_seq_port_subscribe_set_sender(data->subscription, &sender); + snd_seq_port_subscribe_set_dest(data->subscription, &receiver); + if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + errorString_ = "MidiInAlsa::openPort: ALSA error making port connection."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } } - receiver.port = (unsigned char)data->vport; - - // Make subscription - snd_seq_port_subscribe_malloc( &data->subscription ); - snd_seq_port_subscribe_set_sender(data->subscription, &sender); - snd_seq_port_subscribe_set_dest(data->subscription, &receiver); - if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { - errorString_ = "RtMidiIn::openPort: ALSA error making port connection."; - error( RtError::DRIVER_ERROR ); - } - if ( inputData_.doInput == false ) { // Start the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); @@ -996,47 +1554,59 @@ inputData_.doInput = true; int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); pthread_attr_destroy(&attr); - if (err) { + if ( err ) { snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; inputData_.doInput = false; - errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; - error( RtError::THREAD_ERROR ); + errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; + error( RtMidiError::THREAD_ERROR, errorString_ ); + return; } } connected_ = true; } -void RtMidiIn :: openVirtualPort( std::string portName ) +void MidiInAlsa :: openVirtualPort( std::string portName ) { AlsaMidiData *data = static_cast (apiData_); if ( data->vport < 0 ) { snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); snd_seq_port_info_set_capability( pinfo, - SND_SEQ_PORT_CAP_WRITE | - SND_SEQ_PORT_CAP_SUBS_WRITE ); + SND_SEQ_PORT_CAP_WRITE | + SND_SEQ_PORT_CAP_SUBS_WRITE ); snd_seq_port_info_set_type( pinfo, - SND_SEQ_PORT_TYPE_MIDI_GENERIC | - SND_SEQ_PORT_TYPE_APPLICATION ); + SND_SEQ_PORT_TYPE_MIDI_GENERIC | + SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); +#ifndef AVOID_TIMESTAMPING snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); +#endif snd_seq_port_info_set_name(pinfo, portName.c_str()); data->vport = snd_seq_create_port(data->seq, pinfo); if ( data->vport < 0 ) { - errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInAlsa::openVirtualPort: ALSA error creating virtual port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } + data->vport = snd_seq_port_info_get_port(pinfo); } if ( inputData_.doInput == false ) { + // Wait for old thread to stop, if still running + if ( !pthread_equal(data->thread, data->dummy_thread_id) ) + pthread_join( data->thread, NULL ); + // Start the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); @@ -1046,213 +1616,215 @@ inputData_.doInput = true; int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); pthread_attr_destroy(&attr); - if (err) { - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); + if ( err ) { + if ( data->subscription ) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + } inputData_.doInput = false; - errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; - error( RtError::THREAD_ERROR ); + errorString_ = "MidiInAlsa::openPort: error starting MIDI input thread!"; + error( RtMidiError::THREAD_ERROR, errorString_ ); + return; } } } -void RtMidiIn :: closePort( void ) +void MidiInAlsa :: closePort( void ) { + AlsaMidiData *data = static_cast (apiData_); + if ( connected_ ) { - AlsaMidiData *data = static_cast (apiData_); - snd_seq_unsubscribe_port( data->seq, data->subscription ); - snd_seq_port_subscribe_free( data->subscription ); + if ( data->subscription ) { + snd_seq_unsubscribe_port( data->seq, data->subscription ); + snd_seq_port_subscribe_free( data->subscription ); + data->subscription = 0; + } // Stop the input queue +#ifndef AVOID_TIMESTAMPING snd_seq_stop_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); +#endif connected_ = false; } + + // Stop thread to avoid triggering the callback, while the port is intended to be closed + if ( inputData_.doInput ) { + inputData_.doInput = false; + int res = write( data->trigger_fds[1], &inputData_.doInput, sizeof(inputData_.doInput) ); + (void) res; + if ( !pthread_equal(data->thread, data->dummy_thread_id) ) + pthread_join( data->thread, NULL ); + } } -RtMidiIn :: ~RtMidiIn() +//*********************************************************************// +// API: LINUX ALSA +// Class Definitions: MidiOutAlsa +//*********************************************************************// + +MidiOutAlsa :: MidiOutAlsa( const std::string clientName ) : MidiOutApi() +{ + initialize( clientName ); +} + +MidiOutAlsa :: ~MidiOutAlsa() { // Close a connection if it exists. closePort(); - // Shutdown the input thread. + // Cleanup. AlsaMidiData *data = static_cast (apiData_); - if ( inputData_.doInput ) { - inputData_.doInput = false; - pthread_join( data->thread, NULL ); - } - - // Cleanup. if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); - snd_seq_free_queue( data->seq, data->queue_id ); + if ( data->coder ) snd_midi_event_free( data->coder ); + if ( data->buffer ) free( data->buffer ); snd_seq_close( data->seq ); delete data; } -unsigned int RtMidiIn :: getPortCount() +void MidiOutAlsa :: initialize( const std::string& clientName ) { - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); + // Set up the ALSA sequencer client. + snd_seq_t *seq; + int result1 = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); + if ( result1 < 0 ) { + errorString_ = "MidiOutAlsa::initialize: error creating ALSA sequencer client object."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Set client name. + snd_seq_set_client_name( seq, clientName.c_str() ); + + // Save our api-specific connection information. + AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; + data->seq = seq; + data->portNum = -1; + data->vport = -1; + data->bufferSize = 32; + data->coder = 0; + data->buffer = 0; + int result = snd_midi_event_new( data->bufferSize, &data->coder ); + if ( result < 0 ) { + delete data; + errorString_ = "MidiOutAlsa::initialize: error initializing MIDI event parser!\n\n"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + data->buffer = (unsigned char *) malloc( data->bufferSize ); + if ( data->buffer == NULL ) { + delete data; + errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; + error( RtMidiError::MEMORY_ERROR, errorString_ ); + return; + } + snd_midi_event_init( data->coder ); + apiData_ = (void *) data; +} + +unsigned int MidiOutAlsa :: getPortCount() +{ + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast (apiData_); - return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 ); + return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); } -std::string RtMidiIn :: getPortName( unsigned int portNumber ) +std::string MidiOutAlsa :: getPortName( unsigned int portNumber ) { snd_seq_client_info_t *cinfo; snd_seq_port_info_t *pinfo; snd_seq_client_info_alloca( &cinfo ); snd_seq_port_info_alloca( &pinfo ); - AlsaMidiData *data = static_cast (apiData_); - if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { - int cnum = snd_seq_port_info_get_client( pinfo ); - snd_seq_get_any_client_info( data->seq, cnum, cinfo ); - std::ostringstream os; - os << snd_seq_client_info_get_name( cinfo ); - os << ":"; - os << snd_seq_port_info_get_port( pinfo ); - std::string stringName = os.str(); - return stringName; - } - - // If we get here, we didn't find a match. - errorString_ = "RtMidiIn::getPortName: error looking for port name!"; - error( RtError::INVALID_PARAMETER ); - return 0; -} - -//*********************************************************************// -// API: LINUX ALSA -// Class Definitions: RtMidiOut -//*********************************************************************// - -unsigned int RtMidiOut :: getPortCount() -{ - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - - AlsaMidiData *data = static_cast (apiData_); - return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 ); -} - -std::string RtMidiOut :: getPortName( unsigned int portNumber ) -{ - snd_seq_client_info_t *cinfo; - snd_seq_port_info_t *pinfo; - snd_seq_client_info_alloca( &cinfo ); - snd_seq_port_info_alloca( &pinfo ); - + std::string stringName; AlsaMidiData *data = static_cast (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { int cnum = snd_seq_port_info_get_client(pinfo); snd_seq_get_any_client_info( data->seq, cnum, cinfo ); std::ostringstream os; os << snd_seq_client_info_get_name(cinfo); + os << " "; // These lines added to make sure devices are listed + os << snd_seq_port_info_get_client( pinfo ); // with full portnames added to ensure individual device names os << ":"; os << snd_seq_port_info_get_port(pinfo); - std::string stringName = os.str(); + stringName = os.str(); return stringName; } // If we get here, we didn't find a match. - errorString_ = "RtMidiOut::getPortName: error looking for port name!"; - error( RtError::INVALID_PARAMETER ); - return 0; + errorString_ = "MidiOutAlsa::getPortName: error looking for port name!"; + error( RtMidiError::WARNING, errorString_ ); + return stringName; } -void RtMidiOut :: initialize( const std::string& clientName ) -{ - // Set up the ALSA sequencer client. - snd_seq_t *seq; - int result = snd_seq_open( &seq, "default", SND_SEQ_OPEN_OUTPUT, SND_SEQ_NONBLOCK ); - if ( result < 0 ) { - errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; - error( RtError::DRIVER_ERROR ); - } - - // Set client name. - snd_seq_set_client_name( seq, clientName.c_str() ); - - // Save our api-specific connection information. - AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; - data->seq = seq; - data->vport = -1; - data->bufferSize = 32; - data->coder = 0; - data->buffer = 0; - result = snd_midi_event_new( data->bufferSize, &data->coder ); - if ( result < 0 ) { - delete data; - errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n"; - error( RtError::DRIVER_ERROR ); - } - data->buffer = (unsigned char *) malloc( data->bufferSize ); - if ( data->buffer == NULL ) { - delete data; - errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; - error( RtError::MEMORY_ERROR ); - } - snd_midi_event_init( data->coder ); - apiData_ = (void *) data; -} - -void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) +void MidiOutAlsa :: openPort( unsigned int portNumber, const std::string portName ) { if ( connected_ ) { - errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; - error( RtError::WARNING ); + errorString_ = "MidiOutAlsa::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } unsigned int nSrc = this->getPortCount(); if (nSrc < 1) { - errorString_ = "RtMidiOut::openPort: no MIDI output sources found!"; - error( RtError::NO_DEVICES_FOUND ); + errorString_ = "MidiOutAlsa::openPort: no MIDI output sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; } - snd_seq_port_info_t *pinfo; - snd_seq_port_info_alloca( &pinfo ); - std::ostringstream ost; + snd_seq_port_info_t *pinfo; + snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { - ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + std::ostringstream ost; + ost << "MidiOutAlsa::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; } snd_seq_addr_t sender, receiver; - receiver.client = (unsigned char)snd_seq_port_info_get_client( pinfo ); - receiver.port = (unsigned char)snd_seq_port_info_get_port( pinfo ); - sender.client = (unsigned char)snd_seq_client_id( data->seq ); + receiver.client = snd_seq_port_info_get_client( pinfo ); + receiver.port = snd_seq_port_info_get_port( pinfo ); + sender.client = snd_seq_client_id( data->seq ); if ( data->vport < 0 ) { data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); if ( data->vport < 0 ) { - errorString_ = "RtMidiOut::openPort: ALSA error creating output port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutAlsa::openPort: ALSA error creating output port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } } - sender.port = (unsigned char)data->vport; + sender.port = data->vport; // Make subscription - snd_seq_port_subscribe_malloc( &data->subscription ); + if (snd_seq_port_subscribe_malloc( &data->subscription ) < 0) { + snd_seq_port_subscribe_free( data->subscription ); + errorString_ = "MidiOutAlsa::openPort: error allocating port subscription."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } snd_seq_port_subscribe_set_sender(data->subscription, &sender); snd_seq_port_subscribe_set_dest(data->subscription, &receiver); snd_seq_port_subscribe_set_time_update(data->subscription, 1); snd_seq_port_subscribe_set_time_real(data->subscription, 1); if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { - errorString_ = "RtMidiOut::openPort: ALSA error making port connection."; - error( RtError::DRIVER_ERROR ); + snd_seq_port_subscribe_free( data->subscription ); + errorString_ = "MidiOutAlsa::openPort: ALSA error making port connection."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } connected_ = true; } -void RtMidiOut :: closePort( void ) +void MidiOutAlsa :: closePort( void ) { if ( connected_ ) { AlsaMidiData *data = static_cast (apiData_); @@ -1262,73 +1834,62 @@ } } -void RtMidiOut :: openVirtualPort( std::string portName ) +void MidiOutAlsa :: openVirtualPort( std::string portName ) { AlsaMidiData *data = static_cast (apiData_); if ( data->vport < 0 ) { data->vport = snd_seq_create_simple_port( data->seq, portName.c_str(), SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, - SND_SEQ_PORT_TYPE_MIDI_GENERIC ); + SND_SEQ_PORT_TYPE_MIDI_GENERIC|SND_SEQ_PORT_TYPE_APPLICATION ); if ( data->vport < 0 ) { - errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutAlsa::openVirtualPort: ALSA error creating virtual port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); } } } -RtMidiOut :: ~RtMidiOut() -{ - // Close a connection if it exists. - closePort(); - - // Cleanup. - AlsaMidiData *data = static_cast (apiData_); - if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); - if ( data->coder ) snd_midi_event_free( data->coder ); - if ( data->buffer ) free( data->buffer ); - snd_seq_close( data->seq ); - delete data; -} - -void RtMidiOut :: sendMessage( std::vector *message ) +void MidiOutAlsa :: sendMessage( std::vector *message ) { int result; AlsaMidiData *data = static_cast (apiData_); - unsigned int nBytes = (unsigned int) message->size(); + unsigned int nBytes = message->size(); if ( nBytes > data->bufferSize ) { data->bufferSize = nBytes; result = snd_midi_event_resize_buffer ( data->coder, nBytes); if ( result != 0 ) { - errorString_ = "RtMidiOut::sendMessage: ALSA error resizing MIDI event buffer."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutAlsa::sendMessage: ALSA error resizing MIDI event buffer."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } free (data->buffer); data->buffer = (unsigned char *) malloc( data->bufferSize ); if ( data->buffer == NULL ) { - errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; - error( RtError::MEMORY_ERROR ); + errorString_ = "MidiOutAlsa::initialize: error allocating buffer memory!\n\n"; + error( RtMidiError::MEMORY_ERROR, errorString_ ); + return; } } snd_seq_event_t ev; snd_seq_ev_clear(&ev); - snd_seq_ev_set_source(&ev, (unsigned char) data->vport); + snd_seq_ev_set_source(&ev, data->vport); snd_seq_ev_set_subs(&ev); snd_seq_ev_set_direct(&ev); - for ( unsigned int i=0; ibuffer[i] = message->at(i); - result = (int) snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); + for ( unsigned int i=0; ibuffer[i] = message->at(i); + result = snd_midi_event_encode( data->coder, data->buffer, (long)nBytes, &ev ); if ( result < (int)nBytes ) { - errorString_ = "RtMidiOut::sendMessage: event parsing error!"; - error( RtError::WARNING ); + errorString_ = "MidiOutAlsa::sendMessage: event parsing error!"; + error( RtMidiError::WARNING, errorString_ ); return; } // Send the event. result = snd_seq_event_output(data->seq, &ev); if ( result < 0 ) { - errorString_ = "RtMidiOut::sendMessage: error sending MIDI message to port."; - error( RtError::WARNING ); + errorString_ = "MidiOutAlsa::sendMessage: error sending MIDI message to port."; + error( RtMidiError::WARNING, errorString_ ); + return; } snd_seq_drain_output(data->seq); } @@ -1337,406 +1898,6 @@ //*********************************************************************// -// API: IRIX MD -//*********************************************************************// - -// API information gleamed from: -// http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?cmd=getdoc&coll=0650&db=man&fname=3%20mdIntro - -// If the Makefile doesn't work, try the following: -// CC -o midiinfo -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiinfo.cpp -lpthread -lmd -// CC -o midiout -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp midiout.cpp -lpthread -lmd -// CC -o qmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp qmidiin.cpp -lpthread -lmd -// CC -o cmidiin -LANG:std -D__IRIX_MD__ -I../ ../RtMidi.cpp cmidiin.cpp -lpthread -lmd - -#if defined(__IRIX_MD__) - -#include -#include -#include - -// Irix MIDI header file. -#include - -// A structure to hold variables related to the IRIX API -// implementation. -struct IrixMidiData { - MDport port; - pthread_t thread; -}; - -//*********************************************************************// -// API: IRIX -// Class Definitions: RtMidiIn -//*********************************************************************// - -extern "C" void *irixMidiHandler( void *ptr ) -{ - RtMidiIn::RtMidiInData *data = static_cast (ptr); - IrixMidiData *apiData = static_cast (data->apiData); - - bool continueSysex = false; - unsigned char status; - unsigned short size; - MDevent event; - int fd = mdGetFd( apiData->port ); - if ( fd < 0 ) { - data->doInput = false; - cerr << "\nRtMidiIn::irixMidiHandler: error getting port descriptor!\n\n"; - return 0; - } - - fd_set mask, rmask; - FD_ZERO( &mask ); - FD_SET( fd, &mask ); - struct timeval timeout = {0, 0}; - RtMidiIn::MidiMessage message; - int result; - - while ( data->doInput ) { - - rmask = mask; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - if ( select( fd+1, &rmask, NULL, NULL, &timeout ) <= 0 ) { - // No data pending ... sleep a bit. - usleep( 1000 ); - continue; - } - - // If here, there should be data. - result = mdReceive( apiData->port, &event, 1); - if ( result <= 0 ) { - cerr << "\nRtMidiIn::irixMidiHandler: MIDI input read error!\n\n"; - continue; - } - - message.timeStamp = event.stamp * 0.000000001; - - size = 0; - status = event.msg[0]; - if ( !(status & 0x80) ) continue; - if ( status == 0xF0 ) { - // Sysex message ... can be segmented across multiple messages. - if ( !(data->ignoreFlags & 0x01) ) { - if ( continueSysex ) { - // We have a continuing, segmented sysex message. Append - // the new bytes to our existing message. - for ( int i=0; iusingCallback && message.bytes.size() > 0 ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( data->queueLimit > data->queue.size() ) - data->queue.push( message ); - else - cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; - } - message.bytes.clear(); - } - } - } - mdFree( NULL ); - continue; - } - else if ( status < 0xC0 ) size = 3; - else if ( status < 0xE0 ) size = 2; - else if ( status < 0xF0 ) size = 3; - else if ( status < 0xF3 ) { - if ( status == 0xF1 && !(data->ignoreFlags & 0x02) ) { - // A MIDI time code message and we're not ignoring it. - size = 3; - } - } - else if ( status == 0xF3 ) size = 2; - else if ( status == 0xF8 ) { - if ( !(data->ignoreFlags & 0x02) ) { - // A MIDI timing tick message and we're not ignoring it. - size = 1; - } - } - else if ( status == 0xFE ) { // MIDI active sensing - if ( !(data->ignoreFlags & 0x04) ) - size = 1; - } - else size = 1; - - // Copy the MIDI data to our vector. - if ( size ) { - message.bytes.assign( &event.msg[0], &event.msg[size] ); - // Invoke the user callback function or queue the message. - if ( data->usingCallback ) { - RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) data->userCallback; - callback( message.timeStamp, &message.bytes, data->userData ); - } - else { - // As long as we haven't reached our queue size limit, push the message. - if ( data->queueLimit > data->queue.size() ) - data->queue.push( message ); - else - cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; - } - message.bytes.clear(); - } - } - - return 0; -} - -void RtMidiIn :: initialize( const std::string& /*clientName*/ ) -{ - // Initialize the Irix MIDI system. At the moment, we will not - // worry about a return value of zero (ports) because there is a - // chance the user could plug something in after instantiation. - int nPorts = mdInit(); - - // Create our api-specific connection information. - IrixMidiData *data = (IrixMidiData *) new IrixMidiData; - apiData_ = (void *) data; - inputData_.apiData = (void *) data; -} - -void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) -{ - if ( connected_ ) { - errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; - error( RtError::WARNING ); - return; - } - - int nPorts = mdInit(); - if (nPorts < 1) { - errorString_ = "RtMidiIn::openPort: no Irix MIDI input sources found!"; - error( RtError::NO_DEVICES_FOUND ); - } - - std::ostringstream ost; - if ( portNumber >= nPorts ) { - ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - - IrixMidiData *data = static_cast (apiData_); - data->port = mdOpenInPort( mdGetName(portNumber) ); - if ( data->port == NULL ) { - ost << "RtMidiIn::openPort: Irix error opening the port (" << portNumber << ")."; - errorString_ = ost.str(); - error( RtError::DRIVER_ERROR ); - } - mdSetStampMode(data->port, MD_DELTASTAMP); - - // Start our MIDI input thread. - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); - pthread_attr_setschedpolicy(&attr, SCHED_RR); - - inputData_.doInput = true; - int err = pthread_create(&data->thread, &attr, irixMidiHandler, &inputData_); - pthread_attr_destroy(&attr); - if (err) { - mdClosePort( data->port ); - inputData_.doInput = false; - errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; - error( RtError::THREAD_ERROR ); - } - - connected_ = true; -} - -void RtMidiIn :: openVirtualPort( std::string portName ) -{ - // This function cannot be implemented for the Irix MIDI API. - errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Irix MIDI API!"; - error( RtError::WARNING ); -} - -void RtMidiIn :: closePort( void ) -{ - if ( connected_ ) { - IrixMidiData *data = static_cast (apiData_); - mdClosePort( data->port ); - connected_ = false; - - // Shutdown the input thread. - inputData_.doInput = false; - pthread_join( data->thread, NULL ); - } -} - -RtMidiIn :: ~RtMidiIn() -{ - // Close a connection if it exists. - closePort(); - - // Cleanup. - IrixMidiData *data = static_cast (apiData_); - delete data; -} - -unsigned int RtMidiIn :: getPortCount() -{ - int nPorts = mdInit(); - if ( nPorts >= 0 ) return nPorts; - else return 0; -} - -std::string RtMidiIn :: getPortName( unsigned int portNumber ) -{ - int nPorts = mdInit(); - - std::ostringstream ost; - if ( portNumber >= nPorts ) { - ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - - std::string stringName = std::string( mdGetName( portNumber ) ); - return stringName; -} - -//*********************************************************************// -// API: IRIX MD -// Class Definitions: RtMidiOut -//*********************************************************************// - -unsigned int RtMidiOut :: getPortCount() -{ - int nPorts = mdInit(); - if ( nPorts >= 0 ) return nPorts; - else return 0; -} - -std::string RtMidiOut :: getPortName( unsigned int portNumber ) -{ - int nPorts = mdInit(); - - std::ostringstream ost; - if ( portNumber >= nPorts ) { - ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - - std::string stringName = std::string( mdGetName( portNumber ) ); - return stringName; -} - -void RtMidiOut :: initialize( const std::string& /*clientName*/ ) -{ - // Initialize the Irix MIDI system. At the moment, we will not - // worry about a return value of zero (ports) because there is a - // chance the user could plug something in after instantiation. - int nPorts = mdInit(); - - // Create our api-specific connection information. - IrixMidiData *data = (IrixMidiData *) new IrixMidiData; - apiData_ = (void *) data; -} - -void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ ) -{ - if ( connected_ ) { - errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; - error( RtError::WARNING ); - return; - } - - int nPorts = mdInit(); - if (nPorts < 1) { - errorString_ = "RtMidiOut::openPort: no Irix MIDI output sources found!"; - error( RtError::NO_DEVICES_FOUND ); - } - - std::ostringstream ost; - if ( portNumber >= nPorts ) { - ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - - IrixMidiData *data = static_cast (apiData_); - data->port = mdOpenOutPort( mdGetName(portNumber) ); - if ( data->port == NULL ) { - ost << "RtMidiOut::openPort: Irix error opening the port (" << portNumber << ")."; - errorString_ = ost.str(); - error( RtError::DRIVER_ERROR ); - } - mdSetStampMode(data->port, MD_NOSTAMP); - - connected_ = true; -} - -void RtMidiOut :: closePort( void ) -{ - if ( connected_ ) { - IrixMidiData *data = static_cast (apiData_); - mdClosePort( data->port ); - connected_ = false; - } -} - -void RtMidiOut :: openVirtualPort( std::string portName ) -{ - // This function cannot be implemented for the Irix MIDI API. - errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Irix MIDI API!"; - error( RtError::WARNING ); -} - -RtMidiOut :: ~RtMidiOut() -{ - // Close a connection if it exists. - closePort(); - - // Cleanup. - IrixMidiData *data = static_cast (apiData_); - delete data; -} - -void RtMidiOut :: sendMessage( std::vector *message ) -{ - int result; - MDevent event; - IrixMidiData *data = static_cast (apiData_); - char *buffer = 0; - - unsigned int nBytes = message->size(); - if ( nBytes == 0 ) return; - event.stamp = 0; - if ( message->at(0) == 0xF0 ) { - if ( nBytes < 3 ) return; // check for bogus sysex - event.msg[0] = 0xF0; - event.msglen = nBytes; - buffer = (char *) malloc( nBytes ); - for ( int i=0; iat(i); - event.sysexmsg = buffer; - } - else { - for ( int i=0; iat(i); - } - - // Send the event. - result = mdSend( data->port, &event, 1 ); - if ( buffer ) free( buffer ); - if ( result < 1 ) { - errorString_ = "RtMidiOut::sendMessage: IRIX error sending MIDI message!"; - error( RtError::WARNING ); - return; - } -} - -#endif // __IRIX_MD__ - -//*********************************************************************// // API: Windows Multimedia Library (MM) //*********************************************************************// @@ -1755,38 +1916,42 @@ #include #include +#define RT_SYSEX_BUFFER_SIZE 1024 +#define RT_SYSEX_BUFFER_COUNT 4 + // A structure to hold variables related to the CoreMIDI API // implementation. struct WinMidiData { HMIDIIN inHandle; // Handle to Midi Input Device HMIDIOUT outHandle; // Handle to Midi Output Device DWORD lastTime; - RtMidiIn::MidiMessage message; - LPMIDIHDR sysexBuffer; + MidiInApi::MidiMessage message; + LPMIDIHDR sysexBuffer[RT_SYSEX_BUFFER_COUNT]; + CRITICAL_SECTION _mutex; // [Patrice] see https://groups.google.com/forum/#!topic/mididev/6OUjHutMpEo }; -#define RT_SYSEX_BUFFER_SIZE 1024 - //*********************************************************************// // API: Windows MM -// Class Definitions: RtMidiIn +// Class Definitions: MidiInWinMM //*********************************************************************// -static void CALLBACK midiInputCallback( HMIDIOUT hmin, +static void CALLBACK midiInputCallback( HMIDIIN /*hmin*/, UINT inputStatus, - DWORD instancePtr, - DWORD midiMessage, + DWORD_PTR instancePtr, + DWORD_PTR midiMessage, DWORD timestamp ) { - if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA ) return; - - //RtMidiIn::RtMidiInData *data = static_cast (instancePtr); - RtMidiIn::RtMidiInData *data = (RtMidiIn::RtMidiInData *)instancePtr; + if ( inputStatus != MIM_DATA && inputStatus != MIM_LONGDATA && inputStatus != MIM_LONGERROR ) return; + + //MidiInApi::RtMidiInData *data = static_cast (instancePtr); + MidiInApi::RtMidiInData *data = (MidiInApi::RtMidiInData *)instancePtr; WinMidiData *apiData = static_cast (data->apiData); // Calculate time stamp. - apiData->message.timeStamp = 0.0; - if ( data->firstMessage == true ) data->firstMessage = false; + if ( data->firstMessage == true ) { + apiData->message.timeStamp = 0.0; + data->firstMessage = false; + } else apiData->message.timeStamp = (double) ( timestamp - apiData->lastTime ) * 0.001; apiData->lastTime = timestamp; @@ -1801,11 +1966,11 @@ if ( status < 0xC0 ) nBytes = 3; else if ( status < 0xE0 ) nBytes = 2; else if ( status < 0xF0 ) nBytes = 3; - else if ( status < 0xF3 ) { - // A MIDI time code message and we're ignoring it. - if ( status == 0xF1 && (data->ignoreFlags & 0x02) ) return; - nBytes = 3; + else if ( status == 0xF1 ) { + if ( data->ignoreFlags & 0x02 ) return; + else nBytes = 2; } + else if ( status == 0xF2 ) nBytes = 3; else if ( status == 0xF3 ) nBytes = 2; else if ( status == 0xF8 && (data->ignoreFlags & 0x02) ) { // A MIDI timing tick message and we're ignoring it. @@ -1818,13 +1983,13 @@ // Copy bytes to our MIDI message. unsigned char *ptr = (unsigned char *) &midiMessage; - for ( int i=0; imessage.bytes.push_back( *ptr++ ); + for ( int i=0; imessage.bytes.push_back( *ptr++ ); } - else { // Sysex message ( MIM_LONGDATA ) + else { // Sysex message ( MIM_LONGDATA or MIM_LONGERROR ) MIDIHDR *sysex = ( MIDIHDR *) midiMessage; - if ( !( data->ignoreFlags & 0x01 ) ) { + if ( !( data->ignoreFlags & 0x01 ) && inputStatus != MIM_LONGERROR ) { // Sysex message and we're not ignoring it - for ( int i=0; i<(int)sysex->dwBytesRecorded; i++ ) + for ( int i=0; i<(int)sysex->dwBytesRecorded; ++i ) apiData->message.bytes.push_back( sysex->lpData[i] ); } @@ -1836,11 +2001,13 @@ // buffer when an application closes and in this case, we should // avoid requeueing it, else the computer suddenly reboots after // one or two minutes. - if ( apiData->sysexBuffer->dwBytesRecorded > 0 ) { + if ( apiData->sysexBuffer[sysex->dwUser]->dwBytesRecorded > 0 ) { //if ( sysex->dwBytesRecorded > 0 ) { - MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer, sizeof(MIDIHDR) ); + EnterCriticalSection( &(apiData->_mutex) ); + MMRESULT result = midiInAddBuffer( apiData->inHandle, apiData->sysexBuffer[sysex->dwUser], sizeof(MIDIHDR) ); + LeaveCriticalSection( &(apiData->_mutex) ); if ( result != MMSYSERR_NOERROR ) - cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; + std::cerr << "\nRtMidiIn::midiInputCallback: error sending sysex to Midi device!!\n\n"; if ( data->ignoreFlags & 0x01 ) return; } @@ -1853,24 +2020,45 @@ } else { // As long as we haven't reached our queue size limit, push the message. - if ( data->queueLimit > data->queue.size() ) - data->queue.push( apiData->message ); + if ( data->queue.size < data->queue.ringSize ) { + data->queue.ring[data->queue.back++] = apiData->message; + if ( data->queue.back == data->queue.ringSize ) + data->queue.back = 0; + data->queue.size++; + } else - cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; + std::cerr << "\nRtMidiIn: message queue limit reached!!\n\n"; } // Clear the vector for the next input message. apiData->message.bytes.clear(); } -void RtMidiIn :: initialize( const std::string& /*clientName*/ ) +MidiInWinMM :: MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) +{ + initialize( clientName ); +} + +MidiInWinMM :: ~MidiInWinMM() +{ + // Close a connection if it exists. + closePort(); + + WinMidiData *data = static_cast (apiData_); + DeleteCriticalSection( &(data->_mutex) ); + + // Cleanup. + delete data; +} + +void MidiInWinMM :: initialize( const std::string& /*clientName*/ ) { // We'll issue a warning here if no devices are available but not // throw an error since the user can plugin something later. unsigned int nDevices = midiInGetNumDevs(); if ( nDevices == 0 ) { - errorString_ = "RtMidiIn::initialize: no MIDI input devices currently available."; - error( RtError::WARNING ); + errorString_ = "MidiInWinMM::initialize: no MIDI input devices currently available."; + error( RtMidiError::WARNING, errorString_ ); } // Save our api-specific connection information. @@ -1878,100 +2066,168 @@ apiData_ = (void *) data; inputData_.apiData = (void *) data; data->message.bytes.clear(); // needs to be empty for first input message + + if ( !InitializeCriticalSectionAndSpinCount(&(data->_mutex), 0x00000400) ) { + errorString_ = "MidiInWinMM::initialize: InitializeCriticalSectionAndSpinCount failed."; + error( RtMidiError::WARNING, errorString_ ); + } } -void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) +void MidiInWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { - errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; - error( RtError::WARNING ); + errorString_ = "MidiInWinMM::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } unsigned int nDevices = midiInGetNumDevs(); if (nDevices == 0) { - errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; - error( RtError::NO_DEVICES_FOUND ); + errorString_ = "MidiInWinMM::openPort: no MIDI input sources found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; } - std::ostringstream ost; if ( portNumber >= nDevices ) { - ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + std::ostringstream ost; + ost << "MidiInWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; } WinMidiData *data = static_cast (apiData_); MMRESULT result = midiInOpen( &data->inHandle, portNumber, - (DWORD)&midiInputCallback, - (DWORD)&inputData_, + (DWORD_PTR)&midiInputCallback, + (DWORD_PTR)&inputData_, CALLBACK_FUNCTION ); if ( result != MMSYSERR_NOERROR ) { - errorString_ = "RtMidiIn::openPort: error creating Windows MM MIDI input port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInWinMM::openPort: error creating Windows MM MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } - // Allocate and init the sysex buffer. - data->sysexBuffer = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; - data->sysexBuffer->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; - data->sysexBuffer->dwBufferLength = RT_SYSEX_BUFFER_SIZE; - data->sysexBuffer->dwFlags = 0; - - result = midiInPrepareHeader( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) ); - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; - error( RtError::DRIVER_ERROR ); - } - - // Register the buffer. - result = midiInAddBuffer( data->inHandle, data->sysexBuffer, sizeof(MIDIHDR) ); - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port (AddBuffer)."; - error( RtError::DRIVER_ERROR ); + // Allocate and init the sysex buffers. + for ( int i=0; isysexBuffer[i] = (MIDIHDR*) new char[ sizeof(MIDIHDR) ]; + data->sysexBuffer[i]->lpData = new char[ RT_SYSEX_BUFFER_SIZE ]; + data->sysexBuffer[i]->dwBufferLength = RT_SYSEX_BUFFER_SIZE; + data->sysexBuffer[i]->dwUser = i; // We use the dwUser parameter as buffer indicator + data->sysexBuffer[i]->dwFlags = 0; + + result = midiInPrepareHeader( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (PrepareHeader)."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Register the buffer. + result = midiInAddBuffer( data->inHandle, data->sysexBuffer[i], sizeof(MIDIHDR) ); + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port (AddBuffer)."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } } result = midiInStart( data->inHandle ); if ( result != MMSYSERR_NOERROR ) { midiInClose( data->inHandle ); - errorString_ = "RtMidiIn::openPort: error starting Windows MM MIDI input port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiInWinMM::openPort: error starting Windows MM MIDI input port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } connected_ = true; } -void RtMidiIn :: openVirtualPort( std::string portName ) +void MidiInWinMM :: openVirtualPort( std::string /*portName*/ ) { // This function cannot be implemented for the Windows MM MIDI API. - errorString_ = "RtMidiIn::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; - error( RtError::WARNING ); + errorString_ = "MidiInWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); } -void RtMidiIn :: closePort( void ) +void MidiInWinMM :: closePort( void ) { if ( connected_ ) { WinMidiData *data = static_cast (apiData_); + EnterCriticalSection( &(data->_mutex) ); midiInReset( data->inHandle ); midiInStop( data->inHandle ); - int result = midiInUnprepareHeader(data->inHandle, data->sysexBuffer, sizeof(MIDIHDR)); - delete [] data->sysexBuffer->lpData; - delete [] data->sysexBuffer; - if ( result != MMSYSERR_NOERROR ) { - midiInClose( data->inHandle ); - errorString_ = "RtMidiIn::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; - error( RtError::DRIVER_ERROR ); + for ( int i=0; iinHandle, data->sysexBuffer[i], sizeof(MIDIHDR)); + delete [] data->sysexBuffer[i]->lpData; + delete [] data->sysexBuffer[i]; + if ( result != MMSYSERR_NOERROR ) { + midiInClose( data->inHandle ); + errorString_ = "MidiInWinMM::openPort: error closing Windows MM MIDI input port (midiInUnprepareHeader)."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } } midiInClose( data->inHandle ); connected_ = false; + LeaveCriticalSection( &(data->_mutex) ); } } -RtMidiIn :: ~RtMidiIn() +unsigned int MidiInWinMM :: getPortCount() +{ + return midiInGetNumDevs(); +} + +std::string MidiInWinMM :: getPortName( unsigned int portNumber ) +{ + std::string stringName; + unsigned int nDevices = midiInGetNumDevs(); + if ( portNumber >= nDevices ) { + std::ostringstream ost; + ost << "MidiInWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + MIDIINCAPS deviceCaps; + midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); + +#if defined( UNICODE ) || defined( _UNICODE ) + int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1; + stringName.assign( length, 0 ); + length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL); +#else + stringName = std::string( deviceCaps.szPname ); +#endif + + // Next lines added to add the portNumber to the name so that + // the device's names are sure to be listed with individual names + // even when they have the same brand name + std::ostringstream os; + os << " "; + os << portNumber; + stringName += os.str(); + + return stringName; +} + +//*********************************************************************// +// API: Windows MM +// Class Definitions: MidiOutWinMM +//*********************************************************************// + +MidiOutWinMM :: MidiOutWinMM( const std::string clientName ) : MidiOutApi() +{ + initialize( clientName ); +} + +MidiOutWinMM :: ~MidiOutWinMM() { // Close a connection if it exists. closePort(); @@ -1981,77 +2237,14 @@ delete data; } -unsigned int RtMidiIn :: getPortCount() -{ - return midiInGetNumDevs(); -} - -std::string RtMidiIn :: getPortName( unsigned int portNumber ) -{ - unsigned int nDevices = midiInGetNumDevs(); - if ( portNumber >= nDevices ) { - std::ostringstream ost; - ost << "RtMidiIn::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - - MIDIINCAPS deviceCaps; - midiInGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIINCAPS)); - - // For some reason, we need to copy character by character with - // UNICODE (thanks to Eduardo Coutinho!). - //std::string stringName = std::string( deviceCaps.szPname ); - char nameString[MAXPNAMELEN]; - for( int i=0; i= nDevices ) { - std::ostringstream ost; - ost << "RtMidiOut::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; - errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); - } - - MIDIOUTCAPS deviceCaps; - midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); - - // For some reason, we need to copy character by character with - // UNICODE (thanks to Eduardo Coutinho!). - //std::string stringName = std::string( deviceCaps.szPname ); - char nameString[MAXPNAMELEN]; - for( int i=0; i= nDevices ) { + std::ostringstream ost; + ost << "MidiOutWinMM::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + return stringName; + } + + MIDIOUTCAPS deviceCaps; + midiOutGetDevCaps( portNumber, &deviceCaps, sizeof(MIDIOUTCAPS)); + +#if defined( UNICODE ) || defined( _UNICODE ) + int length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, -1, NULL, 0, NULL, NULL) - 1; + stringName.assign( length, 0 ); + length = WideCharToMultiByte(CP_UTF8, 0, deviceCaps.szPname, static_cast(wcslen(deviceCaps.szPname)), &stringName[0], length, NULL, NULL); +#else + stringName = std::string( deviceCaps.szPname ); +#endif + + // Next lines added to add the portNumber to the name so that + // the device's names are sure to be listed with individual names + // even when they have the same brand name + std::ostringstream os; + os << " "; + os << portNumber; + stringName += os.str(); + + return stringName; +} + +void MidiOutWinMM :: openPort( unsigned int portNumber, const std::string /*portName*/ ) { if ( connected_ ) { - errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; - error( RtError::WARNING ); + errorString_ = "MidiOutWinMM::openPort: a valid connection already exists!"; + error( RtMidiError::WARNING, errorString_ ); return; } unsigned int nDevices = midiOutGetNumDevs(); if (nDevices < 1) { - errorString_ = "RtMidiOut::openPort: no MIDI output destinations found!"; - error( RtError::NO_DEVICES_FOUND ); + errorString_ = "MidiOutWinMM::openPort: no MIDI output destinations found!"; + error( RtMidiError::NO_DEVICES_FOUND, errorString_ ); + return; } - std::ostringstream ost; if ( portNumber >= nDevices ) { - ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; + std::ostringstream ost; + ost << "MidiOutWinMM::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); - error( RtError::INVALID_PARAMETER ); + error( RtMidiError::INVALID_PARAMETER, errorString_ ); + return; } WinMidiData *data = static_cast (apiData_); @@ -2087,14 +2321,15 @@ (DWORD)NULL, CALLBACK_NULL ); if ( result != MMSYSERR_NOERROR ) { - errorString_ = "RtMidiOut::openPort: error creating Windows MM MIDI output port."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutWinMM::openPort: error creating Windows MM MIDI output port."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } connected_ = true; } -void RtMidiOut :: closePort( void ) +void MidiOutWinMM :: closePort( void ) { if ( connected_ ) { WinMidiData *data = static_cast (apiData_); @@ -2104,29 +2339,21 @@ } } -void RtMidiOut :: openVirtualPort( std::string portName ) +void MidiOutWinMM :: openVirtualPort( std::string /*portName*/ ) { // This function cannot be implemented for the Windows MM MIDI API. - errorString_ = "RtMidiOut::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; - error( RtError::WARNING ); + errorString_ = "MidiOutWinMM::openVirtualPort: cannot be implemented in Windows MM MIDI API!"; + error( RtMidiError::WARNING, errorString_ ); } -RtMidiOut :: ~RtMidiOut() +void MidiOutWinMM :: sendMessage( std::vector *message ) { - // Close a connection if it exists. - closePort(); - - // Cleanup. - WinMidiData *data = static_cast (apiData_); - delete data; -} - -void RtMidiOut :: sendMessage( std::vector *message ) -{ - unsigned int nBytes = message->size(); + if ( !connected_ ) return; + + unsigned int nBytes = static_cast(message->size()); if ( nBytes == 0 ) { - errorString_ = "RtMidiOut::sendMessage: message argument is empty!"; - error( RtError::WARNING ); + errorString_ = "MidiOutWinMM::sendMessage: message argument is empty!"; + error( RtMidiError::WARNING, errorString_ ); return; } @@ -2137,12 +2364,13 @@ // Allocate buffer for sysex data. char *buffer = (char *) malloc( nBytes ); if ( buffer == NULL ) { - errorString_ = "RtMidiOut::sendMessage: error allocating sysex message memory!"; - error( RtError::MEMORY_ERROR ); + errorString_ = "MidiOutWinMM::sendMessage: error allocating sysex message memory!"; + error( RtMidiError::MEMORY_ERROR, errorString_ ); + return; } // Copy data to buffer. - for ( unsigned int i=0; iat(i); + for ( unsigned int i=0; iat(i); // Create and prepare MIDIHDR structure. MIDIHDR sysex; @@ -2152,116 +2380,465 @@ result = midiOutPrepareHeader( data->outHandle, &sysex, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { free( buffer ); - errorString_ = "RtMidiOut::sendMessage: error preparing sysex header."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutWinMM::sendMessage: error preparing sysex header."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Send the message. result = midiOutLongMsg( data->outHandle, &sysex, sizeof(MIDIHDR) ); if ( result != MMSYSERR_NOERROR ) { free( buffer ); - errorString_ = "RtMidiOut::sendMessage: error sending sysex message."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutWinMM::sendMessage: error sending sysex message."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; } // Unprepare the buffer and MIDIHDR. while ( MIDIERR_STILLPLAYING == midiOutUnprepareHeader( data->outHandle, &sysex, sizeof (MIDIHDR) ) ) Sleep( 1 ); free( buffer ); - } else { // Channel or system message. // Make sure the message size isn't too big. if ( nBytes > 3 ) { - errorString_ = "RtMidiOut::sendMessage: message size is greater than 3 bytes (and not sysex)!"; - error( RtError::WARNING ); + errorString_ = "MidiOutWinMM::sendMessage: message size is greater than 3 bytes (and not sysex)!"; + error( RtMidiError::WARNING, errorString_ ); return; } // Pack MIDI bytes into double word. DWORD packet; unsigned char *ptr = (unsigned char *) &packet; - for ( unsigned int i=0; iat(i); - ptr++; + ++ptr; } // Send the message immediately. result = midiOutShortMsg( data->outHandle, packet ); if ( result != MMSYSERR_NOERROR ) { - errorString_ = "RtMidiOut::sendMessage: error sending MIDI message."; - error( RtError::DRIVER_ERROR ); + errorString_ = "MidiOutWinMM::sendMessage: error sending MIDI message."; + error( RtMidiError::DRIVER_ERROR, errorString_ ); } } } #endif // __WINDOWS_MM__ -#ifdef __RTMIDI_DUMMY_ONLY__ - -void RtMidiIn :: initialize( const std::string& /*clientName*/ ) + +//*********************************************************************// +// API: UNIX JACK +// +// Written primarily by Alexander Svetalkin, with updates for delta +// time by Gary Scavone, April 2011. +// +// *********************************************************************// + +#if defined(__UNIX_JACK__) + +// JACK header files +#include +#include +#include + +#define JACK_RINGBUFFER_SIZE 16384 // Default size for ringbuffer + +struct JackMidiData { + jack_client_t *client; + jack_port_t *port; + jack_ringbuffer_t *buffSize; + jack_ringbuffer_t *buffMessage; + jack_time_t lastTime; + MidiInApi :: RtMidiInData *rtMidiIn; + }; + +//*********************************************************************// +// API: JACK +// Class Definitions: MidiInJack +//*********************************************************************// + +static int jackProcessIn( jack_nframes_t nframes, void *arg ) { + JackMidiData *jData = (JackMidiData *) arg; + MidiInApi :: RtMidiInData *rtData = jData->rtMidiIn; + jack_midi_event_t event; + jack_time_t time; + + // Is port created? + if ( jData->port == NULL ) return 0; + void *buff = jack_port_get_buffer( jData->port, nframes ); + + // We have midi events in buffer + int evCount = jack_midi_get_event_count( buff ); + for (int j = 0; j < evCount; j++) { + MidiInApi::MidiMessage message; + message.bytes.clear(); + + jack_midi_event_get( &event, buff, j ); + + for ( unsigned int i = 0; i < event.size; i++ ) + message.bytes.push_back( event.buffer[i] ); + + // Compute the delta time. + time = jack_get_time(); + if ( rtData->firstMessage == true ) + rtData->firstMessage = false; + else + message.timeStamp = ( time - jData->lastTime ) * 0.000001; + + jData->lastTime = time; + + if ( !rtData->continueSysex ) { + if ( rtData->usingCallback ) { + RtMidiIn::RtMidiCallback callback = (RtMidiIn::RtMidiCallback) rtData->userCallback; + callback( message.timeStamp, &message.bytes, rtData->userData ); + } + else { + // As long as we haven't reached our queue size limit, push the message. + if ( rtData->queue.size < rtData->queue.ringSize ) { + rtData->queue.ring[rtData->queue.back++] = message; + if ( rtData->queue.back == rtData->queue.ringSize ) + rtData->queue.back = 0; + rtData->queue.size++; + } + else + std::cerr << "\nMidiInJack: message queue limit reached!!\n\n"; + } + } + } + + return 0; } -void RtMidiIn :: openPort( unsigned int portNumber, const std::string /*portName*/ ) +MidiInJack :: MidiInJack( const std::string clientName, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { + initialize( clientName ); } -void RtMidiIn :: openVirtualPort( std::string portName ) +void MidiInJack :: initialize( const std::string& clientName ) { + JackMidiData *data = new JackMidiData; + apiData_ = (void *) data; + + data->rtMidiIn = &inputData_; + data->port = NULL; + data->client = NULL; + this->clientName = clientName; + + connect(); } -void RtMidiIn :: closePort( void ) +void MidiInJack :: connect() { + JackMidiData *data = static_cast (apiData_); + if ( data->client ) + return; + + // Initialize JACK client + if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { + errorString_ = "MidiInJack::initialize: JACK server not running?"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + jack_set_process_callback( data->client, jackProcessIn, data ); + jack_activate( data->client ); } -RtMidiIn :: ~RtMidiIn() +MidiInJack :: ~MidiInJack() { + JackMidiData *data = static_cast (apiData_); + closePort(); + + if ( data->client ) + jack_client_close( data->client ); + delete data; } -unsigned int RtMidiIn :: getPortCount() +void MidiInJack :: openPort( unsigned int portNumber, const std::string portName ) { + JackMidiData *data = static_cast (apiData_); + + connect(); + + // Creating new port + if ( data->port == NULL) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + + if ( data->port == NULL) { + errorString_ = "MidiInJack::openPort: JACK error creating port"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Connecting to the output + std::string name = getPortName( portNumber ); + jack_connect( data->client, name.c_str(), jack_port_name( data->port ) ); +} + +void MidiInJack :: openVirtualPort( const std::string portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiInJack::openVirtualPort: JACK error creating virtual port"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + } +} + +unsigned int MidiInJack :: getPortCount() +{ + int count = 0; + JackMidiData *data = static_cast (apiData_); + connect(); + if ( !data->client ) return 0; + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); + + if ( ports == NULL ) return 0; + while ( ports[count] != NULL ) + count++; + + free( ports ); + + return count; } -std::string RtMidiIn :: getPortName( unsigned int portNumber ) +std::string MidiInJack :: getPortName( unsigned int portNumber ) { - return ""; + JackMidiData *data = static_cast (apiData_); + std::string retStr(""); + + connect(); + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput ); + + // Check port validity + if ( ports == NULL ) { + errorString_ = "MidiInJack::getPortName: no ports available!"; + error( RtMidiError::WARNING, errorString_ ); + return retStr; + } + + if ( ports[portNumber] == NULL ) { + std::ostringstream ost; + ost << "MidiInJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + } + else retStr.assign( ports[portNumber] ); + + free( ports ); + return retStr; } -unsigned int RtMidiOut :: getPortCount() +void MidiInJack :: closePort() { + JackMidiData *data = static_cast (apiData_); + + if ( data->port == NULL ) return; + jack_port_unregister( data->client, data->port ); + data->port = NULL; +} + +//*********************************************************************// +// API: JACK +// Class Definitions: MidiOutJack +//*********************************************************************// + +// Jack process callback +static int jackProcessOut( jack_nframes_t nframes, void *arg ) +{ + JackMidiData *data = (JackMidiData *) arg; + jack_midi_data_t *midiData; + int space; + + // Is port created? + if ( data->port == NULL ) return 0; + + void *buff = jack_port_get_buffer( data->port, nframes ); + jack_midi_clear_buffer( buff ); + + while ( jack_ringbuffer_read_space( data->buffSize ) > 0 ) { + jack_ringbuffer_read( data->buffSize, (char *) &space, (size_t) sizeof(space) ); + midiData = jack_midi_event_reserve( buff, 0, space ); + + jack_ringbuffer_read( data->buffMessage, (char *) midiData, (size_t) space ); + } + + return 0; +} + +MidiOutJack :: MidiOutJack( const std::string clientName ) : MidiOutApi() +{ + initialize( clientName ); +} + +void MidiOutJack :: initialize( const std::string& clientName ) +{ + JackMidiData *data = new JackMidiData; + apiData_ = (void *) data; + + data->port = NULL; + data->client = NULL; + this->clientName = clientName; + + connect(); +} + +void MidiOutJack :: connect() +{ + JackMidiData *data = static_cast (apiData_); + if ( data->client ) + return; + + // Initialize output ringbuffers + data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); + data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE ); + + // Initialize JACK client + if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { + errorString_ = "MidiOutJack::initialize: JACK server not running?"; + error( RtMidiError::WARNING, errorString_ ); + return; + } + + jack_set_process_callback( data->client, jackProcessOut, data ); + jack_activate( data->client ); +} + +MidiOutJack :: ~MidiOutJack() +{ + JackMidiData *data = static_cast (apiData_); + closePort(); + + // Cleanup + jack_ringbuffer_free( data->buffSize ); + jack_ringbuffer_free( data->buffMessage ); + if ( data->client ) { + jack_client_close( data->client ); + } + + delete data; +} + +void MidiOutJack :: openPort( unsigned int portNumber, const std::string portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + + // Creating new port + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiOutJack::openPort: JACK error creating port"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + return; + } + + // Connecting to the output + std::string name = getPortName( portNumber ); + jack_connect( data->client, jack_port_name( data->port ), name.c_str() ); +} + +void MidiOutJack :: openVirtualPort( const std::string portName ) +{ + JackMidiData *data = static_cast (apiData_); + + connect(); + if ( data->port == NULL ) + data->port = jack_port_register( data->client, portName.c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0 ); + + if ( data->port == NULL ) { + errorString_ = "MidiOutJack::openVirtualPort: JACK error creating virtual port"; + error( RtMidiError::DRIVER_ERROR, errorString_ ); + } +} + +unsigned int MidiOutJack :: getPortCount() +{ + int count = 0; + JackMidiData *data = static_cast (apiData_); + connect(); + if ( !data->client ) return 0; + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); + + if ( ports == NULL ) return 0; + while ( ports[count] != NULL ) + count++; + + free( ports ); + + return count; } -std::string RtMidiOut :: getPortName( unsigned int portNumber ) +std::string MidiOutJack :: getPortName( unsigned int portNumber ) { - return ""; + JackMidiData *data = static_cast (apiData_); + std::string retStr(""); + + connect(); + + // List of available ports + const char **ports = jack_get_ports( data->client, NULL, + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput ); + + // Check port validity + if ( ports == NULL) { + errorString_ = "MidiOutJack::getPortName: no ports available!"; + error( RtMidiError::WARNING, errorString_ ); + return retStr; + } + + if ( ports[portNumber] == NULL) { + std::ostringstream ost; + ost << "MidiOutJack::getPortName: the 'portNumber' argument (" << portNumber << ") is invalid."; + errorString_ = ost.str(); + error( RtMidiError::WARNING, errorString_ ); + } + else retStr.assign( ports[portNumber] ); + + free( ports ); + return retStr; } -void RtMidiOut :: initialize( const std::string& /*clientName*/ ) +void MidiOutJack :: closePort() { + JackMidiData *data = static_cast (apiData_); + + if ( data->port == NULL ) return; + jack_port_unregister( data->client, data->port ); + data->port = NULL; } -void RtMidiOut :: openPort( unsigned int portNumber, const std::string /*portName*/ ) +void MidiOutJack :: sendMessage( std::vector *message ) { + int nBytes = message->size(); + JackMidiData *data = static_cast (apiData_); + + // Write full message to buffer + jack_ringbuffer_write( data->buffMessage, ( const char * ) &( *message )[0], + message->size() ); + jack_ringbuffer_write( data->buffSize, ( char * ) &nBytes, sizeof( nBytes ) ); } -void RtMidiOut :: closePort( void ) -{ -} - -void RtMidiOut :: openVirtualPort( std::string portName ) -{ -} - -RtMidiOut :: ~RtMidiOut() -{ -} - -void RtMidiOut :: sendMessage( std::vector *message ) -{ -} - -#endif /* __RTMIDI_DUMMY_ONLY__ */ - +#endif // __UNIX_JACK__ diff -r d4a28d1479a8 -r 710e6250a401 data/midi/rtmidi/RtMidi.h --- a/data/midi/rtmidi/RtMidi.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/midi/rtmidi/RtMidi.h Mon Sep 17 13:51:14 2018 +0100 @@ -8,7 +8,7 @@ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi: realtime MIDI i/o C++ classes - Copyright (c) 2003-2009 Gary P. Scavone + Copyright (c) 2003-2016 Gary P. Scavone Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -22,8 +22,9 @@ included in all copies or substantial portions of the Software. Any person wishing to distribute modifications to the Software is - requested to send the modifications to the original developer so that - they can be incorporated into the canonical version. + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF @@ -35,18 +36,108 @@ */ /**********************************************************************/ -// RtMidi: Version 1.0.8 +/*! + \file RtMidi.h + */ #ifndef RTMIDI_H #define RTMIDI_H -#include "RtError.h" +#define RTMIDI_VERSION "2.1.1" + +#include +#include #include +#include + +/************************************************************************/ +/*! \class RtMidiError + \brief Exception handling class for RtMidi. + + The RtMidiError class is quite simple but it does allow errors to be + "caught" by RtMidiError::Type. See the RtMidi documentation to know + which methods can throw an RtMidiError. +*/ +/************************************************************************/ + +class RtMidiError : public std::exception +{ + public: + //! Defined RtMidiError types. + enum Type { + WARNING, /*!< A non-critical error. */ + DEBUG_WARNING, /*!< A non-critical error which might be useful for debugging. */ + UNSPECIFIED, /*!< The default, unspecified error type. */ + NO_DEVICES_FOUND, /*!< No devices found on system. */ + INVALID_DEVICE, /*!< An invalid device ID was specified. */ + MEMORY_ERROR, /*!< An error occured during memory allocation. */ + INVALID_PARAMETER, /*!< An invalid parameter was specified to a function. */ + INVALID_USE, /*!< The function was called incorrectly. */ + DRIVER_ERROR, /*!< A system driver error occured. */ + SYSTEM_ERROR, /*!< A system error occured. */ + THREAD_ERROR /*!< A thread error occured. */ + }; + + //! The constructor. + RtMidiError( const std::string& message, Type type = RtMidiError::UNSPECIFIED ) throw() : message_(message), type_(type) {} + + //! The destructor. + virtual ~RtMidiError( void ) throw() {} + + //! Prints thrown error message to stderr. + virtual void printMessage( void ) const throw() { std::cerr << '\n' << message_ << "\n\n"; } + + //! Returns the thrown error message type. + virtual const Type& getType(void) const throw() { return type_; } + + //! Returns the thrown error message string. + virtual const std::string& getMessage(void) const throw() { return message_; } + + //! Returns the thrown error message as a c-style string. + virtual const char* what( void ) const throw() { return message_.c_str(); } + + protected: + std::string message_; + Type type_; +}; + +//! RtMidi error callback function prototype. +/*! + \param type Type of error. + \param errorText Error description. + + Note that class behaviour is undefined after a critical error (not + a warning) is reported. + */ +typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData ); + +class MidiApi; class RtMidi { public: + //! MIDI API specifier arguments. + enum Api { + UNSPECIFIED, /*!< Search for a working compiled API. */ + MACOSX_CORE, /*!< Macintosh OS-X Core Midi API. */ + LINUX_ALSA, /*!< The Advanced Linux Sound Architecture API. */ + UNIX_JACK, /*!< The JACK Low-Latency MIDI Server API. */ + WINDOWS_MM, /*!< The Microsoft Multimedia MIDI API. */ + RTMIDI_DUMMY /*!< A compilable but non-functional API. */ + }; + + //! A static function to determine the current RtMidi version. + static std::string getVersion( void ) throw(); + + //! A static function to determine the available compiled MIDI APIs. + /*! + The values returned in the std::vector can be compared against + the enumerated list values. Note that there can be more than one + API compiled for certain operating systems. + */ + static void getCompiledApi( std::vector &apis ) throw(); + //! Pure virtual openPort() function. virtual void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi" ) ) = 0; @@ -62,19 +153,22 @@ //! Pure virtual closePort() function. virtual void closePort( void ) = 0; + //! Returns true if a port is open and false if not. + virtual bool isPortOpen( void ) const = 0; + + //! Set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0; + protected: RtMidi(); - virtual ~RtMidi() {}; + virtual ~RtMidi(); - // A basic error reporting function for internal use in the RtMidi - // subclasses. The behavior of this function can be modified to - // suit specific needs. - void error( RtError::Type type ); - - void *apiData_; - bool connected_; - std::string errorString_; + MidiApi *rtapi_; }; /**********************************************************************/ @@ -87,16 +181,27 @@ retrieval using the getMessage() function or immediately passed to a user-specified callback function. Create multiple instances of this class to connect to more than one MIDI device at the same - time. With the OS-X and Linux ALSA MIDI APIs, it is also possible - to open a virtual input port to which other MIDI software clients - can connect. + time. With the OS-X, Linux ALSA, and JACK MIDI APIs, it is also + possible to open a virtual input port to which other MIDI software + clients can connect. - by Gary P. Scavone, 2003-2008. + by Gary P. Scavone, 2003-2014. */ /**********************************************************************/ -#include -#include +// **************************************************************** // +// +// RtMidiIn and RtMidiOut class declarations. +// +// RtMidiIn / RtMidiOut are "controllers" used to select an available +// MIDI input or output interface. They present common APIs for the +// user to call but all functionality is implemented by the classes +// MidiInApi, MidiOutApi and their subclasses. RtMidiIn and RtMidiOut +// each create an instance of a MidiInApi or MidiOutApi subclass based +// on the user's API choice. If no choice is made, they attempt to +// make a "logical" API selection. +// +// **************************************************************** // class RtMidiIn : public RtMidi { @@ -105,123 +210,122 @@ //! User callback function type definition. typedef void (*RtMidiCallback)( double timeStamp, std::vector *message, void *userData); - //! Default constructor that allows an optional client name. + //! Default constructor that allows an optional api, client name and queue size. /*! - An exception will be thrown if a MIDI system initialization error occurs. + An exception will be thrown if a MIDI system initialization + error occurs. The queue size defines the maximum number of + messages that can be held in the MIDI queue (when not using a + callback function). If the queue size limit is reached, + incoming messages will be ignored. + + If no API argument is specified and multiple API support has been + compiled, the default order of use is ALSA, JACK (Linux) and CORE, + JACK (OS-X). + + \param api An optional API id can be specified. + \param clientName An optional client name can be specified. This + will be used to group the ports that are created + by the application. + \param queueSizeLimit An optional size of the MIDI input queue can be specified. */ - RtMidiIn( const std::string clientName = std::string( "RtMidi Input Client") ); + RtMidiIn( RtMidi::Api api=UNSPECIFIED, + const std::string clientName = std::string( "RtMidi Input Client"), + unsigned int queueSizeLimit = 100 ); //! If a MIDI connection is still open, it will be closed by the destructor. - ~RtMidiIn(); + ~RtMidiIn ( void ) throw(); - //! Open a MIDI input connection. + //! Returns the MIDI API specifier for the current instance of RtMidiIn. + RtMidi::Api getCurrentApi( void ) throw(); + + //! Open a MIDI input connection given by enumeration number. /*! - An optional port number greater than 0 can be specified. - Otherwise, the default or first port found is opened. + \param portNumber An optional port number greater than 0 can be specified. + Otherwise, the default or first port found is opened. + \param portName An optional name for the application port that is used to connect to portId can be specified. */ - void openPort( unsigned int portNumber = 0, const std::string Portname = std::string( "RtMidi Input" ) ); + void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Input" ) ); - //! Create a virtual input port, with optional name, to allow software connections (OS X and ALSA only). + //! Create a virtual input port, with optional name, to allow software connections (OS X, JACK and ALSA only). /*! - This function creates a virtual MIDI input port to which other - software applications can connect. This type of functionality - is currently only supported by the Macintosh OS-X and Linux ALSA - APIs (the function does nothing for the other APIs). + This function creates a virtual MIDI input port to which other + software applications can connect. This type of functionality + is currently only supported by the Macintosh OS-X, any JACK, + and Linux ALSA APIs (the function returns an error for the other APIs). + + \param portName An optional name for the application port that is + used to connect to portId can be specified. */ void openVirtualPort( const std::string portName = std::string( "RtMidi Input" ) ); //! Set a callback function to be invoked for incoming MIDI messages. /*! - The callback function will be called whenever an incoming MIDI - message is received. While not absolutely necessary, it is best - to set the callback function before opening a MIDI port to avoid - leaving some messages in the queue. + The callback function will be called whenever an incoming MIDI + message is received. While not absolutely necessary, it is best + to set the callback function before opening a MIDI port to avoid + leaving some messages in the queue. + + \param callback A callback function must be given. + \param userData Optionally, a pointer to additional data can be + passed to the callback function whenever it is called. */ void setCallback( RtMidiCallback callback, void *userData = 0 ); //! Cancel use of the current callback function (if one exists). /*! - Subsequent incoming MIDI messages will be written to the queue - and can be retrieved with the \e getMessage function. + Subsequent incoming MIDI messages will be written to the queue + and can be retrieved with the \e getMessage function. */ void cancelCallback(); //! Close an open MIDI connection (if one exists). void closePort( void ); + //! Returns true if a port is open and false if not. + virtual bool isPortOpen() const; + //! Return the number of available MIDI input ports. + /*! + \return This function returns the number of MIDI ports of the selected API. + */ unsigned int getPortCount(); //! Return a string identifier for the specified MIDI input port number. /*! - An exception is thrown if an invalid port specifier is provided. + \return The name of the port with the given Id is returned. + \retval An empty string is returned if an invalid port specifier is provided. */ std::string getPortName( unsigned int portNumber = 0 ); - //! Set the maximum number of MIDI messages to be saved in the queue. - /*! - If the queue size limit is reached, incoming messages will be - ignored. The default limit is 1024. - */ - void setQueueSizeLimit( unsigned int queueSize ); - //! Specify whether certain MIDI message types should be queued or ignored during input. /*! - By default, MIDI timing and active sensing messages are ignored - during message input because of their relative high data rates. - MIDI sysex messages are ignored by default as well. Variable - values of "true" imply that the respective message type will be - ignored. + By default, MIDI timing and active sensing messages are ignored + during message input because of their relative high data rates. + MIDI sysex messages are ignored by default as well. Variable + values of "true" imply that the respective message type will be + ignored. */ void ignoreTypes( bool midiSysex = true, bool midiTime = true, bool midiSense = true ); //! Fill the user-provided vector with the data bytes for the next available MIDI message in the input queue and return the event delta-time in seconds. /*! - This function returns immediately whether a new message is - available or not. A valid message is indicated by a non-zero - vector size. An exception is thrown if an error occurs during - message retrieval or an input connection was not previously - established. + This function returns immediately whether a new message is + available or not. A valid message is indicated by a non-zero + vector size. An exception is thrown if an error occurs during + message retrieval or an input connection was not previously + established. */ double getMessage( std::vector *message ); - // A MIDI structure used internally by the class to store incoming - // messages. Each message represents one and only one MIDI message. - struct MidiMessage { - std::vector bytes; - double timeStamp; + //! Set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); - // Default constructor. - MidiMessage() - :bytes(3), timeStamp(0.0) {} - }; - - // The RtMidiInData structure is used to pass private class data to - // the MIDI input handling function or thread. - struct RtMidiInData { - std::queue queue; - MidiMessage message; - unsigned int queueLimit; - unsigned char ignoreFlags; - bool doInput; - bool firstMessage; - void *apiData; - bool usingCallback; - void *userCallback; - void *userData; - bool continueSysex; - - // Default constructor. - RtMidiInData() - : queueLimit(1024), ignoreFlags(7), doInput(false), firstMessage(true), - apiData(0), usingCallback(false), userCallback(0), userData(0), - continueSysex(false) {} - }; - - private: - - void initialize( const std::string& clientName ); - RtMidiInData inputData_; + protected: + void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ); }; @@ -233,9 +337,11 @@ output. It allows one to probe available MIDI output ports, to connect to one such port, and to send MIDI bytes immediately over the connection. Create multiple instances of this class to - connect to more than one MIDI device at the same time. + connect to more than one MIDI device at the same time. With the + OS-X, Linux ALSA and JACK MIDI APIs, it is also possible to open a + virtual port to which other MIDI software clients can connect. - by Gary P. Scavone, 2003-2008. + by Gary P. Scavone, 2003-2014. */ /**********************************************************************/ @@ -245,12 +351,20 @@ //! Default constructor that allows an optional client name. /*! - An exception will be thrown if a MIDI system initialization error occurs. + An exception will be thrown if a MIDI system initialization error occurs. + + If no API argument is specified and multiple API support has been + compiled, the default order of use is ALSA, JACK (Linux) and CORE, + JACK (OS-X). */ - RtMidiOut( const std::string clientName = std::string( "RtMidi Output Client" ) ); + RtMidiOut( RtMidi::Api api=UNSPECIFIED, + const std::string clientName = std::string( "RtMidi Output Client") ); //! The destructor closes any open MIDI connections. - ~RtMidiOut(); + ~RtMidiOut( void ) throw(); + + //! Returns the MIDI API specifier for the current instance of RtMidiOut. + RtMidi::Api getCurrentApi( void ) throw(); //! Open a MIDI output connection. /*! @@ -262,25 +376,28 @@ void openPort( unsigned int portNumber = 0, const std::string portName = std::string( "RtMidi Output" ) ); //! Close an open MIDI connection (if one exists). - void closePort(); + void closePort( void ); - //! Create a virtual output port, with optional name, to allow software connections (OS X and ALSA only). + //! Returns true if a port is open and false if not. + virtual bool isPortOpen() const; + + //! Create a virtual output port, with optional name, to allow software connections (OS X, JACK and ALSA only). /*! This function creates a virtual MIDI output port to which other software applications can connect. This type of functionality - is currently only supported by the Macintosh OS-X and Linux ALSA - APIs (the function does nothing with the other APIs). An - exception is thrown if an error occurs while attempting to create - the virtual port. + is currently only supported by the Macintosh OS-X, Linux ALSA + and JACK APIs (the function does nothing with the other APIs). + An exception is thrown if an error occurs while attempting to + create the virtual port. */ void openVirtualPort( const std::string portName = std::string( "RtMidi Output" ) ); //! Return the number of available MIDI output ports. - unsigned int getPortCount(); + unsigned int getPortCount( void ); //! Return a string identifier for the specified MIDI port type and number. /*! - An exception is thrown if an invalid port specifier is provided. + An empty string is returned if an invalid port specifier is provided. */ std::string getPortName( unsigned int portNumber = 0 ); @@ -291,9 +408,356 @@ */ void sendMessage( std::vector *message ); - private: + //! Set an error callback function to be invoked when an error has occured. + /*! + The callback function will be called whenever an error has occured. It is best + to set the error callback function before opening a port. + */ + virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ); + protected: + void openMidiApi( RtMidi::Api api, const std::string clientName ); +}; + + +// **************************************************************** // +// +// MidiInApi / MidiOutApi class declarations. +// +// Subclasses of MidiInApi and MidiOutApi contain all API- and +// OS-specific code necessary to fully implement the RtMidi API. +// +// Note that MidiInApi and MidiOutApi are abstract base classes and +// cannot be explicitly instantiated. RtMidiIn and RtMidiOut will +// create instances of a MidiInApi or MidiOutApi subclass. +// +// **************************************************************** // + +class MidiApi +{ + public: + + MidiApi(); + virtual ~MidiApi(); + virtual RtMidi::Api getCurrentApi( void ) = 0; + virtual void openPort( unsigned int portNumber, const std::string portName ) = 0; + virtual void openVirtualPort( const std::string portName ) = 0; + virtual void closePort( void ) = 0; + + virtual unsigned int getPortCount( void ) = 0; + virtual std::string getPortName( unsigned int portNumber ) = 0; + + inline bool isPortOpen() const { return connected_; } + void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ); + + //! A basic error reporting function for RtMidi classes. + void error( RtMidiError::Type type, std::string errorString ); + +protected: + virtual void initialize( const std::string& clientName ) = 0; + + void *apiData_; + bool connected_; + std::string errorString_; + RtMidiErrorCallback errorCallback_; + bool firstErrorOccurred_; + void *errorCallbackUserData_; +}; + +class MidiInApi : public MidiApi +{ + public: + + MidiInApi( unsigned int queueSizeLimit ); + virtual ~MidiInApi( void ); + void setCallback( RtMidiIn::RtMidiCallback callback, void *userData ); + void cancelCallback( void ); + virtual void ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ); + double getMessage( std::vector *message ); + + // A MIDI structure used internally by the class to store incoming + // messages. Each message represents one and only one MIDI message. + struct MidiMessage { + std::vector bytes; + double timeStamp; + + // Default constructor. + MidiMessage() + :bytes(0), timeStamp(0.0) {} + }; + + struct MidiQueue { + unsigned int front; + unsigned int back; + unsigned int size; + unsigned int ringSize; + MidiMessage *ring; + + // Default constructor. + MidiQueue() + :front(0), back(0), size(0), ringSize(0) {} + }; + + // The RtMidiInData structure is used to pass private class data to + // the MIDI input handling function or thread. + struct RtMidiInData { + MidiQueue queue; + MidiMessage message; + unsigned char ignoreFlags; + bool doInput; + bool firstMessage; + void *apiData; + bool usingCallback; + RtMidiIn::RtMidiCallback userCallback; + void *userData; + bool continueSysex; + + // Default constructor. + RtMidiInData() + : ignoreFlags(7), doInput(false), firstMessage(true), + apiData(0), usingCallback(false), userCallback(0), userData(0), + continueSysex(false) {} + }; + + protected: + RtMidiInData inputData_; +}; + +class MidiOutApi : public MidiApi +{ + public: + + MidiOutApi( void ); + virtual ~MidiOutApi( void ); + virtual void sendMessage( std::vector *message ) = 0; +}; + +// **************************************************************** // +// +// Inline RtMidiIn and RtMidiOut definitions. +// +// **************************************************************** // + +inline RtMidi::Api RtMidiIn :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } +inline void RtMidiIn :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); } +inline void RtMidiIn :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); } +inline void RtMidiIn :: closePort( void ) { rtapi_->closePort(); } +inline bool RtMidiIn :: isPortOpen() const { return rtapi_->isPortOpen(); } +inline void RtMidiIn :: setCallback( RtMidiCallback callback, void *userData ) { ((MidiInApi *)rtapi_)->setCallback( callback, userData ); } +inline void RtMidiIn :: cancelCallback( void ) { ((MidiInApi *)rtapi_)->cancelCallback(); } +inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCount(); } +inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } +inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } +inline double RtMidiIn :: getMessage( std::vector *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); } +inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } + +inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } +inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); } +inline void RtMidiOut :: openVirtualPort( const std::string portName ) { rtapi_->openVirtualPort( portName ); } +inline void RtMidiOut :: closePort( void ) { rtapi_->closePort(); } +inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); } +inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } +inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } +inline void RtMidiOut :: sendMessage( std::vector *message ) { ((MidiOutApi *)rtapi_)->sendMessage( message ); } +inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); } + +// **************************************************************** // +// +// MidiInApi and MidiOutApi subclass prototypes. +// +// **************************************************************** // + +#if !defined(__LINUX_ALSA__) && !defined(__UNIX_JACK__) && !defined(__MACOSX_CORE__) && !defined(__WINDOWS_MM__) + #define __RTMIDI_DUMMY__ +#endif + +#if defined(__MACOSX_CORE__) + +class MidiInCore: public MidiInApi +{ + public: + MidiInCore( const std::string clientName, unsigned int queueSizeLimit ); + ~MidiInCore( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + void initialize( const std::string& clientName ); +}; + +class MidiOutCore: public MidiOutApi +{ + public: + MidiOutCore( const std::string clientName ); + ~MidiOutCore( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::MACOSX_CORE; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( std::vector *message ); + + protected: void initialize( const std::string& clientName ); }; #endif + +#if defined(__UNIX_JACK__) + +class MidiInJack: public MidiInApi +{ + public: + MidiInJack( const std::string clientName, unsigned int queueSizeLimit ); + ~MidiInJack( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + std::string clientName; + + void connect( void ); + void initialize( const std::string& clientName ); +}; + +class MidiOutJack: public MidiOutApi +{ + public: + MidiOutJack( const std::string clientName ); + ~MidiOutJack( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::UNIX_JACK; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( std::vector *message ); + + protected: + std::string clientName; + + void connect( void ); + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__LINUX_ALSA__) + +class MidiInAlsa: public MidiInApi +{ + public: + MidiInAlsa( const std::string clientName, unsigned int queueSizeLimit ); + ~MidiInAlsa( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + void initialize( const std::string& clientName ); +}; + +class MidiOutAlsa: public MidiOutApi +{ + public: + MidiOutAlsa( const std::string clientName ); + ~MidiOutAlsa( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::LINUX_ALSA; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( std::vector *message ); + + protected: + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__WINDOWS_MM__) + +class MidiInWinMM: public MidiInApi +{ + public: + MidiInWinMM( const std::string clientName, unsigned int queueSizeLimit ); + ~MidiInWinMM( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + + protected: + void initialize( const std::string& clientName ); +}; + +class MidiOutWinMM: public MidiOutApi +{ + public: + MidiOutWinMM( const std::string clientName ); + ~MidiOutWinMM( void ); + RtMidi::Api getCurrentApi( void ) { return RtMidi::WINDOWS_MM; }; + void openPort( unsigned int portNumber, const std::string portName ); + void openVirtualPort( const std::string portName ); + void closePort( void ); + unsigned int getPortCount( void ); + std::string getPortName( unsigned int portNumber ); + void sendMessage( std::vector *message ); + + protected: + void initialize( const std::string& clientName ); +}; + +#endif + +#if defined(__RTMIDI_DUMMY__) + +class MidiInDummy: public MidiInApi +{ + public: + MidiInDummy( const std::string /*clientName*/, unsigned int queueSizeLimit ) : MidiInApi( queueSizeLimit ) { errorString_ = "MidiInDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } + RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } + void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {} + void openVirtualPort( const std::string /*portName*/ ) {} + void closePort( void ) {} + unsigned int getPortCount( void ) { return 0; } + std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } + + protected: + void initialize( const std::string& /*clientName*/ ) {} +}; + +class MidiOutDummy: public MidiOutApi +{ + public: + MidiOutDummy( const std::string /*clientName*/ ) { errorString_ = "MidiOutDummy: This class provides no functionality."; error( RtMidiError::WARNING, errorString_ ); } + RtMidi::Api getCurrentApi( void ) { return RtMidi::RTMIDI_DUMMY; } + void openPort( unsigned int /*portNumber*/, const std::string /*portName*/ ) {} + void openVirtualPort( const std::string /*portName*/ ) {} + void closePort( void ) {} + unsigned int getPortCount( void ) { return 0; } + std::string getPortName( unsigned int /*portNumber*/ ) { return ""; } + void sendMessage( std::vector * /*message*/ ) {} + + protected: + void initialize( const std::string& /*clientName*/ ) {} +}; + +#endif + +#endif diff -r d4a28d1479a8 -r 710e6250a401 data/model/AggregateWaveModel.cpp --- a/data/model/AggregateWaveModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/AggregateWaveModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -25,10 +25,15 @@ AggregateWaveModel::m_zoomConstraint; AggregateWaveModel::AggregateWaveModel(ChannelSpecList channelSpecs) : - m_components(channelSpecs) + m_components(channelSpecs), + m_invalidated(false) { for (ChannelSpecList::const_iterator i = channelSpecs.begin(); i != channelSpecs.end(); ++i) { + + connect(i->model, SIGNAL(aboutToBeDeleted()), + this, SLOT(componentModelAboutToBeDeleted())); + if (i->model->getSampleRate() != channelSpecs.begin()->model->getSampleRate()) { SVDEBUG << "AggregateWaveModel::AggregateWaveModel: WARNING: Component models do not all have the same sample rate" << endl; @@ -41,12 +46,27 @@ { } +void +AggregateWaveModel::componentModelAboutToBeDeleted() +{ + SVDEBUG << "AggregateWaveModel::componentModelAboutToBeDeleted: invalidating" + << endl; + m_components.clear(); + m_invalidated = true; + emit modelInvalidated(); +} + bool AggregateWaveModel::isOK() const { + if (m_invalidated) { + return false; + } for (ChannelSpecList::const_iterator i = m_components.begin(); i != m_components.end(); ++i) { - if (!i->model->isOK()) return false; + if (!i->model->isOK()) { + return false; + } } return true; } @@ -55,6 +75,7 @@ AggregateWaveModel::isReady(int *completion) const { if (completion) *completion = 100; + bool ready = true; for (ChannelSpecList::const_iterator i = m_components.begin(); i != m_components.end(); ++i) { @@ -71,13 +92,12 @@ AggregateWaveModel::getFrameCount() const { sv_frame_t count = 0; - for (ChannelSpecList::const_iterator i = m_components.begin(); i != m_components.end(); ++i) { - sv_frame_t thisCount = i->model->getEndFrame() - i->model->getStartFrame(); + sv_frame_t thisCount = + i->model->getEndFrame() - i->model->getStartFrame(); if (thisCount > count) count = thisCount; } - return count; } @@ -94,7 +114,7 @@ return m_components.begin()->model->getSampleRate(); } -vector +floatvec_t AggregateWaveModel::getData(int channel, sv_frame_t start, sv_frame_t count) const { int ch0 = channel, ch1 = channel; @@ -103,8 +123,7 @@ ch1 = getChannelCount()-1; } - vector result(count, 0.f); - + floatvec_t result(count, 0.f); sv_frame_t longest = 0; for (int c = ch0; c <= ch1; ++c) { @@ -123,13 +142,13 @@ return result; } -vector> +vector AggregateWaveModel::getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const { sv_frame_t min = count; - vector> result; + vector result; for (int c = fromchannel; c <= tochannel; ++c) { auto here = getData(c, start, count); @@ -198,10 +217,17 @@ } void -AggregateWaveModel::toXml(QTextStream &, - QString , - QString ) const +AggregateWaveModel::toXml(QTextStream &out, + QString indent, + QString extraAttributes) const { - //!!! complete + QStringList componentStrings; + for (const auto &c: m_components) { + componentStrings.push_back(QString("%1").arg(getObjectExportId(c.model))); + } + Model::toXml(out, indent, + QString("type=\"aggregatewave\" components=\"%1\" %2") + .arg(componentStrings.join(",")) + .arg(extraAttributes)); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/AggregateWaveModel.h --- a/data/model/AggregateWaveModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/AggregateWaveModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -59,9 +59,9 @@ virtual sv_frame_t getStartFrame() const { return 0; } virtual sv_frame_t getEndFrame() const { return getFrameCount(); } - virtual std::vector getData(int channel, sv_frame_t start, sv_frame_t count) const; + virtual floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const; - virtual std::vector> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; + virtual std::vector getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; virtual int getSummaryBlockSize(int desired) const; @@ -79,15 +79,18 @@ void modelChanged(); void modelChangedWithin(sv_frame_t, sv_frame_t); void completionChanged(); + void modelInvalidated(); protected slots: void componentModelChanged(); void componentModelChangedWithin(sv_frame_t, sv_frame_t); void componentModelCompletionChanged(); + void componentModelAboutToBeDeleted(); protected: ChannelSpecList m_components; static PowerOfSqrtTwoZoomConstraint m_zoomConstraint; + bool m_invalidated; // because one of its component models is aboutToBeDeleted }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/model/AlignmentModel.cpp --- a/data/model/AlignmentModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/AlignmentModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -22,7 +22,7 @@ AlignmentModel::AlignmentModel(Model *reference, Model *aligned, Model *inputModel, - SparseTimeValueModel *path) : + SparseTimeValueModel *path) : m_reference(reference), m_aligned(aligned), m_inputModel(inputModel), diff -r d4a28d1479a8 -r 710e6250a401 data/model/Dense3DModelPeakCache.cpp --- a/data/model/Dense3DModelPeakCache.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/Dense3DModelPeakCache.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -17,8 +17,10 @@ #include "base/Profiler.h" +#include "base/HitCount.h" + Dense3DModelPeakCache::Dense3DModelPeakCache(const DenseThreeDimensionalModel *source, - int columnsPerPeak) : + int columnsPerPeak) : m_source(source), m_columnsPerPeak(columnsPerPeak) { @@ -33,18 +35,17 @@ this, SLOT(sourceModelChanged())); connect(source, SIGNAL(aboutToBeDeleted()), this, SLOT(sourceModelAboutToBeDeleted())); - } Dense3DModelPeakCache::~Dense3DModelPeakCache() { + if (m_cache) m_cache->aboutToDelete(); delete m_cache; } Dense3DModelPeakCache::Column Dense3DModelPeakCache::getColumn(int column) const { - Profiler profiler("Dense3DModelPeakCache::getColumn"); if (!m_source) return Column(); if (!haveColumn(column)) fillColumn(column); return m_cache->getColumn(column); @@ -79,7 +80,14 @@ bool Dense3DModelPeakCache::haveColumn(int column) const { - return in_range_for(m_coverage, column) && m_coverage[column]; + static HitCount count("Dense3DModelPeakCache"); + if (in_range_for(m_coverage, column) && m_coverage[column]) { + count.hit(); + return true; + } else { + count.miss(); + return false; + } } void diff -r d4a28d1479a8 -r 710e6250a401 data/model/Dense3DModelPeakCache.h --- a/data/model/Dense3DModelPeakCache.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/Dense3DModelPeakCache.h Mon Sep 17 13:51:14 2018 +0100 @@ -73,9 +73,15 @@ return m_source->getMaximumLevel(); } - virtual Column getColumn(int column) const; + /** + * Retrieve the peaks column at peak-cache column number col. This + * will consist of the peak values in the underlying model from + * columns (col * getColumnsPerPeak()) to ((col+1) * + * getColumnsPerPeak() - 1) inclusive. + */ + virtual Column getColumn(int col) const; - virtual float getValueAt(int column, int n) const; + virtual float getValueAt(int col, int n) const; virtual QString getBinName(int n) const { return m_source->getBinName(n); diff -r d4a28d1479a8 -r 710e6250a401 data/model/DenseTimeValueModel.cpp --- a/data/model/DenseTimeValueModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/DenseTimeValueModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -27,13 +27,13 @@ { PlayParameterRepository::getInstance()->removePlayable(this); } - + QString DenseTimeValueModel::toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const { int ch = getChannelCount(); - cerr << "f0 = " << f0 << ", f1 = " << f1 << endl; +// cerr << "f0 = " << f0 << ", f1 = " << f1 << endl; if (f1 <= f0) return ""; diff -r d4a28d1479a8 -r 710e6250a401 data/model/DenseTimeValueModel.h --- a/data/model/DenseTimeValueModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/DenseTimeValueModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _DENSE_TIME_VALUE_MODEL_H_ -#define _DENSE_TIME_VALUE_MODEL_H_ +#ifndef SV_DENSE_TIME_VALUE_MODEL_H +#define SV_DENSE_TIME_VALUE_MODEL_H #include @@ -64,7 +64,8 @@ * If the channel is given as -1, mix all available channels and * return the result. */ - virtual std::vector getData(int channel, sv_frame_t start, sv_frame_t count) const = 0; + virtual floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) + const = 0; /** * Get the specified set of samples from given contiguous range of @@ -72,12 +73,18 @@ * format. Returned vector may have fewer samples than requested, * if the end of file was reached. */ - virtual std::vector> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const = 0; + virtual std::vector getMultiChannelData(int fromchannel, + int tochannel, + sv_frame_t start, + sv_frame_t count) + const = 0; virtual bool canPlay() const { return true; } virtual QString getDefaultPlayClipId() const { return ""; } - virtual QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const; + virtual QString toDelimitedDataStringSubset(QString delimiter, + sv_frame_t f0, sv_frame_t f1) + const; QString getTypeName() const { return tr("Dense Time-Value"); } }; diff -r d4a28d1479a8 -r 710e6250a401 data/model/EditableDenseThreeDimensionalModel.cpp --- a/data/model/EditableDenseThreeDimensionalModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/EditableDenseThreeDimensionalModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -321,7 +321,7 @@ QWriteLocker locker(&m_lock); while (index >= int(m_data.size())) { - m_data.push_back(Column()); + m_data.push_back(Column()); m_trunc.push_back(0); } @@ -332,14 +332,14 @@ if (ISNAN(value) || ISINF(value)) { continue; } - if (!m_haveExtents || value < m_minimum) { - m_minimum = value; - allChange = true; - } - if (!m_haveExtents || value > m_maximum) { - m_maximum = value; - allChange = true; - } + if (!m_haveExtents || value < m_minimum) { + m_minimum = value; + allChange = true; + } + if (!m_haveExtents || value > m_maximum) { + m_maximum = value; + allChange = true; + } m_haveExtents = true; } @@ -351,26 +351,26 @@ windowStart *= m_resolution; if (m_notifyOnAdd) { - if (allChange) { - emit modelChanged(); - } else { - emit modelChangedWithin(windowStart, windowStart + m_resolution); - } + if (allChange) { + emit modelChanged(); + } else { + emit modelChangedWithin(windowStart, windowStart + m_resolution); + } } else { - if (allChange) { - m_sinceLastNotifyMin = -1; - m_sinceLastNotifyMax = -1; - emit modelChanged(); - } else { - if (m_sinceLastNotifyMin == -1 || - windowStart < m_sinceLastNotifyMin) { - m_sinceLastNotifyMin = windowStart; - } - if (m_sinceLastNotifyMax == -1 || - windowStart > m_sinceLastNotifyMax) { - m_sinceLastNotifyMax = windowStart; - } - } + if (allChange) { + m_sinceLastNotifyMin = -1; + m_sinceLastNotifyMax = -1; + emit modelChanged(); + } else { + if (m_sinceLastNotifyMin == -1 || + windowStart < m_sinceLastNotifyMin) { + m_sinceLastNotifyMin = windowStart; + } + if (m_sinceLastNotifyMax == -1 || + windowStart > m_sinceLastNotifyMax) { + m_sinceLastNotifyMax = windowStart; + } + } } } @@ -455,34 +455,34 @@ if (n[j]) sample[j] /= n[j]; } - return LogRange::useLogScale(sample); + return LogRange::shouldUseLogScale(sample); } void EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update) { if (m_completion != completion) { - m_completion = completion; + m_completion = completion; - if (completion == 100) { + if (completion == 100) { - m_notifyOnAdd = true; // henceforth - emit modelChanged(); + m_notifyOnAdd = true; // henceforth + emit modelChanged(); - } else if (!m_notifyOnAdd) { + } else if (!m_notifyOnAdd) { - if (update && + if (update && m_sinceLastNotifyMin >= 0 && - m_sinceLastNotifyMax >= 0) { - emit modelChangedWithin(m_sinceLastNotifyMin, + m_sinceLastNotifyMax >= 0) { + emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax + m_resolution); - m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; - } else { - emit completionChanged(); - } - } else { - emit completionChanged(); - } + m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; + } else { + emit completionChanged(); + } + } else { + emit completionChanged(); + } } } @@ -493,7 +493,7 @@ QString s; for (int i = 0; in_range_for(m_data, i); ++i) { QStringList list; - for (int j = 0; in_range_for(m_data.at(i), j); ++j) { + for (int j = 0; in_range_for(m_data.at(i), j); ++j) { list << QString("%1").arg(m_data.at(i).at(j)); } s += list.join(delimiter) + "\n"; @@ -531,36 +531,36 @@ SVDEBUG << "EditableDenseThreeDimensionalModel::toXml" << endl; Model::toXml - (out, indent, + (out, indent, QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" startFrame=\"%6\" %7") - .arg(m_resolution) - .arg(m_yBinCount) - .arg(m_minimum) - .arg(m_maximum) - .arg(getObjectExportId(&m_data)) + .arg(m_resolution) + .arg(m_yBinCount) + .arg(m_minimum) + .arg(m_maximum) + .arg(getObjectExportId(&m_data)) .arg(m_startFrame) - .arg(extraAttributes)); + .arg(extraAttributes)); out << indent; out << QString("\n") - .arg(getObjectExportId(&m_data)); + .arg(getObjectExportId(&m_data)); for (int i = 0; i < (int)m_binNames.size(); ++i) { - if (m_binNames[i] != "") { - out << indent + " "; - out << QString("\n") - .arg(i).arg(m_binNames[i]); - } + if (m_binNames[i] != "") { + out << indent + " "; + out << QString("\n") + .arg(i).arg(m_binNames[i]); + } } for (int i = 0; i < (int)m_data.size(); ++i) { - out << indent + " "; - out << QString("").arg(i); - for (int j = 0; j < (int)m_data.at(i).size(); ++j) { - if (j > 0) out << " "; - out << m_data.at(i).at(j); - } - out << QString("\n"); + out << indent + " "; + out << QString("").arg(i); + for (int j = 0; j < (int)m_data.at(i).size(); ++j) { + if (j > 0) out << " "; + out << m_data.at(i).at(j); + } + out << QString("\n"); out.flush(); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/EditableDenseThreeDimensionalModel.h --- a/data/model/EditableDenseThreeDimensionalModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/EditableDenseThreeDimensionalModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -43,10 +43,10 @@ }; EditableDenseThreeDimensionalModel(sv_samplerate_t sampleRate, - int resolution, - int height, + int resolution, + int height, CompressionType compression, - bool notifyOnAdd = true); + bool notifyOnAdd = true); virtual bool isOK() const; diff -r d4a28d1479a8 -r 710e6250a401 data/model/FFTModel.cpp --- a/data/model/FFTModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/FFTModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -19,6 +19,7 @@ #include "base/Profiler.h" #include "base/Pitch.h" #include "base/HitCount.h" +#include "base/Debug.h" #include @@ -44,11 +45,16 @@ m_fftSize(fftSize), m_windower(windowType, windowSize), m_fft(fftSize), + m_cacheWriteIndex(0), m_cacheSize(3) { + while (m_cached.size() < m_cacheSize) { + m_cached.push_back({ -1, cvec(m_fftSize / 2 + 1) }); + } + if (m_windowSize > m_fftSize) { - cerr << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize - << ") must be at least FFT size (" << m_fftSize << ")" << endl; + SVCERR << "ERROR: FFTModel::FFTModel: window size (" << m_windowSize + << ") must be at least FFT size (" << m_fftSize << ")" << endl; throw invalid_argument("FFTModel window size must be at least FFT size"); } @@ -67,7 +73,7 @@ FFTModel::sourceModelAboutToBeDeleted() { if (m_model) { - cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl; + SVDEBUG << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl; m_model = 0; } } @@ -188,7 +194,7 @@ return true; } -vector +FFTModel::fvec FFTModel::getSourceSamples(int column) const { // m_fftSize may be greater than m_windowSize, but not the reverse @@ -204,7 +210,7 @@ return data; } else { vector pad(off, 0.f); - vector padded; + fvec padded; padded.reserve(m_fftSize); padded.insert(padded.end(), pad.begin(), pad.end()); padded.insert(padded.end(), data.begin(), data.end()); @@ -213,7 +219,7 @@ } } -vector +FFTModel::fvec FFTModel::getSourceData(pair range) const { // cerr << "getSourceData(" << range.first << "," << range.second @@ -235,16 +241,20 @@ sv_frame_t discard = range.first - m_savedData.range.first; - vector acc(m_savedData.data.begin() + discard, - m_savedData.data.end()); + fvec data; + data.reserve(range.second - range.first); - vector rest = - getSourceDataUncached({ m_savedData.range.second, range.second }); + data.insert(data.end(), + m_savedData.data.begin() + discard, + m_savedData.data.end()); - acc.insert(acc.end(), rest.begin(), rest.end()); + fvec rest = getSourceDataUncached + ({ m_savedData.range.second, range.second }); + + data.insert(data.end(), rest.begin(), rest.end()); - m_savedData = { range, acc }; - return acc; + m_savedData = { range, data }; + return data; } else { @@ -256,9 +266,11 @@ } } -vector +FFTModel::fvec FFTModel::getSourceDataUncached(pair range) const { + Profiler profiler("FFTModel::getSourceDataUncached"); + decltype(range.first) pfx = 0; if (range.first < 0) { pfx = -range.first; @@ -284,21 +296,21 @@ } if (m_channel == -1) { - int channels = m_model->getChannelCount(); - if (channels > 1) { + int channels = m_model->getChannelCount(); + if (channels > 1) { int n = int(data.size()); float factor = 1.f / float(channels); // use mean instead of sum for fft model input - for (int i = 0; i < n; ++i) { - data[i] *= factor; - } - } + for (int i = 0; i < n; ++i) { + data[i] *= factor; + } + } } return data; } -vector> +const FFTModel::cvec & FFTModel::getFFTColumn(int n) const { // The small cache (i.e. the m_cached deque) is for cases where @@ -321,16 +333,14 @@ m_windower.cut(samples.data()); breakfastquay::v_fftshift(samples.data(), m_fftSize); - vector> col(m_fftSize/2 + 1); + cvec &col = m_cached[m_cacheWriteIndex].col; m_fft.forwardInterleaved(samples.data(), reinterpret_cast(col.data())); - SavedColumn sc { n, col }; - if (m_cached.size() >= m_cacheSize) { - m_cached.pop_front(); - } - m_cached.push_back(sc); + m_cached[m_cacheWriteIndex].n = n; + + m_cacheWriteIndex = (m_cacheWriteIndex + 1) % m_cacheSize; return col; } diff -r d4a28d1479a8 -r 710e6250a401 data/model/FFTModel.h --- a/data/model/FFTModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/FFTModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -22,11 +22,11 @@ #include "base/Window.h" #include +#include #include #include #include -#include /** * An implementation of DenseThreeDimensionalModel that makes FFT data @@ -167,22 +167,27 @@ return { startFrame, endFrame }; } - std::vector > getFFTColumn(int column) const; - std::vector getSourceSamples(int column) const; - std::vector getSourceData(std::pair) const; - std::vector getSourceDataUncached(std::pair) const; + typedef std::vector> fvec; + typedef std::vector, + breakfastquay::StlAllocator>> cvec; + + const cvec &getFFTColumn(int column) const; // returns ref for immediate use only + fvec getSourceSamples(int column) const; + fvec getSourceData(std::pair) const; + fvec getSourceDataUncached(std::pair) const; struct SavedSourceData { std::pair range; - std::vector data; + fvec data; }; mutable SavedSourceData m_savedData; - + struct SavedColumn { int n; - std::vector > col; + cvec col; }; - mutable std::deque m_cached; + mutable std::vector m_cached; + mutable size_t m_cacheWriteIndex; size_t m_cacheSize; }; diff -r d4a28d1479a8 -r 710e6250a401 data/model/FlexiNoteModel.h --- a/data/model/FlexiNoteModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/FlexiNoteModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -44,7 +44,7 @@ public: FlexiNote(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { } FlexiNote(sv_frame_t _frame, float _value, sv_frame_t _duration, float _level, QString _label) : - frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { } + frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { } int getDimensions() const { return 3; } @@ -60,9 +60,9 @@ QString indent = "", QString extraAttributes = "") const { - stream << + stream << QString("%1\n") - .arg(indent).arg(frame).arg(value).arg(duration).arg(level) + .arg(indent).arg(frame).arg(value).arg(duration).arg(level) .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes); } @@ -80,21 +80,21 @@ } struct Comparator { - bool operator()(const FlexiNote &p1, - const FlexiNote &p2) const { - 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; + bool operator()(const FlexiNote &p1, + const FlexiNote &p2) const { + 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; - } + return p1.label < p2.label; + } }; struct OrderComparator { - bool operator()(const FlexiNote &p1, - const FlexiNote &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const FlexiNote &p1, + const FlexiNote &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -106,21 +106,21 @@ public: FlexiNoteModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, notifyOnAdd), - m_valueQuantization(0) + IntervalModel(sampleRate, resolution, notifyOnAdd), + m_valueQuantization(0) { - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } FlexiNoteModel(sv_samplerate_t sampleRate, int resolution, - float valueMinimum, float valueMaximum, - bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + IntervalModel(sampleRate, resolution, valueMinimum, valueMaximum, notifyOnAdd), - m_valueQuantization(0) + m_valueQuantization(0) { - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } virtual ~FlexiNoteModel() @@ -150,10 +150,10 @@ << extraAttributes.toStdString() << std::endl; IntervalModel::toXml - (out, + (out, indent, - QString("%1 subtype=\"flexinote\" valueQuantization=\"%2\"") - .arg(extraAttributes).arg(m_valueQuantization)); + QString("%1 subtype=\"flexinote\" valueQuantization=\"%2\"") + .arg(extraAttributes).arg(m_valueQuantization)); } /** @@ -235,10 +235,10 @@ NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const { - PointList points = getPoints(startFrame, endFrame); + PointList points = getPoints(startFrame, endFrame); NoteList notes; for (PointList::iterator pli = points.begin(); pli != points.end(); ++pli) { - sv_frame_t duration = pli->duration; + sv_frame_t duration = pli->duration; if (duration == 0 || duration == 1) { duration = sv_frame_t(getSampleRate() / 20); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/ImageModel.h --- a/data/model/ImageModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/ImageModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -47,9 +47,9 @@ QString indent = "", QString extraAttributes = "") const { - stream << + stream << QString("%1\n") - .arg(indent).arg(frame) + .arg(indent).arg(frame) .arg(encodeEntities(image)) .arg(encodeEntities(label)) .arg(extraAttributes); @@ -65,19 +65,19 @@ } struct Comparator { - bool operator()(const ImagePoint &p1, - const ImagePoint &p2) const { - if (p1.frame != p2.frame) return p1.frame < p2.frame; + bool operator()(const ImagePoint &p1, + const ImagePoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; if (p1.label != p2.label) return p1.label < p2.label; - return p1.image < p2.image; - } + return p1.image < p2.image; + } }; struct OrderComparator { - bool operator()(const ImagePoint &p1, - const ImagePoint &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const ImagePoint &p1, + const ImagePoint &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -90,7 +90,7 @@ public: ImageModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) : - SparseModel(sampleRate, resolution, notifyOnAdd) + SparseModel(sampleRate, resolution, notifyOnAdd) { } QString getTypeName() const { return tr("Image"); } @@ -100,10 +100,10 @@ QString extraAttributes = "") const { SparseModel::toXml - (out, + (out, indent, - QString("%1 subtype=\"image\"") - .arg(extraAttributes)); + QString("%1 subtype=\"image\"") + .arg(extraAttributes)); } /** @@ -116,25 +116,25 @@ const ImagePoint &point, QString newImage, QString newLabel) : - m_model(model), m_oldPoint(point), m_newPoint(point) { - m_newPoint.image = newImage; + m_model(model), m_oldPoint(point), m_newPoint(point) { + m_newPoint.image = newImage; m_newPoint.label = newLabel; - } + } - virtual QString getName() const { return tr("Edit Image"); } + virtual QString getName() const { return tr("Edit Image"); } - virtual void execute() { - m_model->deletePoint(m_oldPoint); - m_model->addPoint(m_newPoint); - std::swap(m_oldPoint, m_newPoint); - } + virtual void execute() { + m_model->deletePoint(m_oldPoint); + m_model->addPoint(m_newPoint); + std::swap(m_oldPoint, m_newPoint); + } - virtual void unexecute() { execute(); } + virtual void unexecute() { execute(); } private: - ImageModel *m_model; - ImagePoint m_oldPoint; - ImagePoint m_newPoint; + ImageModel *m_model; + ImagePoint m_oldPoint; + ImagePoint m_newPoint; }; /** diff -r d4a28d1479a8 -r 710e6250a401 data/model/IntervalModel.h --- a/data/model/IntervalModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/IntervalModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -31,13 +31,13 @@ public: IntervalModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) : - SparseValueModel(sampleRate, resolution, notifyOnAdd) + SparseValueModel(sampleRate, resolution, notifyOnAdd) { } IntervalModel(sv_samplerate_t sampleRate, int resolution, float valueMinimum, float valueMaximum, bool notifyOnAdd = true) : - SparseValueModel(sampleRate, resolution, + SparseValueModel(sampleRate, resolution, valueMinimum, valueMaximum, notifyOnAdd) { } diff -r d4a28d1479a8 -r 710e6250a401 data/model/Model.cpp --- a/data/model/Model.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/Model.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -27,9 +27,11 @@ // SVDEBUG << "Model::~Model(" << this << ")" << endl; if (!m_aboutToDelete) { - SVDEBUG << "NOTE: Model::~Model(" << this << ", \"" - << objectName() << "\"): Model deleted " - << "with no aboutToDelete notification" << endl; + SVDEBUG << "NOTE: Model(" << this << ", \"" + << objectName() << "\", type uri <" + << m_typeUri << ">)::~Model(): Model deleted " + << "with no aboutToDelete notification" + << endl; } if (m_alignment) { @@ -38,6 +40,20 @@ } } +int +Model::getNextId() +{ + static int nextId = 0; + static QMutex mutex; + QMutexLocker locker(&mutex); + int i = nextId; + if (nextId == INT_MAX) { + nextId = INT_MIN; + } + ++nextId; + return i; +} + void Model::setSourceModel(Model *model) { @@ -59,13 +75,16 @@ void Model::aboutToDelete() { -// cerr << "Model(" << this << ")::aboutToDelete()" << endl; +// SVDEBUG << "Model(" << this << ", \"" +// << objectName() << "\", type uri <" +// << m_typeUri << ">)::aboutToDelete()" << endl; if (m_aboutToDelete) { - cerr << "WARNING: Model(" << this << ", \"" - << objectName() << "\")::aboutToDelete: " - << "aboutToDelete called more than once for the same model" - << endl; + SVDEBUG << "WARNING: Model(" << this << ", \"" + << objectName() << "\", type uri <" + << m_typeUri << ">)::aboutToDelete: " + << "aboutToDelete called more than once for the same model" + << endl; } emit aboutToBeDeleted(); @@ -180,12 +199,12 @@ { stream << indent; stream << QString("\n") - .arg(getObjectExportId(this)) - .arg(encodeEntities(objectName())) - .arg(getSampleRate()) - .arg(getStartFrame()) - .arg(getEndFrame()) - .arg(extraAttributes); + .arg(getObjectExportId(this)) + .arg(encodeEntities(objectName())) + .arg(getSampleRate()) + .arg(getStartFrame()) + .arg(getEndFrame()) + .arg(extraAttributes); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/Model.h --- a/data/model/Model.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/Model.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _MODEL_H_ -#define _MODEL_H_ +#ifndef SV_MODEL_H +#define SV_MODEL_H #include #include @@ -27,13 +27,15 @@ class ZoomConstraint; class AlignmentModel; +typedef int ModelId; + /** * Model is the base class for all data models that represent any sort * of data on a time scale based on an audio frame rate. */ class Model : public QObject, - public XmlExportable, + public XmlExportable, public Playable { Q_OBJECT @@ -53,7 +55,11 @@ virtual sv_frame_t getStartFrame() const = 0; /** - * Return the last audio frame spanned by the model. + * Return the audio frame at the end of the model, i.e. 1 more + * than the final frame contained within the model. The end frame + * minus the start frame should yield the total duration in frames + * spanned by the model. This is consistent with the definition of + * the end frame of a Selection object. */ virtual sv_frame_t getEndFrame() const = 0; @@ -91,6 +97,18 @@ virtual QString getTypeName() const = 0; /** + * Return true if this is a sparse model. + */ + virtual bool isSparse() const { return false; } + + /** + * Return an id for this model. The id is guaranteed to be a + * unique identifier for this model among all models that may ever + * exist within this single run of the application. + */ + ModelId getId() const { return m_id; } + + /** * Mark the model as abandoning. This means that the application * no longer needs it, so it can stop doing any background * calculations it may be involved in. Note that as far as the @@ -125,9 +143,9 @@ * getCompletion(). */ virtual bool isReady(int *completion = 0) const { - bool ok = isOK(); - if (completion) *completion = (ok ? 100 : 0); - return ok; + bool ok = isOK(); + if (completion) *completion = (ok ? 100 : 0); + return ok; } static const int COMPLETION_UNKNOWN; @@ -220,11 +238,11 @@ virtual QString toDelimitedDataString(QString delimiter) const { return toDelimitedDataStringSubset - (delimiter, getStartFrame(), getEndFrame() + 1); + (delimiter, getStartFrame(), getEndFrame()); } virtual QString toDelimitedDataStringWithOptions(QString delimiter, DataExportOptions opts) const { return toDelimitedDataStringSubsetWithOptions - (delimiter, opts, getStartFrame(), getEndFrame() + 1); + (delimiter, opts, getStartFrame(), getEndFrame()); } virtual QString toDelimitedDataStringSubset(QString, sv_frame_t /* f0 */, sv_frame_t /* f1 */) const { return ""; @@ -282,7 +300,8 @@ void aboutToBeDeleted(); protected: - Model() : + Model() : + m_id(getNextId()), m_sourceModel(0), m_alignment(0), m_abandoning(false), @@ -292,11 +311,14 @@ Model(const Model &); Model &operator=(const Model &); + const ModelId m_id; Model *m_sourceModel; AlignmentModel *m_alignment; QString m_typeUri; bool m_abandoning; bool m_aboutToDelete; + + int getNextId(); }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/model/ModelDataTableModel.cpp --- a/data/model/ModelDataTableModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/ModelDataTableModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -73,16 +73,12 @@ if (!m_model) return false; if (parent.isValid()) return false; - emit beginInsertRows(parent, row, row); - Command *command = m_model->getInsertRowCommand(getUnsorted(row)); if (command) { emit addCommand(command); } - emit endInsertRows(); - return (command ? true : false); } @@ -92,16 +88,12 @@ if (!m_model) return false; if (parent.isValid()) return false; - emit beginRemoveRows(parent, row, row); - Command *command = m_model->getRemoveRowCommand(getUnsorted(row)); if (command) { emit addCommand(command); } - emit endRemoveRows(); - return (command ? true : false); } @@ -144,7 +136,8 @@ { if (!m_model) return 0; if (parent.isValid()) return 0; - return m_model->getRowCount(); + int count = m_model->getRowCount(); + return count; } int @@ -215,14 +208,37 @@ void ModelDataTableModel::modelChanged() { + SVDEBUG << "ModelDataTableModel::modelChanged" << endl; + QModelIndex ix0; + QModelIndex ix1; + if (rowCount() > 0) { + ix0 = createIndex(0, 0); + int lastCol = columnCount() - 1; + if (lastCol < 0) lastCol = 0; + ix1 = createIndex(rowCount(), lastCol); + } + SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl; + emit dataChanged(ix0, ix1); clearSort(); emit layoutChanged(); } void -ModelDataTableModel::modelChangedWithin(sv_frame_t, sv_frame_t) +ModelDataTableModel::modelChangedWithin(sv_frame_t f0, sv_frame_t f1) { - //!!! inefficient + SVDEBUG << "ModelDataTableModel::modelChangedWithin(" << f0 << "," << f1 << ")" << endl; + QModelIndex ix0 = getModelIndexForFrame(f0); + QModelIndex ix1 = getModelIndexForFrame(f1); + int row0 = ix0.row(); + int row1 = ix1.row(); + if (row0 > 0) { + ix0 = createIndex(row0 - 1, ix0.column(), (void *)0); + } + if (row1 + 1 < rowCount()) { + ix1 = createIndex(row1 + 1, ix1.column(), (void *)0); + } + SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl; + emit dataChanged(ix0, ix1); clearSort(); emit layoutChanged(); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/ModelDataTableModel.h --- a/data/model/ModelDataTableModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/ModelDataTableModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _MODEL_DATA_TABLE_MODEL_H_ -#define _MODEL_DATA_TABLE_MODEL_H_ +#ifndef SV_MODEL_DATA_TABLE_MODEL_H +#define SV_MODEL_DATA_TABLE_MODEL_H #include diff -r d4a28d1479a8 -r 710e6250a401 data/model/NoteData.h --- a/data/model/NoteData.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/NoteData.h Mon Sep 17 13:51:14 2018 +0100 @@ -12,8 +12,8 @@ COPYING included with this distribution for more information. */ -#ifndef NOTE_DATA_H -#define NOTE_DATA_H +#ifndef SV_NOTE_DATA_H +#define SV_NOTE_DATA_H #include @@ -22,8 +22,8 @@ struct NoteData { NoteData(sv_frame_t _start, sv_frame_t _dur, int _mp, int _vel) : - start(_start), duration(_dur), midiPitch(_mp), frequency(0), - isMidiPitchQuantized(true), velocity(_vel), channel(0) { }; + start(_start), duration(_dur), midiPitch(_mp), frequency(0), + isMidiPitchQuantized(true), velocity(_vel), channel(0) { }; sv_frame_t start; // audio sample frame sv_frame_t duration; // in audio sample frames diff -r d4a28d1479a8 -r 710e6250a401 data/model/NoteModel.h --- a/data/model/NoteModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/NoteModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _NOTE_MODEL_H_ -#define _NOTE_MODEL_H_ +#ifndef SV_NOTE_MODEL_H +#define SV_NOTE_MODEL_H #include "IntervalModel.h" #include "NoteData.h" @@ -40,7 +40,7 @@ public: Note(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0), level(1.f) { } Note(sv_frame_t _frame, float _value, sv_frame_t _duration, float _level, QString _label) : - frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { } + frame(_frame), value(_value), duration(_duration), level(_level), label(_label) { } int getDimensions() const { return 3; } @@ -56,9 +56,9 @@ QString indent = "", QString extraAttributes = "") const { - stream << + stream << QString("%1\n") - .arg(indent).arg(frame).arg(value).arg(duration).arg(level) + .arg(indent).arg(frame).arg(value).arg(duration).arg(level) .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes); } @@ -75,21 +75,21 @@ } struct Comparator { - bool operator()(const Note &p1, - const Note &p2) const { - 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; + bool operator()(const Note &p1, + const Note &p2) const { + 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; - } + return p1.label < p2.label; + } }; struct OrderComparator { - bool operator()(const Note &p1, - const Note &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const Note &p1, + const Note &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -100,22 +100,22 @@ public: NoteModel(sv_samplerate_t sampleRate, int resolution, - bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, notifyOnAdd), - m_valueQuantization(0) + bool notifyOnAdd = true) : + IntervalModel(sampleRate, resolution, notifyOnAdd), + m_valueQuantization(0) { - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } NoteModel(sv_samplerate_t sampleRate, int resolution, - float valueMinimum, float valueMaximum, - bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + IntervalModel(sampleRate, resolution, valueMinimum, valueMaximum, notifyOnAdd), - m_valueQuantization(0) + m_valueQuantization(0) { - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } virtual ~NoteModel() @@ -143,10 +143,10 @@ << extraAttributes.toStdString() << std::endl; IntervalModel::toXml - (out, + (out, indent, - QString("%1 subtype=\"note\" valueQuantization=\"%2\"") - .arg(extraAttributes).arg(m_valueQuantization)); + QString("%1 subtype=\"note\" valueQuantization=\"%2\"") + .arg(extraAttributes).arg(m_valueQuantization)); } /** @@ -227,13 +227,13 @@ NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const { - PointList points = getPoints(startFrame, endFrame); + PointList points = getPoints(startFrame, endFrame); NoteList notes; for (PointList::iterator pli = - points.begin(); pli != points.end(); ++pli) { + points.begin(); pli != points.end(); ++pli) { - sv_frame_t duration = pli->duration; + sv_frame_t duration = pli->duration; if (duration == 0 || duration == 1) { duration = sv_frame_t(getSampleRate() / 20); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/PowerOfSqrtTwoZoomConstraint.cpp --- a/data/model/PowerOfSqrtTwoZoomConstraint.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -21,7 +21,7 @@ ZoomLevel PowerOfSqrtTwoZoomConstraint::getNearestZoomLevel(ZoomLevel requested, - RoundingDirection dir) const + RoundingDirection dir) const { int type, power; int blockSize; @@ -47,30 +47,30 @@ int PowerOfSqrtTwoZoomConstraint::getNearestBlockSize(int blockSize, - int &type, - int &power, - RoundingDirection dir) const + int &type, + int &power, + RoundingDirection dir) const { // cerr << "given " << blockSize << endl; int minCachePower = getMinCachePower(); if (blockSize < (1 << minCachePower)) { - type = -1; - power = 0; - float val = 1.0, prevVal = 1.0; - while (val + 0.01 < blockSize) { - prevVal = val; - val *= sqrtf(2.f); - } - int rval; - if (dir == RoundUp) rval = int(val + 0.01f); - else if (dir == RoundDown) rval = int(prevVal + 0.01f); - else if (val - float(blockSize) < + type = -1; + power = 0; + float val = 1.0, prevVal = 1.0; + while (val + 0.01 < blockSize) { + prevVal = val; + val *= sqrtf(2.f); + } + int rval; + if (dir == RoundUp) rval = int(val + 0.01f); + else if (dir == RoundDown) rval = int(prevVal + 0.01f); + else if (val - float(blockSize) < float(blockSize) - prevVal) rval = int(val + 0.01f); - else rval = int(prevVal + 0.01); -// SVDEBUG << "returning " << rval << endl; - return rval; + else rval = int(prevVal + 0.01); +// SVDEBUG << "returning " << rval << endl; + return rval; } int prevBase = (1 << minCachePower); @@ -81,46 +81,46 @@ for (unsigned int i = 0; ; ++i) { - power = minCachePower + i/2; - type = i % 2; + power = minCachePower + i/2; + type = i % 2; - int base; - if (type == 0) { - base = (1 << power); - } else { - base = (((unsigned int)((1 << minCachePower) * sqrt(2.) + 0.01)) - << (power - minCachePower)); - } + int base; + if (type == 0) { + base = (1 << power); + } else { + base = (((unsigned int)((1 << minCachePower) * sqrt(2.) + 0.01)) + << (power - minCachePower)); + } -// SVDEBUG << "Testing base " << base << endl; +// SVDEBUG << "Testing base " << base << endl; if (base == blockSize) { result = base; break; } - if (base > blockSize) { - if (dir == RoundNearest) { - if (base - blockSize < blockSize - prevBase) { - dir = RoundUp; - } else { - dir = RoundDown; - } - } - if (dir == RoundUp) { - result = base; - break; - } else { - type = prevType; - power = prevPower; - result = prevBase; - break; - } - } + if (base > blockSize) { + if (dir == RoundNearest) { + if (base - blockSize < blockSize - prevBase) { + dir = RoundUp; + } else { + dir = RoundDown; + } + } + if (dir == RoundUp) { + result = base; + break; + } else { + type = prevType; + power = prevPower; + result = prevBase; + break; + } + } - prevType = type; - prevPower = power; - prevBase = base; + prevType = type; + prevPower = power; + prevBase = base; } if (result > getMaxZoomLevel().level) { diff -r d4a28d1479a8 -r 710e6250a401 data/model/PowerOfSqrtTwoZoomConstraint.h --- a/data/model/PowerOfSqrtTwoZoomConstraint.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/PowerOfSqrtTwoZoomConstraint.h Mon Sep 17 13:51:14 2018 +0100 @@ -31,7 +31,7 @@ int &type, int &power, RoundingDirection dir = RoundNearest) - const; + const; }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/model/PowerOfTwoZoomConstraint.cpp --- a/data/model/PowerOfTwoZoomConstraint.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/PowerOfTwoZoomConstraint.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -45,28 +45,28 @@ int PowerOfTwoZoomConstraint::getNearestBlockSize(int req, - RoundingDirection dir) const + RoundingDirection dir) const { int result = 0; for (int bs = 1; ; bs *= 2) { - if (bs >= req) { - if (dir == RoundNearest) { - if (bs - req < req - bs/2) { - result = bs; - break; - } else { - result = bs/2; - break; - } - } else if (dir == RoundDown) { - result = bs/2; - break; - } else { - result = bs; - break; - } - } + if (bs >= req) { + if (dir == RoundNearest) { + if (bs - req < req - bs/2) { + result = bs; + break; + } else { + result = bs/2; + break; + } + } else if (dir == RoundDown) { + result = bs/2; + break; + } else { + result = bs; + break; + } + } } if (result > getMaxZoomLevel().level) result = getMaxZoomLevel().level; diff -r d4a28d1479a8 -r 710e6250a401 data/model/PowerOfTwoZoomConstraint.h --- a/data/model/PowerOfTwoZoomConstraint.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/PowerOfTwoZoomConstraint.h Mon Sep 17 13:51:14 2018 +0100 @@ -28,7 +28,7 @@ protected: virtual int getNearestBlockSize(int requested, RoundingDirection dir = RoundNearest) - const; + const; }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/model/RangeSummarisableTimeValueModel.h --- a/data/model/RangeSummarisableTimeValueModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/RangeSummarisableTimeValueModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _RANGE_SUMMARISABLE_TIME_VALUE_MODEL_H_ -#define _RANGE_SUMMARISABLE_TIME_VALUE_MODEL_H_ +#ifndef SV_RANGE_SUMMARISABLE_TIME_VALUE_MODEL_H +#define SV_RANGE_SUMMARISABLE_TIME_VALUE_MODEL_H #include diff -r d4a28d1479a8 -r 710e6250a401 data/model/ReadOnlyWaveFileModel.cpp --- a/data/model/ReadOnlyWaveFileModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/ReadOnlyWaveFileModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -106,6 +106,10 @@ if (m_fillThread) m_fillThread->wait(); if (m_myReader) delete m_reader; m_reader = 0; + + SVDEBUG << "ReadOnlyWaveFileModel: Destructor exiting; we had caches of " + << (m_cache[0].size() * sizeof(Range)) << " and " + << (m_cache[1].size() * sizeof(Range)) << " bytes" << endl; } bool @@ -202,12 +206,18 @@ return ""; } -vector -ReadOnlyWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const +floatvec_t +ReadOnlyWaveFileModel::getData(int channel, + sv_frame_t start, + sv_frame_t count) + const { - // Read directly from the file. This is used for e.g. audio - // playback or input to transforms. + // Read a single channel (if channel >= 0) or a mixdown of all + // channels (if channel == -1) directly from the file. This is + // used for e.g. audio playback or input to transforms. + Profiler profiler("ReadOnlyWaveFileModel::getData"); + #ifdef DEBUG_WAVE_FILE_MODEL cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << channel << ", " << start << ", " << count << endl; #endif @@ -215,7 +225,7 @@ int channels = getChannelCount(); if (channel >= channels) { - cerr << "ERROR: WaveFileModel::getData: channel (" + SVCERR << "ERROR: WaveFileModel::getData: channel (" << channel << ") >= channel count (" << channels << ")" << endl; return {}; @@ -236,12 +246,12 @@ } } - vector interleaved = m_reader->getInterleavedFrames(start, count); + floatvec_t interleaved = m_reader->getInterleavedFrames(start, count); if (channels == 1) return interleaved; sv_frame_t obtained = interleaved.size() / channels; - vector result(obtained, 0.f); + floatvec_t result(obtained, 0.f); if (channel != -1) { // get a single channel @@ -260,12 +270,14 @@ return result; } -vector> +vector ReadOnlyWaveFileModel::getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const { - // Read directly from the file. This is used for e.g. audio - // playback or input to transforms. + // Read a set of channels directly from the file. This is used + // for e.g. audio playback or input to transforms. + + Profiler profiler("ReadOnlyWaveFileModel::getMultiChannelData"); #ifdef DEBUG_WAVE_FILE_MODEL cout << "ReadOnlyWaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << endl; @@ -274,16 +286,18 @@ int channels = getChannelCount(); if (fromchannel > tochannel) { - cerr << "ERROR: ReadOnlyWaveFileModel::getData: fromchannel (" - << fromchannel << ") > tochannel (" << tochannel << ")" - << endl; + SVCERR << "ERROR: ReadOnlyWaveFileModel::getMultiChannelData: " + << "fromchannel (" << fromchannel + << ") > tochannel (" << tochannel << ")" + << endl; return {}; } if (tochannel >= channels) { - cerr << "ERROR: ReadOnlyWaveFileModel::getData: tochannel (" - << tochannel << ") >= channel count (" << channels << ")" - << endl; + SVCERR << "ERROR: ReadOnlyWaveFileModel::getMultiChannelData: " + << "tochannel (" << tochannel + << ") >= channel count (" << channels << ")" + << endl; return {}; } @@ -304,11 +318,11 @@ } } - vector interleaved = m_reader->getInterleavedFrames(start, count); + floatvec_t interleaved = m_reader->getInterleavedFrames(start, count); if (channels == 1) return { interleaved }; sv_frame_t obtained = interleaved.size() / channels; - vector> result(reqchannels, vector(obtained, 0.f)); + vector result(reqchannels, floatvec_t(obtained, 0.f)); for (int c = fromchannel; c <= tochannel; ++c) { int destc = c - fromchannel; @@ -361,13 +375,13 @@ if (cacheType != 0 && cacheType != 1) { - // We need to read directly from the file. We haven't got - // this cached. Hope the requested area is small. This is - // not optimal -- we'll end up reading the same frames twice - // for stereo files, in two separate calls to this method. - // We could fairly trivially handle this for most cases that - // matter by putting a single cache in getInterleavedFrames - // for short queries. + // We need to read directly from the file. We haven't got + // this cached. Hope the requested area is small. This is + // not optimal -- we'll end up reading the same frames twice + // for stereo files, in two separate calls to this method. + // We could fairly trivially handle this for most cases that + // matter by putting a single cache in getInterleavedFrames + // for short queries. m_directReadMutex.lock(); @@ -380,86 +394,86 @@ m_lastDirectReadCount = count; } - float max = 0.0, min = 0.0, total = 0.0; - sv_frame_t i = 0, got = 0; + float max = 0.0, min = 0.0, total = 0.0; + sv_frame_t i = 0, got = 0; - while (i < count) { + while (i < count) { - sv_frame_t index = i * channels + channel; - if (index >= (sv_frame_t)m_directRead.size()) break; + sv_frame_t index = i * channels + channel; + if (index >= (sv_frame_t)m_directRead.size()) break; - float sample = m_directRead[index]; + float sample = m_directRead[index]; if (sample > max || got == 0) max = sample; - if (sample < min || got == 0) min = sample; + if (sample < min || got == 0) min = sample; total += fabsf(sample); - ++i; + ++i; ++got; if (got == blockSize) { ranges.push_back(Range(min, max, total / float(got))); min = max = total = 0.0f; got = 0; - } - } + } + } m_directReadMutex.unlock(); - if (got > 0) { + if (got > 0) { ranges.push_back(Range(min, max, total / float(got))); - } + } - return; + return; } else { - QMutexLocker locker(&m_mutex); + QMutexLocker locker(&m_mutex); - const RangeBlock &cache = m_cache[cacheType]; + const RangeBlock &cache = m_cache[cacheType]; blockSize = roundedBlockSize; - sv_frame_t cacheBlock, div; + sv_frame_t cacheBlock, div; cacheBlock = (sv_frame_t(1) << m_zoomConstraint.getMinCachePower()); - if (cacheType == 1) { - cacheBlock = sv_frame_t(double(cacheBlock) * sqrt(2.) + 0.01); - } + if (cacheType == 1) { + cacheBlock = sv_frame_t(double(cacheBlock) * sqrt(2.) + 0.01); + } div = blockSize / cacheBlock; - sv_frame_t startIndex = start / cacheBlock; - sv_frame_t endIndex = (start + count) / cacheBlock; + sv_frame_t startIndex = start / cacheBlock; + sv_frame_t endIndex = (start + count) / cacheBlock; - float max = 0.0, min = 0.0, total = 0.0; - sv_frame_t i = 0, got = 0; + float max = 0.0, min = 0.0, total = 0.0; + sv_frame_t i = 0, got = 0; #ifdef DEBUG_WAVE_FILE_MODEL - cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", count " << count << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; + cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", count " << count << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; #endif - for (i = 0; i <= endIndex - startIndex; ) { + for (i = 0; i <= endIndex - startIndex; ) { - sv_frame_t index = (i + startIndex) * channels + channel; - if (!in_range_for(cache, index)) break; + sv_frame_t index = (i + startIndex) * channels + channel; + if (!in_range_for(cache, index)) break; const Range &range = cache[index]; if (range.max() > max || got == 0) max = range.max(); if (range.min() < min || got == 0) min = range.min(); total += range.absmean(); - ++i; + ++i; ++got; - if (got == div) { - ranges.push_back(Range(min, max, total / float(got))); + if (got == div) { + ranges.push_back(Range(min, max, total / float(got))); min = max = total = 0.0f; got = 0; - } - } - - if (got > 0) { + } + } + + if (got > 0) { ranges.push_back(Range(min, max, total / float(got))); - } + } } #ifdef DEBUG_WAVE_FILE_MODEL @@ -544,19 +558,19 @@ ReadOnlyWaveFileModel::fillTimerTimedOut() { if (m_fillThread) { - sv_frame_t fillExtent = m_fillThread->getFillExtent(); + sv_frame_t fillExtent = m_fillThread->getFillExtent(); #ifdef DEBUG_WAVE_FILE_MODEL SVDEBUG << "ReadOnlyWaveFileModel::fillTimerTimedOut: extent = " << fillExtent << endl; #endif - if (fillExtent > m_lastFillExtent) { - emit modelChangedWithin(m_lastFillExtent, fillExtent); - m_lastFillExtent = fillExtent; - } + if (fillExtent > m_lastFillExtent) { + emit modelChangedWithin(m_lastFillExtent, fillExtent); + m_lastFillExtent = fillExtent; + } } else { #ifdef DEBUG_WAVE_FILE_MODEL SVDEBUG << "ReadOnlyWaveFileModel::fillTimerTimedOut: no thread" << endl; #endif - emit modelChanged(); + emit modelChanged(); } } @@ -589,7 +603,7 @@ sv_frame_t frame = 0; const sv_frame_t readBlockSize = 32768; - vector block; + floatvec_t block; if (!m_model.isOK()) return; @@ -643,7 +657,7 @@ m_model.m_mutex.lock(); for (sv_frame_t i = 0; i < gotBlockSize; ++i) { - + for (int ch = 0; ch < channels; ++ch) { sv_frame_t index = channels * i + ch; diff -r d4a28d1479a8 -r 710e6250a401 data/model/ReadOnlyWaveFileModel.h --- a/data/model/ReadOnlyWaveFileModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/ReadOnlyWaveFileModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -36,8 +36,20 @@ Q_OBJECT public: + /** + * Construct a WaveFileModel from a source path and optional + * resampling target rate + */ ReadOnlyWaveFileModel(FileSource source, sv_samplerate_t targetRate = 0); + + /** + * Construct a WaveFileModel from a source path using an existing + * AudioFileReader. The model does not take ownership of the + * AudioFileReader, which remains managed by the caller and must + * outlive the model. + */ ReadOnlyWaveFileModel(FileSource source, AudioFileReader *reader); + ~ReadOnlyWaveFileModel(); bool isOK() const; @@ -64,9 +76,9 @@ void setStartFrame(sv_frame_t startFrame) { m_startFrame = startFrame; } - virtual std::vector getData(int channel, sv_frame_t start, sv_frame_t count) const; + virtual floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const; - virtual std::vector> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; + virtual std::vector getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; virtual int getSummaryBlockSize(int desired) const; @@ -93,15 +105,15 @@ { public: RangeCacheFillThread(ReadOnlyWaveFileModel &model) : - m_model(model), m_fillExtent(0), + m_model(model), m_fillExtent(0), m_frameCount(model.getFrameCount()) { } - sv_frame_t getFillExtent() const { return m_fillExtent; } + sv_frame_t getFillExtent() const { return m_fillExtent; } virtual void run(); protected: ReadOnlyWaveFileModel &m_model; - sv_frame_t m_fillExtent; + sv_frame_t m_fillExtent; sv_frame_t m_frameCount; }; @@ -122,7 +134,7 @@ bool m_exiting; static PowerOfSqrtTwoZoomConstraint m_zoomConstraint; - mutable std::vector m_directRead; + mutable floatvec_t m_directRead; mutable sv_frame_t m_lastDirectReadStart; mutable sv_frame_t m_lastDirectReadCount; mutable QMutex m_directReadMutex; diff -r d4a28d1479a8 -r 710e6250a401 data/model/RegionModel.h --- a/data/model/RegionModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/RegionModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -39,7 +39,7 @@ RegionRec() : frame(0), value(0.f), duration(0) { } RegionRec(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0) { } RegionRec(sv_frame_t _frame, float _value, sv_frame_t _duration, QString _label) : - frame(_frame), value(_value), duration(_duration), label(_label) { } + frame(_frame), value(_value), duration(_duration), label(_label) { } int getDimensions() const { return 3; } @@ -54,9 +54,9 @@ QString indent = "", QString extraAttributes = "") const { - stream << + stream << QString("%1\n") - .arg(indent).arg(frame).arg(value).arg(duration) + .arg(indent).arg(frame).arg(value).arg(duration) .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes); } @@ -71,20 +71,20 @@ } struct Comparator { - bool operator()(const RegionRec &p1, - const RegionRec &p2) const { - 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; - return p1.label < p2.label; - } + bool operator()(const RegionRec &p1, + const RegionRec &p2) const { + 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; + return p1.label < p2.label; + } }; struct OrderComparator { - bool operator()(const RegionRec &p1, - const RegionRec &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const RegionRec &p1, + const RegionRec &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -96,8 +96,8 @@ public: RegionModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, notifyOnAdd), - m_valueQuantization(0), + IntervalModel(sampleRate, resolution, notifyOnAdd), + m_valueQuantization(0), m_haveDistinctValues(false) { } @@ -105,10 +105,10 @@ RegionModel(sv_samplerate_t sampleRate, int resolution, float valueMinimum, float valueMaximum, bool notifyOnAdd = true) : - IntervalModel(sampleRate, resolution, + IntervalModel(sampleRate, resolution, valueMinimum, valueMaximum, notifyOnAdd), - m_valueQuantization(0), + m_valueQuantization(0), m_haveDistinctValues(false) { } @@ -132,10 +132,10 @@ << extraAttributes.toStdString() << std::endl; IntervalModel::toXml - (out, + (out, indent, - QString("%1 subtype=\"region\" valueQuantization=\"%2\"") - .arg(extraAttributes).arg(m_valueQuantization)); + QString("%1 subtype=\"region\" valueQuantization=\"%2\"") + .arg(extraAttributes).arg(m_valueQuantization)); } /** diff -r d4a28d1479a8 -r 710e6250a401 data/model/SparseModel.h --- a/data/model/SparseModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/SparseModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SPARSE_MODEL_H_ -#define _SPARSE_MODEL_H_ +#ifndef SV_SPARSE_MODEL_H +#define SV_SPARSE_MODEL_H #include "Model.h" #include "TabularModel.h" @@ -43,9 +43,16 @@ class SparseModel : public Model, public TabularModel { + // If we omit the Q_OBJECT macro, lupdate complains. + + // If we include it, moc fails (can't handle template classes). + + // If we omit it, lupdate still seems to emit translatable + // messages for the tr() strings in here. So I guess we omit it. + public: SparseModel(sv_samplerate_t sampleRate, int resolution, - bool notifyOnAdd = true); + bool notifyOnAdd = true); virtual ~SparseModel() { } virtual bool isOK() const { return true; } @@ -66,12 +73,12 @@ // Extend the end of the model. If this is set to something beyond // the end of the final point in the model, then getEndFrame() // will return this value. Otherwise getEndFrame() will return the - // end of the final point. + // end of the final point. (This is used by the Tony application) virtual void extendEndFrame(sv_frame_t to) { m_extendTo = to; } - + typedef PointType Point; typedef std::multiset PointList; + typename PointType::OrderComparator> PointList; typedef typename PointList::iterator PointListIterator; typedef typename PointList::const_iterator PointListConstIterator; @@ -151,6 +158,8 @@ virtual bool hasTextLabels() const { return m_hasTextLabels; } + virtual bool isSparse() const { return true; } + QString getTypeName() const { return tr("Sparse"); } virtual QString getXmlOutputType() const { return "sparse"; } @@ -168,7 +177,7 @@ DataExportOptions opts) const { return toDelimitedDataStringSubsetWithOptions (delimiter, opts, - std::min(getStartFrame(), sv_frame_t(0)), getEndFrame() + 1); + std::min(getStartFrame(), sv_frame_t(0)), getEndFrame()); } virtual QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const { @@ -196,23 +205,23 @@ class AddPointCommand : public Command { public: - AddPointCommand(SparseModel *model, - const PointType &point, + AddPointCommand(SparseModel *model, + const PointType &point, QString name = "") : - m_model(model), m_point(point), m_name(name) { } + m_model(model), m_point(point), m_name(name) { } - virtual QString getName() const { + virtual QString getName() const { return (m_name == "" ? tr("Add Point") : m_name); } - virtual void execute() { m_model->addPoint(m_point); } - virtual void unexecute() { m_model->deletePoint(m_point); } + virtual void execute() { m_model->addPoint(m_point); } + virtual void unexecute() { m_model->deletePoint(m_point); } - const PointType &getPoint() const { return m_point; } + const PointType &getPoint() const { return m_point; } private: - SparseModel *m_model; - PointType m_point; + SparseModel *m_model; + PointType m_point; QString m_name; }; @@ -223,20 +232,20 @@ class DeletePointCommand : public Command { public: - DeletePointCommand(SparseModel *model, - const PointType &point) : - m_model(model), m_point(point) { } + DeletePointCommand(SparseModel *model, + const PointType &point) : + m_model(model), m_point(point) { } - virtual QString getName() const { return tr("Delete Point"); } + virtual QString getName() const { return tr("Delete Point"); } - virtual void execute() { m_model->deletePoint(m_point); } - virtual void unexecute() { m_model->addPoint(m_point); } + virtual void execute() { m_model->deletePoint(m_point); } + virtual void unexecute() { m_model->addPoint(m_point); } - const PointType &getPoint() const { return m_point; } + const PointType &getPoint() const { return m_point; } private: - SparseModel *m_model; - PointType m_point; + SparseModel *m_model; + PointType m_point; }; @@ -247,27 +256,27 @@ class EditCommand : public MacroCommand { public: - EditCommand(SparseModel *model, QString commandName); + EditCommand(SparseModel *model, QString commandName); - virtual void addPoint(const PointType &point); - virtual void deletePoint(const PointType &point); + virtual void addPoint(const PointType &point); + virtual void deletePoint(const PointType &point); - /** - * Stack an arbitrary other command in the same sequence. - */ - virtual void addCommand(Command *command) { addCommand(command, true); } + /** + * Stack an arbitrary other command in the same sequence. + */ + virtual void addCommand(Command *command) { addCommand(command, true); } - /** - * If any points have been added or deleted, return this - * command (so the caller can add it to the command history). - * Otherwise delete the command and return NULL. - */ - virtual EditCommand *finish(); + /** + * If any points have been added or deleted, return this + * command (so the caller can add it to the command history). + * Otherwise delete the command and return NULL. + */ + virtual EditCommand *finish(); protected: - virtual void addCommand(Command *command, bool executeFirst); + virtual void addCommand(Command *command, bool executeFirst); - SparseModel *m_model; + SparseModel *m_model; }; @@ -277,27 +286,27 @@ class RelabelCommand : public Command { public: - RelabelCommand(SparseModel *model, - const PointType &point, - QString newLabel) : - m_model(model), m_oldPoint(point), m_newPoint(point) { - m_newPoint.label = newLabel; - } + RelabelCommand(SparseModel *model, + const PointType &point, + QString newLabel) : + m_model(model), m_oldPoint(point), m_newPoint(point) { + m_newPoint.label = newLabel; + } - virtual QString getName() const { return tr("Re-Label Point"); } + virtual QString getName() const { return tr("Re-Label Point"); } - virtual void execute() { - m_model->deletePoint(m_oldPoint); - m_model->addPoint(m_newPoint); - std::swap(m_oldPoint, m_newPoint); - } + virtual void execute() { + m_model->deletePoint(m_oldPoint); + m_model->addPoint(m_newPoint); + std::swap(m_oldPoint, m_newPoint); + } - virtual void unexecute() { execute(); } + virtual void unexecute() { execute(); } private: - SparseModel *m_model; - PointType m_oldPoint; - PointType m_newPoint; + SparseModel *m_model; + PointType m_oldPoint; + PointType m_newPoint; }; /** @@ -558,7 +567,7 @@ QMutexLocker locker(&m_mutex); sv_frame_t f = 0; if (!m_points.empty()) { - f = m_points.begin()->frame; + f = m_points.begin()->frame; } return f; } @@ -570,11 +579,14 @@ QMutexLocker locker(&m_mutex); sv_frame_t f = 0; if (!m_points.empty()) { - PointListConstIterator i(m_points.end()); - f = (--i)->frame; + PointListConstIterator i(m_points.end()); + f = (--i)->frame + 1; } - if (m_extendTo > f) return m_extendTo; - else return f; + if (m_extendTo > f) { + return m_extendTo; + } else { + return f; + } } template @@ -618,7 +630,7 @@ PointList rv; for (PointListConstIterator i = startItr; i != endItr; ++i) { - rv.insert(*i); + rv.insert(*i); } return rv; @@ -634,7 +646,7 @@ PointList rv; for (PointListConstIterator i = startItr; i != endItr; ++i) { - rv.insert(*i); + rv.insert(*i); } return rv; @@ -704,9 +716,9 @@ --i; sv_frame_t frame = i->frame; while (i->frame == frame) { - rv.insert(*i); - if (i == m_points.begin()) break; - --i; + rv.insert(*i); + if (i == m_points.begin()) break; + --i; } return rv; @@ -726,8 +738,8 @@ sv_frame_t frame = i->frame; while (i != m_points.end() && i->frame == frame) { - rv.insert(*i); - ++i; + rv.insert(*i); + ++i; } return rv; @@ -738,8 +750,8 @@ SparseModel::setResolution(int resolution) { { - QMutexLocker locker(&m_mutex); - m_resolution = resolution; + QMutexLocker locker(&m_mutex); + m_resolution = resolution; m_rows.clear(); } emit modelChanged(); @@ -750,8 +762,8 @@ SparseModel::clear() { { - QMutexLocker locker(&m_mutex); - m_points.clear(); + QMutexLocker locker(&m_mutex); + m_points.clear(); m_pointCount = 0; m_rows.clear(); } @@ -762,30 +774,35 @@ void SparseModel::addPoint(const PointType &point) { - QMutexLocker locker(&m_mutex); + { + QMutexLocker locker(&m_mutex); - m_points.insert(point); - m_pointCount++; - if (point.getLabel() != "") m_hasTextLabels = true; + m_points.insert(point); + m_pointCount++; + if (point.getLabel() != "") m_hasTextLabels = true; - // Even though this model is nominally sparse, there may still be - // too many signals going on here (especially as they'll probably - // be queued from one thread to another), which is why we need the - // notifyOnAdd as an option rather than a necessity (the - // alternative is to notify on setCompletion). + // Even though this model is nominally sparse, there may still + // be too many signals going on here (especially as they'll + // probably be queued from one thread to another), which is + // why we need the notifyOnAdd as an option rather than a + // necessity (the alternative is to notify on setCompletion). + + if (m_notifyOnAdd) { + m_rows.clear(); //!!! inefficient + } else { + if (m_sinceLastNotifyMin == -1 || + point.frame < m_sinceLastNotifyMin) { + m_sinceLastNotifyMin = point.frame; + } + if (m_sinceLastNotifyMax == -1 || + point.frame > m_sinceLastNotifyMax) { + m_sinceLastNotifyMax = point.frame; + } + } + } if (m_notifyOnAdd) { - m_rows.clear(); //!!! inefficient - emit modelChangedWithin(point.frame, point.frame + m_resolution); - } else { - if (m_sinceLastNotifyMin == -1 || - point.frame < m_sinceLastNotifyMin) { - m_sinceLastNotifyMin = point.frame; - } - if (m_sinceLastNotifyMax == -1 || - point.frame > m_sinceLastNotifyMax) { - m_sinceLastNotifyMax = point.frame; - } + emit modelChangedWithin(point.frame, point.frame + m_resolution); } } @@ -812,23 +829,26 @@ void SparseModel::deletePoint(const PointType &point) { - QMutexLocker locker(&m_mutex); + { + QMutexLocker locker(&m_mutex); - PointListIterator i = m_points.lower_bound(point); - typename PointType::Comparator comparator; - while (i != m_points.end()) { - if (i->frame > point.frame) break; - if (!comparator(*i, point) && !comparator(point, *i)) { - m_points.erase(i); - m_pointCount--; - break; - } - ++i; - } + PointListIterator i = m_points.lower_bound(point); + typename PointType::Comparator comparator; + while (i != m_points.end()) { + if (i->frame > point.frame) break; + if (!comparator(*i, point) && !comparator(point, *i)) { + m_points.erase(i); + m_pointCount--; + break; + } + ++i; + } // std::cout << "SparseOneDimensionalModel: emit modelChanged(" -// << point.frame << ")" << std::endl; - m_rows.clear(); //!!! inefficient +// << point.frame << ")" << std::endl; + m_rows.clear(); //!!! inefficient + } + emit modelChangedWithin(point.frame, point.frame + m_resolution); } @@ -837,36 +857,47 @@ SparseModel::setCompletion(int completion, bool update) { // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl; + bool emitCompletionChanged = true; + bool emitGeneralModelChanged = false; + bool emitRegionChanged = false; - QMutexLocker locker(&m_mutex); + { + QMutexLocker locker(&m_mutex); - if (m_completion != completion) { - m_completion = completion; + if (m_completion != completion) { + m_completion = completion; - if (completion == 100) { + if (completion == 100) { - if (!m_notifyOnAdd) { - emit completionChanged(); + if (m_notifyOnAdd) { + emitCompletionChanged = false; + } + + m_notifyOnAdd = true; // henceforth + m_rows.clear(); //!!! inefficient + emitGeneralModelChanged = true; + + } else if (!m_notifyOnAdd) { + + if (update && + m_sinceLastNotifyMin >= 0 && + m_sinceLastNotifyMax >= 0) { + m_rows.clear(); //!!! inefficient + emitRegionChanged = true; + } } + } + } - m_notifyOnAdd = true; // henceforth - m_rows.clear(); //!!! inefficient - emit modelChanged(); - - } else if (!m_notifyOnAdd) { - - if (update && - m_sinceLastNotifyMin >= 0 && - m_sinceLastNotifyMax >= 0) { - m_rows.clear(); //!!! inefficient - emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); - m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; - } else { - emit completionChanged(); - } - } else { - emit completionChanged(); - } + if (emitCompletionChanged) { + emit completionChanged(); + } + if (emitGeneralModelChanged) { + emit modelChanged(); + } + if (emitRegionChanged) { + emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax); + m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1; } } @@ -882,20 +913,20 @@ QString type = getXmlOutputType(); Model::toXml - (out, + (out, indent, - QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6") + QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6") .arg(type) - .arg(PointType(0).getDimensions()) - .arg(m_resolution) - .arg(m_notifyOnAdd ? "true" : "false") - .arg(getObjectExportId(&m_points)) - .arg(extraAttributes)); + .arg(PointType(0).getDimensions()) + .arg(m_resolution) + .arg(m_notifyOnAdd ? "true" : "false") + .arg(getObjectExportId(&m_points)) + .arg(extraAttributes)); out << indent; out << QString("\n") - .arg(getObjectExportId(&m_points)) - .arg(PointType(0).getDimensions()); + .arg(getObjectExportId(&m_points)) + .arg(PointType(0).getDimensions()); for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) { i->toXml(out, indent + " "); @@ -942,24 +973,24 @@ template void SparseModel::EditCommand::addCommand(Command *command, - bool executeFirst) + bool executeFirst) { if (executeFirst) command->execute(); if (!m_commands.empty()) { - DeletePointCommand *dpc = dynamic_cast(command); - if (dpc) { - AddPointCommand *apc = dynamic_cast - (m_commands[m_commands.size() - 1]); - typename PointType::Comparator comparator; - if (apc) { - if (!comparator(apc->getPoint(), dpc->getPoint()) && - !comparator(dpc->getPoint(), apc->getPoint())) { - deleteCommand(apc); - return; - } - } - } + DeletePointCommand *dpc = dynamic_cast(command); + if (dpc) { + AddPointCommand *apc = dynamic_cast + (m_commands[m_commands.size() - 1]); + typename PointType::Comparator comparator; + if (apc) { + if (!comparator(apc->getPoint(), dpc->getPoint()) && + !comparator(dpc->getPoint(), apc->getPoint())) { + deleteCommand(apc); + return; + } + } + } } MacroCommand::addCommand(command); diff -r d4a28d1479a8 -r 710e6250a401 data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/SparseOneDimensionalModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -41,7 +41,7 @@ QString extraAttributes = "") const { stream << QString("%1\n") - .arg(indent).arg(frame).arg(XmlExportable::encodeEntities(label)) + .arg(indent).arg(frame).arg(XmlExportable::encodeEntities(label)) .arg(extraAttributes); } @@ -54,18 +54,18 @@ } struct Comparator { - bool operator()(const OneDimensionalPoint &p1, - const OneDimensionalPoint &p2) const { - if (p1.frame != p2.frame) return p1.frame < p2.frame; - return p1.label < p2.label; - } + bool operator()(const OneDimensionalPoint &p1, + const OneDimensionalPoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + return p1.label < p2.label; + } }; struct OrderComparator { - bool operator()(const OneDimensionalPoint &p1, - const OneDimensionalPoint &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const OneDimensionalPoint &p1, + const OneDimensionalPoint &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -77,10 +77,10 @@ public: SparseOneDimensionalModel(sv_samplerate_t sampleRate, int resolution, - bool notifyOnAdd = true) : - SparseModel(sampleRate, resolution, notifyOnAdd) + bool notifyOnAdd = true) : + SparseModel(sampleRate, resolution, notifyOnAdd) { - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } virtual ~SparseOneDimensionalModel() @@ -97,14 +97,14 @@ int getIndexOf(const Point &point) { - // slow - int i = 0; - Point::Comparator comparator; - for (PointList::const_iterator j = m_points.begin(); - j != m_points.end(); ++j, ++i) { - if (!comparator(*j, point) && !comparator(point, *j)) return i; - } - return -1; + // slow + int i = 0; + Point::Comparator comparator; + for (PointList::const_iterator j = m_points.begin(); + j != m_points.end(); ++j, ++i) { + if (!comparator(*j, point) && !comparator(point, *j)) return i; + } + return -1; } QString getTypeName() const { return tr("Sparse 1-D"); } @@ -189,11 +189,11 @@ NoteList getNotesWithin(sv_frame_t startFrame, sv_frame_t endFrame) const { - PointList points = getPoints(startFrame, endFrame); + PointList points = getPoints(startFrame, endFrame); NoteList notes; - for (PointList::iterator pli = - points.begin(); pli != points.end(); ++pli) { + for (PointList::iterator pli = + points.begin(); pli != points.end(); ++pli) { notes.push_back (NoteData(pli->frame, diff -r d4a28d1479a8 -r 710e6250a401 data/model/SparseTimeValueModel.h --- a/data/model/SparseTimeValueModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/SparseTimeValueModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -31,7 +31,7 @@ public: TimeValuePoint(sv_frame_t _frame) : frame(_frame), value(0.0f) { } TimeValuePoint(sv_frame_t _frame, float _value, QString _label) : - frame(_frame), value(_value), label(_label) { } + frame(_frame), value(_value), label(_label) { } int getDimensions() const { return 2; } @@ -45,7 +45,7 @@ QString extraAttributes = "") const { stream << QString("%1\n") - .arg(indent).arg(frame).arg(value).arg(XmlExportable::encodeEntities(label)) + .arg(indent).arg(frame).arg(value).arg(XmlExportable::encodeEntities(label)) .arg(extraAttributes); } @@ -59,19 +59,19 @@ } struct Comparator { - bool operator()(const TimeValuePoint &p1, - const TimeValuePoint &p2) const { - if (p1.frame != p2.frame) return p1.frame < p2.frame; - if (p1.value != p2.value) return p1.value < p2.value; - return p1.label < p2.label; - } + bool operator()(const TimeValuePoint &p1, + const TimeValuePoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + if (p1.value != p2.value) return p1.value < p2.value; + return p1.label < p2.label; + } }; struct OrderComparator { - bool operator()(const TimeValuePoint &p1, - const TimeValuePoint &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const TimeValuePoint &p1, + const TimeValuePoint &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -82,30 +82,30 @@ public: SparseTimeValueModel(sv_samplerate_t sampleRate, int resolution, - bool notifyOnAdd = true) : - SparseValueModel(sampleRate, resolution, - notifyOnAdd) + bool notifyOnAdd = true) : + SparseValueModel(sampleRate, resolution, + notifyOnAdd) { // Model is playable, but may not sound (if units not Hz or // range unsuitable) - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } SparseTimeValueModel(sv_samplerate_t sampleRate, int resolution, - float valueMinimum, float valueMaximum, - bool notifyOnAdd = true) : - SparseValueModel(sampleRate, resolution, - valueMinimum, valueMaximum, - notifyOnAdd) + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + SparseValueModel(sampleRate, resolution, + valueMinimum, valueMaximum, + notifyOnAdd) { // Model is playable, but may not sound (if units not Hz or // range unsuitable) - PlayParameterRepository::getInstance()->addPlayable(this); + PlayParameterRepository::getInstance()->addPlayable(this); } virtual ~SparseTimeValueModel() { - PlayParameterRepository::getInstance()->removePlayable(this); + PlayParameterRepository::getInstance()->removePlayable(this); } QString getTypeName() const { return tr("Sparse Time-Value"); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/SparseValueModel.h --- a/data/model/SparseValueModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/SparseValueModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -32,19 +32,19 @@ { public: SparseValueModel(sv_samplerate_t sampleRate, int resolution, - bool notifyOnAdd = true) : - SparseModel(sampleRate, resolution, notifyOnAdd), - m_valueMinimum(0.f), - m_valueMaximum(0.f), + bool notifyOnAdd = true) : + SparseModel(sampleRate, resolution, notifyOnAdd), + m_valueMinimum(0.f), + m_valueMaximum(0.f), m_haveExtents(false) { } SparseValueModel(sv_samplerate_t sampleRate, int resolution, - float valueMinimum, float valueMaximum, - bool notifyOnAdd = true) : - SparseModel(sampleRate, resolution, notifyOnAdd), - m_valueMinimum(valueMinimum), - m_valueMaximum(valueMaximum), + float valueMinimum, float valueMaximum, + bool notifyOnAdd = true) : + SparseModel(sampleRate, resolution, notifyOnAdd), + m_valueMinimum(valueMinimum), + m_valueMaximum(valueMaximum), m_haveExtents(true) { } @@ -66,7 +66,7 @@ virtual void addPoint(const PointType &point) { - bool allChange = false; + bool allChange = false; if (!ISNAN(point.value) && !ISINF(point.value)) { if (!m_haveExtents || point.value < m_valueMinimum) { @@ -80,37 +80,37 @@ m_haveExtents = true; } - SparseModel::addPoint(point); - if (allChange) emit modelChanged(); + SparseModel::addPoint(point); + if (allChange) emit modelChanged(); } virtual void deletePoint(const PointType &point) { - SparseModel::deletePoint(point); + SparseModel::deletePoint(point); - if (point.value == m_valueMinimum || - point.value == m_valueMaximum) { + if (point.value == m_valueMinimum || + point.value == m_valueMaximum) { - float formerMin = m_valueMinimum, formerMax = m_valueMaximum; + float formerMin = m_valueMinimum, formerMax = m_valueMaximum; - for (typename SparseModel::PointList::const_iterator i - = m_points.begin(); - i != m_points.end(); ++i) { + for (typename SparseModel::PointList::const_iterator i + = m_points.begin(); + i != m_points.end(); ++i) { - if (i == m_points.begin() || i->value < m_valueMinimum) { - m_valueMinimum = i->value; + if (i == m_points.begin() || i->value < m_valueMinimum) { + m_valueMinimum = i->value; // std::cerr << "deletePoint: value min = " << m_valueMinimum << std::endl; - } - if (i == m_points.begin() || i->value > m_valueMaximum) { - m_valueMaximum = i->value; + } + if (i == m_points.begin() || i->value > m_valueMaximum) { + m_valueMaximum = i->value; // std::cerr << "deletePoint: value max = " << m_valueMaximum << std::endl; - } - } + } + } - if (formerMin != m_valueMinimum || formerMax != m_valueMaximum) { - emit modelChanged(); - } - } + if (formerMin != m_valueMinimum || formerMax != m_valueMaximum) { + emit modelChanged(); + } + } } virtual void toXml(QTextStream &stream, @@ -120,11 +120,11 @@ std::cerr << "SparseValueModel::toXml: extraAttributes = \"" << extraAttributes.toStdString() << std::endl; - SparseModel::toXml - (stream, + SparseModel::toXml + (stream, indent, - QString("%1 minimum=\"%2\" maximum=\"%3\" units=\"%4\"") - .arg(extraAttributes).arg(m_valueMinimum).arg(m_valueMaximum) + QString("%1 minimum=\"%2\" maximum=\"%3\" units=\"%4\"") + .arg(extraAttributes).arg(m_valueMinimum).arg(m_valueMaximum) .arg(this->encodeEntities(m_units))); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/TextModel.h --- a/data/model/TextModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/TextModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -33,7 +33,7 @@ public: TextPoint(sv_frame_t _frame) : frame(_frame), height(0.0f) { } TextPoint(sv_frame_t _frame, float _height, QString _label) : - frame(_frame), height(_height), label(_label) { } + frame(_frame), height(_height), label(_label) { } int getDimensions() const { return 2; } @@ -46,8 +46,8 @@ void toXml(QTextStream &stream, QString indent = "", QString extraAttributes = "") const { - stream << QString("%1\n") - .arg(indent).arg(frame).arg(height) + stream << QString("%1\n") + .arg(indent).arg(frame).arg(height) .arg(encodeEntities(label)).arg(extraAttributes); } @@ -61,19 +61,19 @@ } struct Comparator { - bool operator()(const TextPoint &p1, - const TextPoint &p2) const { - if (p1.frame != p2.frame) return p1.frame < p2.frame; - if (p1.height != p2.height) return p1.height < p2.height; - return p1.label < p2.label; - } + bool operator()(const TextPoint &p1, + const TextPoint &p2) const { + if (p1.frame != p2.frame) return p1.frame < p2.frame; + if (p1.height != p2.height) return p1.height < p2.height; + return p1.label < p2.label; + } }; struct OrderComparator { - bool operator()(const TextPoint &p1, - const TextPoint &p2) const { - return p1.frame < p2.frame; - } + bool operator()(const TextPoint &p1, + const TextPoint &p2) const { + return p1.frame < p2.frame; + } }; }; @@ -86,7 +86,7 @@ public: TextModel(sv_samplerate_t sampleRate, int resolution, bool notifyOnAdd = true) : - SparseModel(sampleRate, resolution, notifyOnAdd) + SparseModel(sampleRate, resolution, notifyOnAdd) { } virtual void toXml(QTextStream &out, @@ -94,10 +94,10 @@ QString extraAttributes = "") const { SparseModel::toXml - (out, + (out, indent, - QString("%1 subtype=\"text\"") - .arg(extraAttributes)); + QString("%1 subtype=\"text\"") + .arg(extraAttributes)); } QString getTypeName() const { return tr("Text"); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/WritableWaveFileModel.cpp --- a/data/model/WritableWaveFileModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/WritableWaveFileModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -36,57 +36,121 @@ //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1 -WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, - int channels, - QString path) : +WritableWaveFileModel::WritableWaveFileModel(QString path, + sv_samplerate_t sampleRate, + int channels, + Normalisation norm) : m_model(0), - m_writer(0), + m_temporaryWriter(0), + m_targetWriter(0), m_reader(0), + m_normalisation(norm), m_sampleRate(sampleRate), m_channels(channels), m_frameCount(0), m_startFrame(0), m_proportion(PROPORTION_UNKNOWN) { + init(path); +} + +WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, + int channels, + Normalisation norm) : + m_model(0), + m_temporaryWriter(0), + m_targetWriter(0), + m_reader(0), + m_normalisation(norm), + m_sampleRate(sampleRate), + m_channels(channels), + m_frameCount(0), + m_startFrame(0), + m_proportion(PROPORTION_UNKNOWN) +{ + init(); +} + +WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate, + int channels) : + m_model(0), + m_temporaryWriter(0), + m_targetWriter(0), + m_reader(0), + m_normalisation(Normalisation::None), + m_sampleRate(sampleRate), + m_channels(channels), + m_frameCount(0), + m_startFrame(0), + m_proportion(PROPORTION_UNKNOWN) +{ + init(); +} + +void +WritableWaveFileModel::init(QString path) +{ if (path.isEmpty()) { try { + // Temp dir is exclusive to this run of the application, + // so the filename only needs to be unique within that - + // model ID should be ok QDir dir(TempDirectory::getInstance()->getPath()); - path = dir.filePath(QString("written_%1.wav") - .arg((intptr_t)this)); - } catch (DirectoryCreationFailed f) { - cerr << "WritableWaveFileModel: Failed to create temporary directory" << endl; + path = dir.filePath(QString("written_%1.wav").arg(getId())); + } catch (const DirectoryCreationFailed &f) { + SVCERR << "WritableWaveFileModel: Failed to create temporary directory" << endl; return; } } - // Write directly to the target file, so that we can do - // incremental writes and concurrent reads - m_writer = new WavFileWriter(path, sampleRate, channels, - WavFileWriter::WriteToTarget); - if (!m_writer->isOK()) { - cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError() << endl; - delete m_writer; - m_writer = 0; + m_targetPath = path; + m_temporaryPath = ""; + + // We don't delete or null-out writer/reader members after + // failures here - they are all deleted in the dtor, and the + // presence/existence of the model is what's used to determine + // whether to go ahead, not the writer/readers. If the model is + // non-null, then the necessary writer/readers must be OK, as the + // model is the last thing initialised + + m_targetWriter = new WavFileWriter(m_targetPath, m_sampleRate, m_channels, + WavFileWriter::WriteToTarget); + + if (!m_targetWriter->isOK()) { + SVCERR << "WritableWaveFileModel: Error in creating WAV file writer: " << m_targetWriter->getError() << endl; return; } + + if (m_normalisation != Normalisation::None) { - FileSource source(m_writer->getPath()); + // Temp dir is exclusive to this run of the application, so + // the filename only needs to be unique within that + QDir dir(TempDirectory::getInstance()->getPath()); + m_temporaryPath = dir.filePath(QString("prenorm_%1.wav").arg(getId())); + + m_temporaryWriter = new WavFileWriter + (m_temporaryPath, m_sampleRate, m_channels, + WavFileWriter::WriteToTarget); + + if (!m_temporaryWriter->isOK()) { + SVCERR << "WritableWaveFileModel: Error in creating temporary WAV file writer: " << m_temporaryWriter->getError() << endl; + return; + } + } + + FileSource source(m_targetPath); m_reader = new WavFileReader(source, true); if (!m_reader->getError().isEmpty()) { - cerr << "WritableWaveFileModel: Error in creating wave file reader" << endl; - delete m_reader; - m_reader = 0; + SVCERR << "WritableWaveFileModel: Error in creating wave file reader: " << m_reader->getError() << endl; return; } m_model = new ReadOnlyWaveFileModel(source, m_reader); if (!m_model->isOK()) { - cerr << "WritableWaveFileModel: Error in creating wave file model" << endl; + SVCERR << "WritableWaveFileModel: Error in creating wave file model" << endl; delete m_model; m_model = 0; - delete m_reader; - m_reader = 0; return; } m_model->setStartFrame(m_startFrame); @@ -99,7 +163,8 @@ WritableWaveFileModel::~WritableWaveFileModel() { delete m_model; - delete m_writer; + delete m_targetWriter; + delete m_temporaryWriter; delete m_reader; } @@ -107,49 +172,53 @@ WritableWaveFileModel::setStartFrame(sv_frame_t startFrame) { m_startFrame = startFrame; - if (m_model) m_model->setStartFrame(startFrame); + if (m_model) { + m_model->setStartFrame(startFrame); + } } bool -WritableWaveFileModel::addSamples(float **samples, sv_frame_t count) +WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t count) { - if (!m_writer) return false; + if (!m_model) return false; #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl; #endif - if (!m_writer->writeSamples(samples, count)) { - cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError() << endl; + WavFileWriter *writer = m_targetWriter; + if (m_normalisation != Normalisation::None) { + writer = m_temporaryWriter; + } + + if (!writer->writeSamples(samples, count)) { + SVCERR << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << writer->getError() << endl; return false; } m_frameCount += count; - static int updateCounter = 0; - - if (m_reader && m_reader->getChannelCount() == 0) { -#ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL - SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (initial)" << endl; -#endif - m_reader->updateFrameCount(); - } else if (++updateCounter == 100) { -#ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL - SVDEBUG << "WritableWaveFileModel::addSamples(" << count << "): calling updateFrameCount (periodic)" << endl; -#endif - if (m_reader) m_reader->updateFrameCount(); - updateCounter = 0; + if (m_normalisation == Normalisation::None) { + if (m_reader->getChannelCount() == 0) { + m_reader->updateFrameCount(); + } } return true; } +void +WritableWaveFileModel::updateModel() +{ + if (!m_model) return; + + m_reader->updateFrameCount(); +} + bool WritableWaveFileModel::isOK() const { - bool ok = (m_writer && m_writer->isOK()); -// SVDEBUG << "WritableWaveFileModel::isOK(): ok = " << ok << endl; - return ok; + return (m_model && m_model->isOK()); } bool @@ -176,11 +245,56 @@ void WritableWaveFileModel::writeComplete() { - if (m_reader) m_reader->updateDone(); + if (!m_model) return; + + if (m_normalisation == Normalisation::None) { + m_targetWriter->close(); + } else { + m_temporaryWriter->close(); + normaliseToTarget(); + } + + m_reader->updateDone(); m_proportion = 100; emit modelChanged(); } +void +WritableWaveFileModel::normaliseToTarget() +{ + if (m_temporaryPath == "") { + SVCERR << "WritableWaveFileModel::normaliseToTarget: No temporary path available" << endl; + return; + } + + WavFileReader normalisingReader(m_temporaryPath, false, + WavFileReader::Normalisation::Peak); + + if (!normalisingReader.getError().isEmpty()) { + SVCERR << "WritableWaveFileModel: Error in creating normalising reader: " << normalisingReader.getError() << endl; + return; + } + + sv_frame_t frame = 0; + sv_frame_t block = 65536; + sv_frame_t count = normalisingReader.getFrameCount(); + + while (frame < count) { + auto frames = normalisingReader.getInterleavedFrames(frame, block); + if (!m_targetWriter->putInterleavedFrames(frames)) { + SVCERR << "ERROR: WritableWaveFileModel::normaliseToTarget: writer failed: " << m_targetWriter->getError() << endl; + return; + } + frame += block; + } + + m_targetWriter->close(); + + delete m_temporaryWriter; + m_temporaryWriter = 0; + QFile::remove(m_temporaryPath); +} + sv_frame_t WritableWaveFileModel::getFrameCount() const { @@ -188,14 +302,14 @@ return m_frameCount; } -vector +floatvec_t WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const { if (!m_model || m_model->getChannelCount() == 0) return {}; return m_model->getData(channel, start, count); } -vector> +vector WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const { @@ -241,7 +355,7 @@ Model::toXml (out, indent, QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2") - .arg(encodeEntities(m_writer->getPath())) + .arg(encodeEntities(m_targetPath)) .arg(extraAttributes)); } diff -r d4a28d1479a8 -r 710e6250a401 data/model/WritableWaveFileModel.h --- a/data/model/WritableWaveFileModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/WritableWaveFileModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -28,18 +28,88 @@ Q_OBJECT public: - WritableWaveFileModel(sv_samplerate_t sampleRate, int channels, QString path = ""); + enum class Normalisation { None, Peak }; + + /** + * Create a WritableWaveFileModel of the given sample rate and + * channel count, storing data in a new float-type extended WAV + * file with the given path. If path is the empty string, the data + * will be stored in a newly-created temporary file. + * + * If normalisation == None, sample values will be written + * verbatim, and will be ready to read as soon as they have been + * written. Otherwise samples will be normalised on writing; this + * will require an additional pass and temporary file, and no + * samples will be available to read until after writeComplete() + * has returned. + */ + WritableWaveFileModel(QString path, + sv_samplerate_t sampleRate, + int channels, + Normalisation normalisation); + + /** + * Create a WritableWaveFileModel of the given sample rate and + * channel count, storing data in a new float-type extended WAV + * file in a temporary location. This is equivalent to passing an + * empty path to the constructor above. + * + * If normalisation == None, sample values will be written + * verbatim, and will be ready to read as soon as they have been + * written. Otherwise samples will be normalised on writing; this + * will require an additional pass and temporary file, and no + * samples will be available to read until after writeComplete() + * has returned. + */ + WritableWaveFileModel(sv_samplerate_t sampleRate, + int channels, + Normalisation normalisation); + + /** + * Create a WritableWaveFileModel of the given sample rate and + * channel count, storing data in a new float-type extended WAV + * file in a temporary location, and applying no normalisation. + * + * This is equivalent to passing an empty path and + * Normalisation::None to the first constructor above. + */ + WritableWaveFileModel(sv_samplerate_t sampleRate, + int channels); + ~WritableWaveFileModel(); /** * Call addSamples to append a block of samples to the end of the - * file. Caller should also call setWriteProportion() to update - * the progress of this file, if it has a known end point, and - * should call writeComplete() when the file has been written. + * file. + * + * This function only appends the samples to the file being + * written; it does not update the model's view of the samples in + * that file. That is, it updates the file on disc but the model + * itself does not change its content. This is because re-reading + * the file to update the model may be more expensive than adding + * the samples in the first place. If you are writing small + * numbers of samples repeatedly, you probably only want the model + * to update periodically rather than after every write. + * + * Call updateModel() periodically to tell the model to update its + * own view of the samples in the file being written. + * + * Call setWriteProportion() periodically if the file being + * written has known duration and you want the model to be able to + * report the write progress as a percentage. + * + * Call writeComplete() when the file has been completely written. */ - virtual bool addSamples(float **samples, sv_frame_t count); + virtual bool addSamples(const float *const *samples, sv_frame_t count); /** + * Tell the model to update its own (read) view of the (written) + * file. May cause modelChanged() and modelChangedWithin() to be + * emitted. See the comment to addSamples above for rationale. + */ + void updateModel(); + + /** * Set the proportion of the file which has been written so far, * as a percentage. This may be used to indicate progress. * @@ -56,7 +126,7 @@ /** * Indicate that writing is complete. You should call this even if - * you have never called setWriteProportion(). + * you have never called setWriteProportion() or updateModel(). */ void writeComplete(); @@ -110,9 +180,9 @@ void setStartFrame(sv_frame_t startFrame); - virtual std::vector getData(int channel, sv_frame_t start, sv_frame_t count) const; + virtual floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const; - virtual std::vector> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; + virtual std::vector getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; virtual int getSummaryBlockSize(int desired) const; @@ -129,13 +199,35 @@ protected: ReadOnlyWaveFileModel *m_model; - WavFileWriter *m_writer; + + /** When normalising, this writer is used to write verbatim + * samples to the temporary file prior to + * normalisation. Otherwise it's null + */ + WavFileWriter *m_temporaryWriter; + QString m_temporaryPath; + + /** When not normalising, this writer is used to write verbatim + * samples direct to the target file. When normalising, it is + * used to write normalised samples to the target after the + * temporary file has been completed. But it is still created on + * initialisation, so that there is a file header ready for the + * reader to address. + */ + WavFileWriter *m_targetWriter; + QString m_targetPath; + WavFileReader *m_reader; + Normalisation m_normalisation; sv_samplerate_t m_sampleRate; int m_channels; sv_frame_t m_frameCount; sv_frame_t m_startFrame; int m_proportion; + +private: + void init(QString path = ""); + void normaliseToTarget(); }; #endif diff -r d4a28d1479a8 -r 710e6250a401 data/model/test/Compares.h --- a/data/model/test/Compares.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/test/Compares.h Mon Sep 17 13:51:14 2018 +0100 @@ -24,12 +24,12 @@ COMPARE_FUZZIER(a[cmp_i], n); \ } -#define COMPARE_ALL(a, b) \ +#define COMPARE_ALL(a, b) \ for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ COMPARE_FUZZIER(a[cmp_i], b[cmp_i]); \ } -#define COMPARE_SCALED(a, b, s) \ +#define COMPARE_SCALED(a, b, s) \ for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ COMPARE_FUZZIER(a[cmp_i] / s, b[cmp_i]); \ } @@ -39,12 +39,12 @@ COMPARE_FUZZIER_F(a[cmp_i], n); \ } -#define COMPARE_ALL_F(a, b) \ +#define COMPARE_ALL_F(a, b) \ for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ COMPARE_FUZZIER_F(a[cmp_i], b[cmp_i]); \ } -#define COMPARE_SCALED_F(a, b, s) \ +#define COMPARE_SCALED_F(a, b, s) \ for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \ COMPARE_FUZZIER_F(a[cmp_i] / s, b[cmp_i]); \ } diff -r d4a28d1479a8 -r 710e6250a401 data/model/test/MockWaveModel.cpp --- a/data/model/test/MockWaveModel.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/test/MockWaveModel.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -22,25 +22,25 @@ MockWaveModel::MockWaveModel(vector sorts, int length, int pad) { for (auto sort: sorts) { - m_data.push_back(generate(sort, length, pad)); + m_data.push_back(generate(sort, length, pad)); } } -vector +floatvec_t MockWaveModel::getData(int channel, sv_frame_t start, sv_frame_t count) const { sv_frame_t i = 0; // cerr << "MockWaveModel::getData(" << channel << "," << start << "," << count << "): "; - vector data; + floatvec_t data; while (i < count) { - sv_frame_t idx = start + i; - if (!in_range_for(m_data[channel], idx)) break; - data.push_back(m_data[channel][idx]); -// cerr << data[i] << " "; - ++i; + sv_frame_t idx = start + i; + if (!in_range_for(m_data[channel], idx)) break; + data.push_back(m_data[channel][idx]); +// cerr << data[i] << " "; + ++i; } // cerr << endl; @@ -48,14 +48,14 @@ return data; } -vector> +vector MockWaveModel::getMultiChannelData(int fromchannel, int tochannel, - sv_frame_t start, sv_frame_t count) const + sv_frame_t start, sv_frame_t count) const { - vector> data(tochannel - fromchannel + 1); + vector data(tochannel - fromchannel + 1); for (int c = fromchannel; c <= tochannel; ++c) { - data.push_back(getData(c, start, count)); + data[c] = getData(c, start, count); } return data; @@ -72,17 +72,17 @@ for (int i = 0; i < length; ++i) { - double v = 0.0; - - switch (sort) { - case DC: v = 1.0; break; - case Sine: v = sin((2.0 * M_PI / 8.0) * i); break; - case Cosine: v = cos((2.0 * M_PI / 8.0) * i); break; - case Nyquist: v = (i % 2) * 2 - 1; break; - case Dirac: v = (i == 0) ? 1.0 : 0.0; break; - } + double v = 0.0; + + switch (sort) { + case DC: v = 1.0; break; + case Sine: v = sin((2.0 * M_PI / 8.0) * i); break; + case Cosine: v = cos((2.0 * M_PI / 8.0) * i); break; + case Nyquist: v = (i % 2) * 2 - 1; break; + case Dirac: v = (i == 0) ? 1.0 : 0.0; break; + } - data.push_back(float(v)); + data.push_back(float(v)); } for (int i = 0; i < pad; ++i) { diff -r d4a28d1479a8 -r 710e6250a401 data/model/test/MockWaveModel.h --- a/data/model/test/MockWaveModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/test/MockWaveModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -41,8 +41,8 @@ virtual float getValueMaximum() const { return 1.f; } virtual int getChannelCount() const { return int(m_data.size()); } - virtual std::vector getData(int channel, sv_frame_t start, sv_frame_t count) const; - virtual std::vector> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; + virtual floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const; + virtual std::vector getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const; virtual bool canPlay() const { return true; } virtual QString getDefaultPlayClipId() const { return ""; } diff -r d4a28d1479a8 -r 710e6250a401 data/model/test/TestFFTModel.h --- a/data/model/test/TestFFTModel.h Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/test/TestFFTModel.h Mon Sep 17 13:51:14 2018 +0100 @@ -66,18 +66,18 @@ float thresh = 1e-5f; if (abs(reals[i] - eRe) > thresh || abs(imags[i] - eIm) > thresh) { - cerr << "ERROR: output is not as expected for column " + SVCERR << "ERROR: output is not as expected for column " << i << " in channel " << ch << " (stepThrough = " << stepThrough << ")" << endl; - cerr << "expected : "; + SVCERR << "expected : "; for (int j = 0; j < hs1; ++j) { - cerr << expectedValues[ch][j] << " "; + SVCERR << expectedValues[ch][j] << " "; } - cerr << "\nactual : "; + SVCERR << "\nactual : "; for (int j = 0; j < hs1; ++j) { - cerr << complex(reals[j], imags[j]) << " "; + SVCERR << complex(reals[j], imags[j]) << " "; } - cerr << endl; + SVCERR << endl; } COMPARE_FUZZIER_F(reals[i], eRe); COMPARE_FUZZIER_F(imags[i], eIm); @@ -101,7 +101,7 @@ // are those of our expected signal. void dc_simple_rect() { - MockWaveModel mwm({ DC }, 16, 4); + MockWaveModel mwm({ DC }, 16, 4); test(&mwm, RectangularWindow, 8, 8, 8, 0, { { {}, {}, {}, {}, {} } }, 4); test(&mwm, RectangularWindow, 8, 8, 8, 1, @@ -115,7 +115,7 @@ void dc_simple_hann() { // The Hann window function is a simple sinusoid with period // equal to twice the window size, and it halves the DC energy - MockWaveModel mwm({ DC }, 16, 4); + MockWaveModel mwm({ DC }, 16, 4); test(&mwm, HanningWindow, 8, 8, 8, 0, { { {}, {}, {}, {}, {} } }, 4); test(&mwm, HanningWindow, 8, 8, 8, 1, @@ -127,7 +127,7 @@ } void dc_simple_hann_halfoverlap() { - MockWaveModel mwm({ DC }, 16, 4); + MockWaveModel mwm({ DC }, 16, 4); test(&mwm, HanningWindow, 8, 4, 8, 0, { { {}, {}, {}, {}, {} } }, 7); test(&mwm, HanningWindow, 8, 4, 8, 2, @@ -139,7 +139,7 @@ } void sine_simple_rect() { - MockWaveModel mwm({ Sine }, 16, 4); + MockWaveModel mwm({ Sine }, 16, 4); // Sine: output is purely imaginary. Note the sign is flipped // (normally the first half of the output would have negative // sign for a sine starting at 0) because the model does an @@ -155,7 +155,7 @@ } void cosine_simple_rect() { - MockWaveModel mwm({ Cosine }, 16, 4); + MockWaveModel mwm({ Cosine }, 16, 4); // Cosine: output is purely real. Note the sign is flipped // because the model does an FFT shift to centre the phase test(&mwm, RectangularWindow, 8, 8, 8, 0, @@ -169,7 +169,7 @@ } void twochan_simple_rect() { - MockWaveModel mwm({ Sine, Cosine }, 16, 4); + MockWaveModel mwm({ Sine, Cosine }, 16, 4); // Test that the two channels are read and converted separately test(&mwm, RectangularWindow, 8, 8, 8, 0, { @@ -194,7 +194,7 @@ } void nyquist_simple_rect() { - MockWaveModel mwm({ Nyquist }, 16, 4); + MockWaveModel mwm({ Nyquist }, 16, 4); // Again, the sign is flipped. This has the same amount of // energy as the DC example test(&mwm, RectangularWindow, 8, 8, 8, 0, @@ -208,7 +208,7 @@ } void dirac_simple_rect() { - MockWaveModel mwm({ Dirac }, 16, 4); + MockWaveModel mwm({ Dirac }, 16, 4); // The window scales by 0.5 and some signs are flipped. Only // column 1 has any data (the single impulse). test(&mwm, RectangularWindow, 8, 8, 8, 0, @@ -222,7 +222,7 @@ } void dirac_simple_rect_2() { - MockWaveModel mwm({ Dirac }, 16, 8); + MockWaveModel mwm({ Dirac }, 16, 8); // With 8 samples padding, the FFT shift places the first // Dirac impulse at the start of column 1, thus giving all // positive values @@ -239,7 +239,7 @@ } void dirac_simple_rect_halfoverlap() { - MockWaveModel mwm({ Dirac }, 16, 4); + MockWaveModel mwm({ Dirac }, 16, 4); test(&mwm, RectangularWindow, 8, 4, 8, 0, { { {}, {}, {}, {}, {} } }, 7); test(&mwm, RectangularWindow, 8, 4, 8, 1, diff -r d4a28d1479a8 -r 710e6250a401 data/model/test/svcore-data-model-test.cpp --- a/data/model/test/svcore-data-model-test.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/model/test/svcore-data-model-test.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -24,21 +24,21 @@ int good = 0, bad = 0; QCoreApplication app(argc, argv); - app.setOrganizationName("Sonic Visualiser"); + app.setOrganizationName("sonic-visualiser"); app.setApplicationName("test-model"); { - TestFFTModel t; - if (QTest::qExec(&t, argc, argv) == 0) ++good; - else ++bad; + TestFFTModel t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; } if (bad > 0) { - cerr << "\n********* " << bad << " test suite(s) failed!\n" << endl; - return 1; + SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; + return 1; } else { - cerr << "All tests passed" << endl; - return 0; + SVCERR << "All tests passed" << endl; + return 0; } } diff -r d4a28d1479a8 -r 710e6250a401 data/osc/OSCQueue.cpp --- a/data/osc/OSCQueue.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/data/osc/OSCQueue.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -34,7 +34,7 @@ OSCQueue::oscError(int num, const char *msg, const char *path) { cerr << "ERROR: OSCQueue::oscError: liblo server error " << num - << " in path " << path << ": " << msg << endl; + << " in path " << path << ": " << msg << endl; } int @@ -48,7 +48,7 @@ QString method; if (!queue->parseOSCPath(path, target, targetData, method)) { - return 1; + return 1; } OSCMessage message; @@ -80,7 +80,7 @@ break; } - ++i; + ++i; } queue->postMessage(message); @@ -190,7 +190,7 @@ QString &method) { while (path.startsWith("/")) { - path = path.right(path.length()-1); + path = path.right(path.length()-1); } int i = 0; diff -r d4a28d1479a8 -r 710e6250a401 data/osc/demoscript.sh --- a/data/osc/demoscript.sh Mon Dec 12 15:18:52 2016 +0000 +++ b/data/osc/demoscript.sh Mon Sep 17 13:51:14 2018 +0100 @@ -1,15 +1,16 @@ #!/bin/bash -audio=/share/music +audio=/data/Music preferred=$audio/free list=audiofiles.txt used=audiofiles-used.txt -df=vamp:vamp-aubio:aubioonset:detectionfunction -#df=vamp:qm-vamp-plugins:qm-tempotracker:detection_fn -onsets=vamp:vamp-aubio:aubioonset:onsets -#onsets=vamp:qm-vamp-plugins:qm-tempotracker:beats -beats=vamp:vamp-aubio:aubiotempo:beats +#df=vamp:vamp-aubio:aubioonset:detectionfunction +df=vamp:qm-vamp-plugins:qm-tempotracker:detection_fn +#onsets=vamp:vamp-aubio:aubioonset:onsets +onsets=vamp:vamp-example-plugins:percussiononsets:onsets +beats=vamp:qm-vamp-plugins:qm-tempotracker:beats +#beats=vamp:vamp-aubio:aubiotempo:beats #beats=$onsets #onsets=$beats chromagram=vamp:qm-vamp-plugins:qm-chromagram:chromagram @@ -49,17 +50,27 @@ echo "$file" } +resize_normal() { +# sv-command resize 1000 500 + sv-command resize 2000 1000 +} + +resize_big() { +# sv-command resize 1000 700 + sv-command resize 2000 1400 +} + load_a_file() { file=`pick_file` if ! sv-command open "$file"; then pid="`pidof sonic-visualiser`" if [ -z "$pid" ]; then - ( setsid sonic-visualiser -geometry 1000x500+10+100 & ) + ( setsid sonic-visualiser -geometry +10+100 & ) sleep 2 #sudo renice +19 `pidof sonic-visualiser` #sudo renice +18 `pidof Xorg` - sv-command resize 1000 500 + resize_normal load_a_file else echo "ERROR: Unable to contact sonic-visualiser pid $pid" 1>&2 @@ -122,7 +133,7 @@ fade_in() { sv-command set gain 0 - sleep 0.5 + sleep 1 play "$@" for gain in 0.001 0.01 0.05 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1; do sv-command set gain $gain @@ -146,8 +157,8 @@ # sv-command set speedup "$speed" # sleep 1 # done - for speed in -20 -100 -1000; do - sv-command set speedup "$speed" + for speed in 80 50 10; do + sv-command set speed "$speed" sleep 10 done } @@ -155,7 +166,7 @@ stop() { sv-command stop "$@" - sv-command set speedup 0 + sv-command set speed 100 } quit() @@ -285,7 +296,7 @@ sv-command select 7.5 11 fade_in selection sleep 10 - sv-command set speedup -200 + sv-command set speed 40 sleep 10 sv-command setcurrent 1 sv-command delete pane @@ -294,7 +305,7 @@ sv-command set layer Normalize-Columns off sv-command set layer Normalize-Visible-Area on sleep 20 - sv-command set speedup 0 + sv-command set speed 100 sleep 10 sv-command select none # fade_out @@ -420,7 +431,7 @@ # reset sv-command set overlays 1 sv-command set zoomwheels 0 - sv-command resize 1000 500 + resize_normal sv-command zoom default sv-command setcurrent 2 sv-command delete pane @@ -466,13 +477,13 @@ sleep 4 sv-command delete layer sleep 16 - sv-command set speedup -50 + sv-command set speed 66 sleep 14 - sv-command set speedup 50 + sv-command set speed 150 sleep 8 - sv-command set speedup 100 + sv-command set speed 200 sleep 5 - sv-command set speedup 200 + sv-command set speed 400 fade_out # sleep 10 sv-command select none @@ -507,14 +518,14 @@ load_a_file sv-command loop on -sv-command resize 1000 500 +resize_normal show_stuff sleep 5 sleep 20 playback_bits #sleep 10 -sv-command resize 1000 700 +resize_big sv-command zoom default show_stuff onset_bits @@ -524,7 +535,7 @@ #sv-command resize 1000 700 #sleep 10 -sv-command resize 1000 700 +resize_big #show_stuff spectrogram_bits diff -r d4a28d1479a8 -r 710e6250a401 files.pri --- a/files.pri Mon Dec 12 15:18:52 2016 +0000 +++ b/files.pri Mon Sep 17 13:51:14 2018 +0100 @@ -1,6 +1,7 @@ SVCORE_HEADERS = \ base/AudioLevel.h \ base/AudioPlaySource.h \ + base/AudioRecordTarget.h \ base/BaseTypes.h \ base/Clipboard.h \ base/ColumnOp.h \ @@ -23,8 +24,10 @@ base/RangeMapper.h \ base/RealTime.h \ base/RecentFiles.h \ + base/RecordDirectory.h \ base/ResourceFinder.h \ base/RingBuffer.h \ + base/ScaleTickIntervals.h \ base/Scavenger.h \ base/Selection.h \ base/Serialiser.h \ @@ -49,6 +52,7 @@ data/fileio/CSVFileReader.h \ data/fileio/CSVFileWriter.h \ data/fileio/CSVFormat.h \ + data/fileio/CSVStreamWriter.h \ data/fileio/DataFileReader.h \ data/fileio/DataFileReaderFactory.h \ data/fileio/FileFinder.h \ @@ -59,14 +63,12 @@ data/fileio/MP3FileReader.h \ data/fileio/OggVorbisFileReader.h \ data/fileio/PlaylistFileReader.h \ - data/fileio/QuickTimeFileReader.h \ data/fileio/CoreAudioFileReader.h \ data/fileio/DecodingWavFileReader.h \ data/fileio/WavFileReader.h \ data/fileio/WavFileWriter.h \ data/midi/MIDIEvent.h \ data/midi/MIDIInput.h \ - data/midi/rtmidi/RtError.h \ data/midi/rtmidi/RtMidi.h \ data/model/AggregateWaveModel.h \ data/model/AlignmentModel.h \ @@ -107,6 +109,7 @@ plugin/NativeVampPluginFactory.h \ plugin/PiperVampPluginFactory.h \ plugin/PluginIdentifier.h \ + plugin/PluginPathSetter.h \ plugin/PluginXml.h \ plugin/RealTimePluginFactory.h \ plugin/RealTimePluginInstance.h \ @@ -158,6 +161,7 @@ base/RangeMapper.cpp \ base/RealTimeSV.cpp \ base/RecentFiles.cpp \ + base/RecordDirectory.cpp \ base/ResourceFinder.cpp \ base/Selection.cpp \ base/Serialiser.cpp \ @@ -188,7 +192,6 @@ data/fileio/MP3FileReader.cpp \ data/fileio/OggVorbisFileReader.cpp \ data/fileio/PlaylistFileReader.cpp \ - data/fileio/QuickTimeFileReader.cpp \ data/fileio/CoreAudioFileReader.cpp \ data/fileio/DecodingWavFileReader.cpp \ data/fileio/WavFileReader.cpp \ @@ -220,6 +223,7 @@ plugin/NativeVampPluginFactory.cpp \ plugin/PiperVampPluginFactory.cpp \ plugin/PluginIdentifier.cpp \ + plugin/PluginPathSetter.cpp \ plugin/PluginXml.cpp \ plugin/RealTimePluginFactory.cpp \ plugin/RealTimePluginInstance.cpp \ diff -r d4a28d1479a8 -r 710e6250a401 plugin/DSSIPluginFactory.cpp --- a/plugin/DSSIPluginFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/DSSIPluginFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -39,6 +39,7 @@ #include "lrdf.h" #endif // HAVE_LRDF +using std::string; DSSIPluginFactory::DSSIPluginFactory() : LADSPAPluginFactory() @@ -61,61 +62,61 @@ Profiler profiler("DSSIPluginFactory::enumeratePlugins"); for (std::vector::iterator i = m_identifiers.begin(); - i != m_identifiers.end(); ++i) { + i != m_identifiers.end(); ++i) { - const DSSI_Descriptor *ddesc = getDSSIDescriptor(*i); - if (!ddesc) continue; + const DSSI_Descriptor *ddesc = getDSSIDescriptor(*i); + if (!ddesc) continue; - const LADSPA_Descriptor *descriptor = ddesc->LADSPA_Plugin; - if (!descriptor) continue; - -// SVDEBUG << "DSSIPluginFactory::enumeratePlugins: Name " << (descriptor->Name ? descriptor->Name : "NONE" ) << endl; + const LADSPA_Descriptor *descriptor = ddesc->LADSPA_Plugin; + if (!descriptor) continue; + +// SVDEBUG << "DSSIPluginFactory::enumeratePlugins: Name " << (descriptor->Name ? descriptor->Name : "NONE" ) << endl; - list.push_back(*i); - list.push_back(descriptor->Name); - list.push_back(QString("%1").arg(descriptor->UniqueID)); - list.push_back(descriptor->Label); - list.push_back(descriptor->Maker); - list.push_back(descriptor->Copyright); - list.push_back((ddesc->run_synth || ddesc->run_multiple_synths) ? "true" : "false"); - list.push_back(ddesc->run_multiple_synths ? "true" : "false"); - list.push_back(m_taxonomy[*i]); - list.push_back(QString("%1").arg(descriptor->PortCount)); + list.push_back(*i); + list.push_back(descriptor->Name); + list.push_back(QString("%1").arg(descriptor->UniqueID)); + list.push_back(descriptor->Label); + list.push_back(descriptor->Maker); + list.push_back(descriptor->Copyright); + list.push_back((ddesc->run_synth || ddesc->run_multiple_synths) ? "true" : "false"); + list.push_back(ddesc->run_multiple_synths ? "true" : "false"); + list.push_back(m_taxonomy[*i]); + list.push_back(QString("%1").arg(descriptor->PortCount)); - for (int p = 0; p < (int)descriptor->PortCount; ++p) { + for (int p = 0; p < (int)descriptor->PortCount; ++p) { - int type = 0; - if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) { - type |= PortType::Control; - } else { - type |= PortType::Audio; - } - if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) { - type |= PortType::Input; - } else { - type |= PortType::Output; - } + int type = 0; + if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) { + type |= PortType::Control; + } else { + type |= PortType::Audio; + } + if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) { + type |= PortType::Input; + } else { + type |= PortType::Output; + } - list.push_back(QString("%1").arg(p)); - list.push_back(descriptor->PortNames[p]); - list.push_back(QString("%1").arg(type)); - list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p))); - list.push_back(QString("%1").arg(getPortMinimum(descriptor, p))); - list.push_back(QString("%1").arg(getPortMaximum(descriptor, p))); - list.push_back(QString("%1").arg(getPortDefault(descriptor, p))); - } + list.push_back(QString("%1").arg(p)); + list.push_back(descriptor->PortNames[p]); + list.push_back(QString("%1").arg(type)); + list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p))); + list.push_back(QString("%1").arg(getPortMinimum(descriptor, p))); + list.push_back(QString("%1").arg(getPortMaximum(descriptor, p))); + list.push_back(QString("%1").arg(getPortDefault(descriptor, p))); + } } unloadUnusedLibraries(); } - + RealTimePluginInstance * DSSIPluginFactory::instantiatePlugin(QString identifier, - int instrument, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int channels) + int instrument, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int channels) { Profiler profiler("DSSIPluginFactory::instantiatePlugin"); @@ -123,14 +124,14 @@ if (descriptor) { - DSSIPluginInstance *instance = - new DSSIPluginInstance - (this, instrument, identifier, position, sampleRate, blockSize, channels, - descriptor); + DSSIPluginInstance *instance = + new DSSIPluginInstance + (this, instrument, identifier, position, sampleRate, blockSize, channels, + descriptor); - m_instances.insert(instance); + m_instances.insert(instance); - return instance; + return instance; } return 0; @@ -143,49 +144,49 @@ PluginIdentifier::parseIdentifier(identifier, type, soname, label); if (soname == PluginIdentifier::BUILTIN_PLUGIN_SONAME) { - if (label == "sample_player") { - const DSSI_Descriptor *descriptor = SamplePlayer::getDescriptor(0); - if (descriptor) { - descriptor->receive_host_descriptor(&m_hostDescriptor); - } - return descriptor; - } else { - return 0; - } + if (label == "sample_player") { + const DSSI_Descriptor *descriptor = SamplePlayer::getDescriptor(0); + if (descriptor) { + descriptor->receive_host_descriptor(&m_hostDescriptor); + } + return descriptor; + } else { + return 0; + } } bool firstInLibrary = false; if (m_libraryHandles.find(soname) == m_libraryHandles.end()) { - loadLibrary(soname); - if (m_libraryHandles.find(soname) == m_libraryHandles.end()) { - cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: loadLibrary failed for " << soname << endl; - return 0; - } - firstInLibrary = true; + loadLibrary(soname); + if (m_libraryHandles.find(soname) == m_libraryHandles.end()) { + cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: loadLibrary failed for " << soname << endl; + return 0; + } + firstInLibrary = true; } void *libraryHandle = m_libraryHandles[soname]; DSSI_Descriptor_Function fn = (DSSI_Descriptor_Function) - DLSYM(libraryHandle, "dssi_descriptor"); + DLSYM(libraryHandle, "dssi_descriptor"); if (!fn) { - cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No descriptor function in library " << soname << endl; - return 0; + cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No descriptor function in library " << soname << endl; + return 0; } const DSSI_Descriptor *descriptor = 0; int index = 0; while ((descriptor = fn(index))) { - if (descriptor->LADSPA_Plugin->Label == label) { - if (firstInLibrary && (descriptor->DSSI_API_Version >= 2)) { - descriptor->receive_host_descriptor(&m_hostDescriptor); - } - return descriptor; - } - ++index; + if (descriptor->LADSPA_Plugin->Label == label) { + if (firstInLibrary && (descriptor->DSSI_API_Version >= 2)) { + descriptor->receive_host_descriptor(&m_hostDescriptor); + } + return descriptor; + } + ++index; } cerr << "WARNING: DSSIPluginFactory::getDSSIDescriptor: No such plugin as " << label << " in library " << soname << endl; @@ -206,42 +207,42 @@ DSSIPluginFactory::getPluginPath() { std::vector pathList; - std::string path; + string path; - char *cpath = getenv("DSSI_PATH"); - if (cpath) path = cpath; + (void)getEnvUtf8("DSSI_PATH", path); if (path == "") { path = DEFAULT_DSSI_PATH; - char *home = getenv("HOME"); - if (home) { - std::string::size_type f; - while ((f = path.find("$HOME")) != std::string::npos && + string home; + if (getEnvUtf8("HOME", home)) { + string::size_type f; + while ((f = path.find("$HOME")) != string::npos && f < path.length()) { path.replace(f, 5, home); } } #ifdef _WIN32 - const char *pfiles = getenv("ProgramFiles"); - if (!pfiles) pfiles = "C:\\Program Files"; - { - std::string::size_type f; - while ((f = path.find("%ProgramFiles%")) != std::string::npos && + string pfiles; + if (!getEnvUtf8("ProgramFiles", pfiles)) { + pfiles = "C:\\Program Files"; + } + + string::size_type f; + while ((f = path.find("%ProgramFiles%")) != string::npos && f < path.length()) { path.replace(f, 14, pfiles); } - } #endif } - std::string::size_type index = 0, newindex = 0; + string::size_type index = 0, newindex = 0; while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) { - pathList.push_back(path.substr(index, newindex - index).c_str()); - index = newindex + 1; + pathList.push_back(path.substr(index, newindex - index).c_str()); + index = newindex + 1; } pathList.push_back(path.substr(index).c_str()); @@ -265,8 +266,8 @@ lrdfPaths.push_back("/usr/share/ladspa/rdf"); for (std::vector::iterator i = pathList.begin(); - i != pathList.end(); ++i) { - lrdfPaths.push_back(*i + "/rdf"); + i != pathList.end(); ++i) { + lrdfPaths.push_back(*i + "/rdf"); } #ifdef DSSI_BASE @@ -300,11 +301,11 @@ } DSSI_Descriptor_Function fn = (DSSI_Descriptor_Function) - DLSYM(libraryHandle, "dssi_descriptor"); + DLSYM(libraryHandle, "dssi_descriptor"); if (!fn) { - cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No descriptor function in " << soname << endl; - return; + cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No descriptor function in " << soname << endl; + return; } const DSSI_Descriptor *descriptor = 0; @@ -312,12 +313,12 @@ int index = 0; while ((descriptor = fn(index))) { - const LADSPA_Descriptor *ladspaDescriptor = descriptor->LADSPA_Plugin; - if (!ladspaDescriptor) { - cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No LADSPA descriptor for plugin " << index << " in " << soname << endl; - ++index; - continue; - } + const LADSPA_Descriptor *ladspaDescriptor = descriptor->LADSPA_Plugin; + if (!ladspaDescriptor) { + cerr << "WARNING: DSSIPluginFactory::discoverPlugins: No LADSPA descriptor for plugin " << index << " in " << soname << endl; + ++index; + continue; + } RealTimePluginDescriptor *rtd = new RealTimePluginDescriptor; rtd->name = ladspaDescriptor->Name; @@ -332,71 +333,71 @@ rtd->audioOutputPortCount = 0; rtd->controlOutputPortCount = 0; - QString identifier = PluginIdentifier::createIdentifier - ("dssi", soname, ladspaDescriptor->Label); + QString identifier = PluginIdentifier::createIdentifier + ("dssi", soname, ladspaDescriptor->Label); #ifdef HAVE_LRDF - char *def_uri = 0; - lrdf_defaults *defs = 0; - - QString category = m_taxonomy[identifier]; + char *def_uri = 0; + lrdf_defaults *defs = 0; + + QString category = m_taxonomy[identifier]; if (category == "" && m_lrdfTaxonomy[ladspaDescriptor->UniqueID] != "") { m_taxonomy[identifier] = m_lrdfTaxonomy[ladspaDescriptor->UniqueID]; category = m_taxonomy[identifier]; } - if (category == "") { - std::string name = rtd->name; - if (name.length() > 4 && - name.substr(name.length() - 4) == " VST") { - if (descriptor->run_synth || descriptor->run_multiple_synths) { - category = "VST instruments"; - } else { - category = "VST effects"; - } - m_taxonomy[identifier] = category; - } - } + if (category == "") { + string name = rtd->name; + if (name.length() > 4 && + name.substr(name.length() - 4) == " VST") { + if (descriptor->run_synth || descriptor->run_multiple_synths) { + category = "VST instruments"; + } else { + category = "VST effects"; + } + m_taxonomy[identifier] = category; + } + } rtd->category = category.toStdString(); - -// cerr << "Plugin id is " << ladspaDescriptor->UniqueID + +// cerr << "Plugin id is " << ladspaDescriptor->UniqueID // << ", identifier is \"" << identifier -// << "\", category is \"" << category -// << "\", name is " << ladspaDescriptor->Name -// << ", label is " << ladspaDescriptor->Label -// << endl; - - def_uri = lrdf_get_default_uri(ladspaDescriptor->UniqueID); - if (def_uri) { - defs = lrdf_get_setting_values(def_uri); - } - - unsigned int controlPortNumber = 1; - - for (int i = 0; i < (int)ladspaDescriptor->PortCount; i++) { - - if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) { - - if (def_uri && defs) { - - for (int j = 0; j < (int)defs->count; j++) { - if (defs->items[j].pid == controlPortNumber) { -// cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << ladspaDescriptor->PortNames[i] << endl; - m_portDefaults[ladspaDescriptor->UniqueID][i] = - defs->items[j].value; - } - } - } - - ++controlPortNumber; - } - } +// << "\", category is \"" << category +// << "\", name is " << ladspaDescriptor->Name +// << ", label is " << ladspaDescriptor->Label +// << endl; + + def_uri = lrdf_get_default_uri(ladspaDescriptor->UniqueID); + if (def_uri) { + defs = lrdf_get_setting_values(def_uri); + } + + unsigned int controlPortNumber = 1; + + for (int i = 0; i < (int)ladspaDescriptor->PortCount; i++) { + + if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) { + + if (def_uri && defs) { + + for (int j = 0; j < (int)defs->count; j++) { + if (defs->items[j].pid == controlPortNumber) { +// cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << ladspaDescriptor->PortNames[i] << endl; + m_portDefaults[ladspaDescriptor->UniqueID][i] = + defs->items[j].value; + } + } + } + + ++controlPortNumber; + } + } #endif // HAVE_LRDF - for (unsigned long i = 0; i < ladspaDescriptor->PortCount; i++) { - if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) { + for (unsigned long i = 0; i < ladspaDescriptor->PortCount; i++) { + if (LADSPA_IS_PORT_CONTROL(ladspaDescriptor->PortDescriptors[i])) { if (LADSPA_IS_PORT_INPUT(ladspaDescriptor->PortDescriptors[i])) { ++rtd->parameterCount; } else { @@ -416,11 +417,13 @@ } } - m_identifiers.push_back(identifier); + m_identifiers.push_back(identifier); + + m_libraries[identifier] = soname; m_rtDescriptors[identifier] = rtd; - ++index; + ++index; } if (DLCLOSE(libraryHandle) != 0) { diff -r d4a28d1479a8 -r 710e6250a401 plugin/DSSIPluginFactory.h --- a/plugin/DSSIPluginFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/DSSIPluginFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -38,11 +38,13 @@ virtual void enumeratePlugins(std::vector &list); virtual RealTimePluginInstance *instantiatePlugin(QString identifier, - int clientId, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int channels); + int clientId, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int channels); + + static std::vector getPluginPath(); protected: DSSIPluginFactory(); @@ -52,8 +54,6 @@ return PluginScan::DSSIPlugin; } - virtual std::vector getPluginPath(); - virtual std::vector getLRDFPath(QString &baseUri); virtual void discoverPluginsFrom(QString soName); diff -r d4a28d1479a8 -r 710e6250a401 plugin/DSSIPluginInstance.cpp --- a/plugin/DSSIPluginInstance.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/DSSIPluginInstance.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -54,13 +54,13 @@ DSSIPluginInstance::DSSIPluginInstance(RealTimePluginFactory *factory, - int clientId, - QString identifier, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int idealChannelCount, - const DSSI_Descriptor* descriptor) : + int clientId, + QString identifier, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int idealChannelCount, + const DSSI_Descriptor* descriptor) : RealTimePluginInstance(factory, identifier), m_client(clientId), m_position(position), @@ -79,7 +79,7 @@ { #ifdef DEBUG_DSSI SVDEBUG << "DSSIPluginInstance::DSSIPluginInstance(" << identifier << ")" - << endl; + << endl; #endif init(); @@ -88,10 +88,10 @@ m_outputBuffers = new sample_t*[m_outputBufferCount]; for (size_t i = 0; i < m_audioPortsIn.size(); ++i) { - m_inputBuffers[i] = new sample_t[blockSize]; + m_inputBuffers[i] = new sample_t[blockSize]; } for (int i = 0; i < m_outputBufferCount; ++i) { - m_outputBuffers[i] = new sample_t[blockSize]; + m_outputBuffers[i] = new sample_t[blockSize]; } m_ownBuffers = true; @@ -100,9 +100,9 @@ instantiate(sampleRate); if (isOK()) { - connectPorts(); - activate(); - initialiseGroupMembership(); + connectPorts(); + activate(); + initialiseGroupMembership(); } } @@ -229,34 +229,34 @@ { if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) { m_audioPortsIn.push_back(i); - } else { + } else { m_audioPortsOut.push_back(i); - } + } } else if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) { - if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) { + if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) { - LADSPA_Data *data = new LADSPA_Data(0.0); + LADSPA_Data *data = new LADSPA_Data(0.0); - m_controlPortsIn.push_back(std::pair - (i, data)); + m_controlPortsIn.push_back(std::pair + (i, data)); - m_backupControlPortsIn.push_back(0.0); + m_backupControlPortsIn.push_back(0.0); - } else { - LADSPA_Data *data = new LADSPA_Data(0.0); - m_controlPortsOut.push_back( + } else { + LADSPA_Data *data = new LADSPA_Data(0.0); + m_controlPortsOut.push_back( std::pair(i, data)); - if (!strcmp(descriptor->PortNames[i], "latency") || - !strcmp(descriptor->PortNames[i], "_latency")) { + if (!strcmp(descriptor->PortNames[i], "latency") || + !strcmp(descriptor->PortNames[i], "_latency")) { #ifdef DEBUG_DSSI - cerr << "Wooo! We have a latency port!" << endl; + cerr << "Wooo! We have a latency port!" << endl; #endif - m_latencyPort = data; - } - } + m_latencyPort = data; + } + } } #ifdef DEBUG_DSSI else @@ -279,7 +279,7 @@ #endif if (m_latencyPort) { - if (!m_run) { + if (!m_run) { for (int i = 0; i < getAudioInputCount(); ++i) { for (int j = 0; j < m_blockSize; ++j) { m_inputBuffers[i][j] = 0.f; @@ -287,7 +287,7 @@ } run(Vamp::RealTime::zeroTime); } - latency = (sv_frame_t)(*m_latencyPort + 0.1); + latency = (sv_frame_t)(*m_latencyPort + 0.1); } #ifdef DEBUG_DSSI_PROCESS @@ -301,8 +301,8 @@ DSSIPluginInstance::silence() { if (m_instanceHandle != 0) { - deactivate(); - activate(); + deactivate(); + activate(); } } @@ -317,41 +317,41 @@ { #ifdef DEBUG_DSSI SVDEBUG << "DSSIPluginInstance::setIdealChannelCount: channel count " - << channels << " (was " << m_idealChannelCount << ")" << endl; + << channels << " (was " << m_idealChannelCount << ")" << endl; #endif if (channels == m_idealChannelCount) { - silence(); - return; + silence(); + return; } if (m_instanceHandle != 0) { - deactivate(); + deactivate(); } m_idealChannelCount = channels; if (channels > m_outputBufferCount) { - for (int i = 0; i < m_outputBufferCount; ++i) { - delete[] m_outputBuffers[i]; - } + for (int i = 0; i < m_outputBufferCount; ++i) { + delete[] m_outputBuffers[i]; + } - delete[] m_outputBuffers; + delete[] m_outputBuffers; - m_outputBufferCount = channels; + m_outputBufferCount = channels; - m_outputBuffers = new sample_t*[m_outputBufferCount]; + m_outputBuffers = new sample_t*[m_outputBufferCount]; - for (int i = 0; i < m_outputBufferCount; ++i) { - m_outputBuffers[i] = new sample_t[m_blockSize]; - } + for (int i = 0; i < m_outputBufferCount; ++i) { + m_outputBuffers[i] = new sample_t[m_blockSize]; + } - connectPorts(); + connectPorts(); } if (m_instanceHandle != 0) { - activate(); + activate(); } } @@ -367,8 +367,8 @@ DSSIPluginInstance::initialiseGroupMembership() { if (!m_descriptor->run_multiple_synths) { - m_grouped = false; - return; + m_grouped = false; + return; } //!!! GroupMap is not actually thread-safe. @@ -377,24 +377,24 @@ if (++pluginsInGroup > m_groupLocalEventBufferCount) { - size_t nextBufferCount = pluginsInGroup * 2; + size_t nextBufferCount = pluginsInGroup * 2; - snd_seq_event_t **eventLocalBuffers = new snd_seq_event_t *[nextBufferCount]; + snd_seq_event_t **eventLocalBuffers = new snd_seq_event_t *[nextBufferCount]; - for (size_t i = 0; i < m_groupLocalEventBufferCount; ++i) { - eventLocalBuffers[i] = m_groupLocalEventBuffers[i]; - } - for (size_t i = m_groupLocalEventBufferCount; i < nextBufferCount; ++i) { - eventLocalBuffers[i] = new snd_seq_event_t[EVENT_BUFFER_SIZE]; - } + for (size_t i = 0; i < m_groupLocalEventBufferCount; ++i) { + eventLocalBuffers[i] = m_groupLocalEventBuffers[i]; + } + for (size_t i = m_groupLocalEventBufferCount; i < nextBufferCount; ++i) { + eventLocalBuffers[i] = new snd_seq_event_t[EVENT_BUFFER_SIZE]; + } - if (m_groupLocalEventBuffers) { - m_bufferScavenger.claim(new ScavengerArrayWrapper - (m_groupLocalEventBuffers)); - } + if (m_groupLocalEventBuffers) { + m_bufferScavenger.claim(new ScavengerArrayWrapper + (m_groupLocalEventBuffers)); + } - m_groupLocalEventBuffers = eventLocalBuffers; - m_groupLocalEventBufferCount = nextBufferCount; + m_groupLocalEventBuffers = eventLocalBuffers; + m_groupLocalEventBufferCount = nextBufferCount; } m_grouped = true; @@ -409,22 +409,22 @@ if (m_threads.find(m_instanceHandle) != m_threads.end()) { - for (std::set::iterator i = - m_threads[m_instanceHandle].begin(); - i != m_threads[m_instanceHandle].end(); ++i) { + for (std::set::iterator i = + m_threads[m_instanceHandle].begin(); + i != m_threads[m_instanceHandle].end(); ++i) { - (*i)->setExiting(); - (*i)->wait(); - delete *i; - } + (*i)->setExiting(); + (*i)->wait(); + delete *i; + } - m_threads.erase(m_instanceHandle); + m_threads.erase(m_instanceHandle); } detachFromGroup(); if (m_instanceHandle != 0) { - deactivate(); + deactivate(); } cleanup(); @@ -439,15 +439,15 @@ m_controlPortsOut.clear(); if (m_ownBuffers) { - for (int i = 0; i < getAudioInputCount(); ++i) { - delete[] m_inputBuffers[i]; - } - for (int i = 0; i < m_outputBufferCount; ++i) { - delete[] m_outputBuffers[i]; - } + for (int i = 0; i < getAudioInputCount(); ++i) { + delete[] m_inputBuffers[i]; + } + for (int i = 0; i < m_outputBufferCount; ++i) { + delete[] m_outputBuffers[i]; + } - delete[] m_inputBuffers; - delete[] m_outputBuffers; + delete[] m_inputBuffers; + delete[] m_outputBuffers; } m_audioPortsIn.clear(); @@ -467,10 +467,10 @@ const LADSPA_Descriptor *descriptor = m_descriptor->LADSPA_Plugin; if (!descriptor->instantiate) { - cerr << "Bad plugin: plugin id " << descriptor->UniqueID - << ":" << descriptor->Label - << " has no instantiate method!" << endl; - return; + cerr << "Bad plugin: plugin id " << descriptor->UniqueID + << ":" << descriptor->Label + << " has no instantiate method!" << endl; + return; } unsigned long pluginRate = (unsigned long)(sampleRate); @@ -483,24 +483,24 @@ if (m_instanceHandle) { - if (m_descriptor->get_midi_controller_for_port) { + if (m_descriptor->get_midi_controller_for_port) { - for (int i = 0; i < (int)descriptor->PortCount; ++i) { + for (int i = 0; i < (int)descriptor->PortCount; ++i) { - if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i]) && - LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) { + if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i]) && + LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) { - int controller = m_descriptor->get_midi_controller_for_port - (m_instanceHandle, i); + int controller = m_descriptor->get_midi_controller_for_port + (m_instanceHandle, i); - if (controller != 0 && controller != 32 && - DSSI_IS_CC(controller)) { + if (controller != 0 && controller != 32 && + DSSI_IS_CC(controller)) { - m_controllerMap[DSSI_CC_NUMBER(controller)] = i; - } - } - } - } + m_controllerMap[DSSI_CC_NUMBER(controller)] = i; + } + } + } + } } } @@ -515,19 +515,19 @@ #endif if (!m_descriptor || !m_descriptor->get_program) { - m_programCacheValid = true; - return; + m_programCacheValid = true; + return; } int index = 0; const DSSI_Program_Descriptor *programDescriptor; while ((programDescriptor = m_descriptor->get_program(m_instanceHandle, index))) { - ++index; - ProgramDescriptor d; - d.bank = (int)programDescriptor->Bank; - d.program = (int)programDescriptor->Program; - d.name = programDescriptor->Name; - m_cachedPrograms.push_back(d); + ++index; + ProgramDescriptor d; + d.bank = (int)programDescriptor->Bank; + d.program = (int)programDescriptor->Program; + d.name = programDescriptor->Name; + m_cachedPrograms.push_back(d); } #ifdef DEBUG_DSSI @@ -551,8 +551,8 @@ ProgramList programs; for (std::vector::iterator i = m_cachedPrograms.begin(); - i != m_cachedPrograms.end(); ++i) { - programs.push_back(i->name); + i != m_cachedPrograms.end(); ++i) { + programs.push_back(i->name); } return programs; @@ -570,8 +570,8 @@ checkProgramCache(); for (std::vector::iterator i = m_cachedPrograms.begin(); - i != m_cachedPrograms.end(); ++i) { - if (i->bank == bank && i->program == program) return i->name; + i != m_cachedPrograms.end(); ++i) { + if (i->bank == bank && i->program == program) return i->name; } return std::string(); @@ -591,12 +591,12 @@ int rv; for (std::vector::iterator i = m_cachedPrograms.begin(); - i != m_cachedPrograms.end(); ++i) { - if (i->name == name) { - rv = i->bank; - rv = (rv << 16) + i->program; - return rv; - } + i != m_cachedPrograms.end(); ++i) { + if (i->name == name) { + rv = i->bank; + rv = (rv << 16) + i->program; + return rv; + } } return 0; @@ -631,20 +631,20 @@ int bankNo = 0, programNo = 0; for (std::vector::iterator i = m_cachedPrograms.begin(); - i != m_cachedPrograms.end(); ++i) { + i != m_cachedPrograms.end(); ++i) { - if (i->name == program) { + if (i->name == program) { - bankNo = i->bank; - programNo = i->program; - found = true; + bankNo = i->bank; + programNo = i->program; + found = true; #ifdef DEBUG_DSSI - SVDEBUG << "DSSIPluginInstance::selectProgram(" << program << "): found at bank " << bankNo << ", program " << programNo << endl; + SVDEBUG << "DSSIPluginInstance::selectProgram(" << program << "): found at bank " << bankNo << ", program " << programNo << endl; #endif - break; - } + break; + } } if (!found) return; @@ -660,9 +660,9 @@ #endif if (backupPortValues) { - for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) { - m_backupControlPortsIn[i] = *m_controlPortsIn[i].second; - } + for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) { + m_backupControlPortsIn[i] = *m_controlPortsIn[i].second; + } } } @@ -678,16 +678,16 @@ if (m_program != "") { #ifdef DEBUG_DSSI - SVDEBUG << "DSSIPluginInstance::activate: restoring program " << m_program << endl; + SVDEBUG << "DSSIPluginInstance::activate: restoring program " << m_program << endl; #endif - selectProgramAux(m_program, false); + selectProgramAux(m_program, false); } for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) { #ifdef DEBUG_DSSI - SVDEBUG << "DSSIPluginInstance::activate: setting port " << m_controlPortsIn[i].first << " to " << m_backupControlPortsIn[i] << endl; + SVDEBUG << "DSSIPluginInstance::activate: setting port " << m_controlPortsIn[i].first << " to " << m_backupControlPortsIn[i] << endl; #endif - *m_controlPortsIn[i].second = m_backupControlPortsIn[i]; + *m_controlPortsIn[i].second = m_backupControlPortsIn[i]; } } @@ -697,8 +697,8 @@ if (!m_descriptor || !m_descriptor->LADSPA_Plugin->connect_port) return; #ifdef DEBUG_DSSI SVDEBUG << "DSSIPluginInstance::connectPorts: " << getAudioInputCount() - << " audio ports in, " << m_audioPortsOut.size() << " out, " - << m_outputBufferCount << " output buffers" << endl; + << " audio ports in, " << m_audioPortsOut.size() << " out, " + << m_outputBufferCount << " output buffers" << endl; #endif assert(sizeof(LADSPA_Data) == sizeof(float)); @@ -708,26 +708,26 @@ int inbuf = 0, outbuf = 0; for (int i = 0; i < getAudioInputCount(); ++i) { - m_descriptor->LADSPA_Plugin->connect_port - (m_instanceHandle, - m_audioPortsIn[i], - (LADSPA_Data *)m_inputBuffers[inbuf]); - ++inbuf; + m_descriptor->LADSPA_Plugin->connect_port + (m_instanceHandle, + m_audioPortsIn[i], + (LADSPA_Data *)m_inputBuffers[inbuf]); + ++inbuf; } for (size_t i = 0; i < m_audioPortsOut.size(); ++i) { - m_descriptor->LADSPA_Plugin->connect_port - (m_instanceHandle, - m_audioPortsOut[i], - (LADSPA_Data *)m_outputBuffers[outbuf]); - ++outbuf; + m_descriptor->LADSPA_Plugin->connect_port + (m_instanceHandle, + m_audioPortsOut[i], + (LADSPA_Data *)m_outputBuffers[outbuf]); + ++outbuf; } for (size_t i = 0; i < m_controlPortsIn.size(); ++i) { - m_descriptor->LADSPA_Plugin->connect_port - (m_instanceHandle, - m_controlPortsIn[i].first, - m_controlPortsIn[i].second); + m_descriptor->LADSPA_Plugin->connect_port + (m_instanceHandle, + m_controlPortsIn[i].first, + m_controlPortsIn[i].second); if (f) { float defaultValue = f->getPortDefault @@ -741,10 +741,10 @@ } for (size_t i = 0; i < m_controlPortsOut.size(); ++i) { - m_descriptor->LADSPA_Plugin->connect_port - (m_instanceHandle, - m_controlPortsOut[i].first, - m_controlPortsOut[i].second); + m_descriptor->LADSPA_Plugin->connect_port + (m_instanceHandle, + m_controlPortsOut[i].first, + m_controlPortsOut[i].second); } } @@ -766,12 +766,12 @@ LADSPAPluginFactory *f = dynamic_cast(m_factory); if (f) { - if (value < f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber)) { - value = f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber); - } - if (value > f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber)) { - value = f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber); - } + if (value < f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber)) { + value = f->getPortMinimum(m_descriptor->LADSPA_Plugin, portNumber); + } + if (value > f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber)) { + value = f->getPortMaximum(m_descriptor->LADSPA_Plugin, portNumber); + } } (*m_controlPortsIn[parameter].second) = value; @@ -793,27 +793,27 @@ float value = (float)cv; if (!LADSPA_IS_HINT_BOUNDED_BELOW(d)) { - if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) { - /* unbounded: might as well leave the value alone. */ - } else { - /* bounded above only. just shift the range. */ - value = ub - 127.0f + value; - } + if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) { + /* unbounded: might as well leave the value alone. */ + } else { + /* bounded above only. just shift the range. */ + value = ub - 127.0f + value; + } } else { - if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) { - /* bounded below only. just shift the range. */ - value = lb + value; - } else { - /* bounded both ends. more interesting. */ - /* XXX !!! todo: fill in logarithmic, sample rate &c */ - value = lb + ((ub - lb) * value / 127.0f); - } + if (!LADSPA_IS_HINT_BOUNDED_ABOVE(d)) { + /* bounded below only. just shift the range. */ + value = lb + value; + } else { + /* bounded both ends. more interesting. */ + /* XXX !!! todo: fill in logarithmic, sample rate &c */ + value = lb + ((ub - lb) * value / 127.0f); + } } for (int i = 0; in_range_for(m_controlPortsIn, i); ++i) { - if (m_controlPortsIn[i].first == port) { - setParameterValue(i, value); - } + if (m_controlPortsIn[i].first == port) { + setParameterValue(i, value); + } } } @@ -841,10 +841,10 @@ LADSPAPluginFactory *f = dynamic_cast(m_factory); if (f) { - return f->getPortDefault(m_descriptor->LADSPA_Plugin, - m_controlPortsIn[parameter].first); + return f->getPortDefault(m_descriptor->LADSPA_Plugin, + m_controlPortsIn[parameter].first); } else { - return 0.0f; + return 0.0f; } } @@ -855,35 +855,35 @@ LADSPAPluginFactory *f = dynamic_cast(m_factory); if (f) { - return f->getPortDisplayHint(m_descriptor->LADSPA_Plugin, + return f->getPortDisplayHint(m_descriptor->LADSPA_Plugin, m_controlPortsIn[parameter].first); } else { - return PortHint::NoHint; + return PortHint::NoHint; } } std::string DSSIPluginInstance::configure(std::string key, - std::string value) + std::string value) { if (!m_descriptor || !m_descriptor->configure) return std::string(); if (key == PluginIdentifier::RESERVED_PROJECT_DIRECTORY_KEY.toStdString()) { #ifdef DSSI_PROJECT_DIRECTORY_KEY - key = DSSI_PROJECT_DIRECTORY_KEY; + key = DSSI_PROJECT_DIRECTORY_KEY; #else - return std::string(); + return std::string(); #endif } - + #ifdef DEBUG_DSSI SVDEBUG << "DSSIPluginInstance::configure(" << key << "," << value << ")" << endl; #endif char *message = m_descriptor->configure(m_instanceHandle, - key.c_str(), - value.c_str()); + key.c_str(), + value.c_str()); m_programCacheValid = false; @@ -895,16 +895,16 @@ // as project directory #ifdef DSSI_RESERVED_CONFIGURE_PREFIX if (QString(key.c_str()).startsWith(DSSI_RESERVED_CONFIGURE_PREFIX)) { - return qm; + return qm; } #endif if (message) { - if (m_descriptor->LADSPA_Plugin && m_descriptor->LADSPA_Plugin->Label) { - qm = std::string(m_descriptor->LADSPA_Plugin->Label) + ": "; - } - qm = qm + message; - free(message); + if (m_descriptor->LADSPA_Plugin && m_descriptor->LADSPA_Plugin->Label) { + qm = std::string(m_descriptor->LADSPA_Plugin->Label) + ": "; + } + qm = qm + message; + free(message); cerr << "DSSIPluginInstance::configure: warning: configure returned message: \"" << qm << "\"" << endl; } @@ -914,7 +914,7 @@ void DSSIPluginInstance::sendEvent(const RealTime &eventTime, - const void *e) + const void *e) { #ifdef DEBUG_DSSI_PROCESS SVDEBUG << "DSSIPluginInstance::sendEvent: last was " << m_lastEventSendTime << " (valid " << m_haveLastEventSendTime << "), this is " << eventTime << endl; @@ -925,12 +925,12 @@ // we will happily drop events here if we find the timeline going // backwards. if (m_haveLastEventSendTime && - m_lastEventSendTime > eventTime) { + m_lastEventSendTime > eventTime) { #ifdef DEBUG_DSSI_PROCESS - cerr << "... clearing down" << endl; + cerr << "... clearing down" << endl; #endif - m_haveLastEventSendTime = false; - clearEvents(); + m_haveLastEventSendTime = false; + clearEvents(); } snd_seq_event_t *event = (snd_seq_event_t *)e; @@ -968,21 +968,21 @@ #endif if (controller == 0) { // bank select MSB - - m_pending.msb = ev->data.control.value; + + m_pending.msb = ev->data.control.value; } else if (controller == 32) { // bank select LSB - m_pending.lsb = ev->data.control.value; + m_pending.lsb = ev->data.control.value; } else if (controller > 0 && controller < 128) { - - if (m_controllerMap.find(controller) != m_controllerMap.end()) { - int port = m_controllerMap[controller]; - setPortValueFromController(port, ev->data.control.value); - } else { - return true; // pass through to plugin - } + + if (m_controllerMap.find(controller) != m_controllerMap.end()) { + int port = m_controllerMap[controller]; + setPortValueFromController(port, ev->data.control.value); + } else { + return true; // pass through to plugin + } } return false; @@ -1000,32 +1000,32 @@ if (m_descriptor && m_descriptor->select_program) needLock = true; if (needLock) { - if (!m_processLock.tryLock()) { - for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) { - memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t)); - } - return; - } + if (!m_processLock.tryLock()) { + for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) { + memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t)); + } + return; + } } if (m_grouped) { - runGrouped(blockTime); - goto done; + runGrouped(blockTime); + goto done; } if (!m_descriptor || !m_descriptor->run_synth) { - m_eventBuffer.skip(m_eventBuffer.getReadSpace()); - m_haveLastEventSendTime = false; - if (m_descriptor && m_descriptor->LADSPA_Plugin->run) { - m_descriptor->LADSPA_Plugin->run(m_instanceHandle, count); - } else { - for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) { - memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t)); - } - } - m_run = true; - if (needLock) m_processLock.unlock(); - return; + m_eventBuffer.skip(m_eventBuffer.getReadSpace()); + m_haveLastEventSendTime = false; + if (m_descriptor && m_descriptor->LADSPA_Plugin->run) { + m_descriptor->LADSPA_Plugin->run(m_instanceHandle, count); + } else { + for (size_t ch = 0; ch < m_audioPortsOut.size(); ++ch) { + memset(m_outputBuffers[ch], 0, m_blockSize * sizeof(sample_t)); + } + } + m_run = true; + if (needLock) m_processLock.unlock(); + return; } #ifdef DEBUG_DSSI_PROCESS @@ -1034,65 +1034,65 @@ #ifdef DEBUG_DSSI_PROCESS if (m_eventBuffer.getReadSpace() > 0) { - SVDEBUG << "DSSIPluginInstance::run: event buffer has " - << m_eventBuffer.getReadSpace() << " event(s) in it" << endl; + SVDEBUG << "DSSIPluginInstance::run: event buffer has " + << m_eventBuffer.getReadSpace() << " event(s) in it" << endl; } #endif while (m_eventBuffer.getReadSpace() > 0) { - snd_seq_event_t *ev = localEventBuffer + evCount; - *ev = m_eventBuffer.peekOne(); - bool accept = true; + snd_seq_event_t *ev = localEventBuffer + evCount; + *ev = m_eventBuffer.peekOne(); + bool accept = true; - RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec); + RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec); sv_frame_t frameOffset = 0; - if (evTime > blockTime) { - frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate); - } + if (evTime > blockTime) { + frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate); + } #ifdef DEBUG_DSSI_PROCESS - SVDEBUG << "DSSIPluginInstance::run: evTime " << evTime << ", blockTime " << blockTime << ", frameOffset " << frameOffset - << ", blockSize " << m_blockSize << endl; - cerr << "Type: " << int(ev->type) << ", pitch: " << int(ev->data.note.note) << ", velocity: " << int(ev->data.note.velocity) << endl; + SVDEBUG << "DSSIPluginInstance::run: evTime " << evTime << ", blockTime " << blockTime << ", frameOffset " << frameOffset + << ", blockSize " << m_blockSize << endl; + cerr << "Type: " << int(ev->type) << ", pitch: " << int(ev->data.note.note) << ", velocity: " << int(ev->data.note.velocity) << endl; #endif - if (frameOffset >= (long)count) break; - if (frameOffset < 0) { - frameOffset = 0; - if (ev->type == SND_SEQ_EVENT_NOTEON) { - m_eventBuffer.skip(1); - continue; - } - } + if (frameOffset >= (long)count) break; + if (frameOffset < 0) { + frameOffset = 0; + if (ev->type == SND_SEQ_EVENT_NOTEON) { + m_eventBuffer.skip(1); + continue; + } + } - ev->time.tick = (snd_seq_tick_time_t)frameOffset; - m_eventBuffer.skip(1); + ev->time.tick = (snd_seq_tick_time_t)frameOffset; + m_eventBuffer.skip(1); - if (ev->type == SND_SEQ_EVENT_CONTROLLER) { - accept = handleController(ev); - } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) { - m_pending.program = ev->data.control.value; - accept = false; - } + if (ev->type == SND_SEQ_EVENT_CONTROLLER) { + accept = handleController(ev); + } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) { + m_pending.program = ev->data.control.value; + accept = false; + } - if (accept) { - if (++evCount >= EVENT_BUFFER_SIZE) break; - } + if (accept) { + if (++evCount >= EVENT_BUFFER_SIZE) break; + } } if (m_pending.program >= 0 && m_descriptor->select_program) { - int program = m_pending.program; - int bank = m_pending.lsb + 128 * m_pending.msb; + int program = m_pending.program; + int bank = m_pending.lsb + 128 * m_pending.msb; #ifdef DEBUG_DSSI SVDEBUG << "DSSIPluginInstance::run: making select_program(" << bank << "," << program << ") call" << endl; #endif - m_pending.lsb = m_pending.msb = m_pending.program = -1; - m_descriptor->select_program(m_instanceHandle, bank, program); + m_pending.lsb = m_pending.msb = m_pending.program = -1; + m_descriptor->select_program(m_instanceHandle, bank, program); #ifdef DEBUG_DSSI SVDEBUG << "DSSIPluginInstance::run: made select_program(" << bank << "," << program << ") call" << endl; @@ -1101,16 +1101,16 @@ #ifdef DEBUG_DSSI_PROCESS SVDEBUG << "DSSIPluginInstance::run: running with " << evCount << " events" - << endl; + << endl; #endif m_descriptor->run_synth(m_instanceHandle, count, - localEventBuffer, evCount); + localEventBuffer, evCount); #ifdef DEBUG_DSSI_PROCESS // for (int i = 0; i < count; ++i) { -// cout << m_outputBuffers[0][i] << " "; -// if (i % 8 == 0) cout << endl; +// cout << m_outputBuffers[0][i] << " "; +// if (i % 8 == 0) cout << endl; // } #endif @@ -1120,31 +1120,31 @@ int numAudioOuts = int(m_audioPortsOut.size()); if (numAudioOuts == 0) { - // copy inputs to outputs - for (int ch = 0; ch < m_idealChannelCount; ++ch) { - int sch = ch % getAudioInputCount(); - for (int i = 0; i < m_blockSize; ++i) { - m_outputBuffers[ch][i] = m_inputBuffers[sch][i]; - } - } + // copy inputs to outputs + for (int ch = 0; ch < m_idealChannelCount; ++ch) { + int sch = ch % getAudioInputCount(); + for (int i = 0; i < m_blockSize; ++i) { + m_outputBuffers[ch][i] = m_inputBuffers[sch][i]; + } + } } else if (m_idealChannelCount < numAudioOuts) { - if (m_idealChannelCount == 1) { - // mix down to mono - for (int ch = 1; ch < numAudioOuts; ++ch) { - for (int i = 0; i < m_blockSize; ++i) { - m_outputBuffers[0][i] += m_outputBuffers[ch][i]; - } - } - } + if (m_idealChannelCount == 1) { + // mix down to mono + for (int ch = 1; ch < numAudioOuts; ++ch) { + for (int i = 0; i < m_blockSize; ++i) { + m_outputBuffers[0][i] += m_outputBuffers[ch][i]; + } + } + } } else if (m_idealChannelCount > numAudioOuts) { - // duplicate - for (int ch = numAudioOuts; ch < m_idealChannelCount; ++ch) { - int sch = (ch - numAudioOuts) % numAudioOuts; - for (int i = 0; i < m_blockSize; ++i) { - m_outputBuffers[ch][i] = m_outputBuffers[sch][i]; - } - } - } + // duplicate + for (int ch = numAudioOuts; ch < m_idealChannelCount; ++ch) { + int sch = (ch - numAudioOuts) % numAudioOuts; + for (int i = 0; i < m_blockSize; ++i) { + m_outputBuffers[ch][i] = m_outputBuffers[sch][i]; + } + } + } m_lastRunTime = blockTime; m_run = true; @@ -1168,22 +1168,22 @@ #endif if (m_lastRunTime != blockTime) { - for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) { - DSSIPluginInstance *instance = *i; - if (instance != this && instance->m_lastRunTime == blockTime) { + for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) { + DSSIPluginInstance *instance = *i; + if (instance != this && instance->m_lastRunTime == blockTime) { #ifdef DEBUG_DSSI_PROCESS - SVDEBUG << "DSSIPluginInstance::runGrouped(" << blockTime << "): plugin " << instance << " has already been run" << endl; + SVDEBUG << "DSSIPluginInstance::runGrouped(" << blockTime << "): plugin " << instance << " has already been run" << endl; #endif - needRun = false; - } - } + needRun = false; + } + } } if (!needRun) { #ifdef DEBUG_DSSI_PROCESS - SVDEBUG << "DSSIPluginInstance::runGrouped(" << blockTime << "): already run, returning" << endl; + SVDEBUG << "DSSIPluginInstance::runGrouped(" << blockTime << "): already run, returning" << endl; #endif - return; + return; } #ifdef DEBUG_DSSI_PROCESS @@ -1192,81 +1192,81 @@ size_t index = 0; unsigned long *counts = (unsigned long *) - alloca(m_groupLocalEventBufferCount * sizeof(unsigned long)); + alloca(m_groupLocalEventBufferCount * sizeof(unsigned long)); LADSPA_Handle *instances = (LADSPA_Handle *) - alloca(m_groupLocalEventBufferCount * sizeof(LADSPA_Handle)); + alloca(m_groupLocalEventBufferCount * sizeof(LADSPA_Handle)); for (PluginSet::iterator i = s.begin(); i != s.end(); ++i) { - if (index >= m_groupLocalEventBufferCount) break; + if (index >= m_groupLocalEventBufferCount) break; - DSSIPluginInstance *instance = *i; - counts[index] = 0; - instances[index] = instance->m_instanceHandle; + DSSIPluginInstance *instance = *i; + counts[index] = 0; + instances[index] = instance->m_instanceHandle; #ifdef DEBUG_DSSI_PROCESS - SVDEBUG << "DSSIPluginInstance::runGrouped(" << blockTime << "): running " << instance << endl; + SVDEBUG << "DSSIPluginInstance::runGrouped(" << blockTime << "): running " << instance << endl; #endif - if (instance->m_pending.program >= 0 && - instance->m_descriptor->select_program) { - int program = instance->m_pending.program; - int bank = instance->m_pending.lsb + 128 * instance->m_pending.msb; - instance->m_pending.lsb = instance->m_pending.msb = instance->m_pending.program = -1; - instance->m_descriptor->select_program - (instance->m_instanceHandle, bank, program); - } + if (instance->m_pending.program >= 0 && + instance->m_descriptor->select_program) { + int program = instance->m_pending.program; + int bank = instance->m_pending.lsb + 128 * instance->m_pending.msb; + instance->m_pending.lsb = instance->m_pending.msb = instance->m_pending.program = -1; + instance->m_descriptor->select_program + (instance->m_instanceHandle, bank, program); + } - while (instance->m_eventBuffer.getReadSpace() > 0) { + while (instance->m_eventBuffer.getReadSpace() > 0) { - snd_seq_event_t *ev = m_groupLocalEventBuffers[index] + counts[index]; - *ev = instance->m_eventBuffer.peekOne(); - bool accept = true; + snd_seq_event_t *ev = m_groupLocalEventBuffers[index] + counts[index]; + *ev = instance->m_eventBuffer.peekOne(); + bool accept = true; - RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec); + RealTime evTime(ev->time.time.tv_sec, ev->time.time.tv_nsec); - sv_frame_t frameOffset = 0; - if (evTime > blockTime) { - frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate); - } + sv_frame_t frameOffset = 0; + if (evTime > blockTime) { + frameOffset = RealTime::realTime2Frame(evTime - blockTime, m_sampleRate); + } #ifdef DEBUG_DSSI_PROCESS - SVDEBUG << "DSSIPluginInstance::runGrouped: evTime " << evTime << ", frameOffset " << frameOffset - << ", block size " << m_blockSize << endl; + SVDEBUG << "DSSIPluginInstance::runGrouped: evTime " << evTime << ", frameOffset " << frameOffset + << ", block size " << m_blockSize << endl; #endif - if (frameOffset >= int(m_blockSize)) break; - if (frameOffset < 0) frameOffset = 0; + if (frameOffset >= int(m_blockSize)) break; + if (frameOffset < 0) frameOffset = 0; - ev->time.tick = snd_seq_tick_time_t(frameOffset); - instance->m_eventBuffer.skip(1); + ev->time.tick = snd_seq_tick_time_t(frameOffset); + instance->m_eventBuffer.skip(1); - if (ev->type == SND_SEQ_EVENT_CONTROLLER) { - accept = instance->handleController(ev); - } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) { - instance->m_pending.program = ev->data.control.value; - accept = false; - } + if (ev->type == SND_SEQ_EVENT_CONTROLLER) { + accept = instance->handleController(ev); + } else if (ev->type == SND_SEQ_EVENT_PGMCHANGE) { + instance->m_pending.program = ev->data.control.value; + accept = false; + } - if (accept) { - if (++counts[index] >= EVENT_BUFFER_SIZE) break; - } - } + if (accept) { + if (++counts[index] >= EVENT_BUFFER_SIZE) break; + } + } - ++index; + ++index; } m_descriptor->run_multiple_synths(index, - instances, - m_blockSize, - m_groupLocalEventBuffers, - counts); + instances, + m_blockSize, + m_groupLocalEventBuffers, + counts); } int DSSIPluginInstance::requestMidiSend(LADSPA_Handle /* instance */, - unsigned char /* ports */, - unsigned char /* channels */) + unsigned char /* ports */, + unsigned char /* channels */) { // This is called from a non-RT context (during instantiate) @@ -1276,8 +1276,8 @@ void DSSIPluginInstance::midiSend(LADSPA_Handle /* instance */, - snd_seq_event_t * /* events */, - unsigned long /* eventCount */) + snd_seq_event_t * /* events */, + unsigned long /* eventCount */) { // This is likely to be called from an RT context @@ -1288,14 +1288,14 @@ DSSIPluginInstance::NonRTPluginThread::run() { while (!m_exiting) { - m_runFunction(m_handle); - usleep(100000); + m_runFunction(m_handle); + usleep(100000); } } int DSSIPluginInstance::requestNonRTThread(LADSPA_Handle instance, - void (*runFunction)(LADSPA_Handle)) + void (*runFunction)(LADSPA_Handle)) { NonRTPluginThread *thread = new NonRTPluginThread(instance, runFunction); m_threads[instance].insert(thread); @@ -1312,7 +1312,7 @@ if (!m_descriptor || !m_descriptor->LADSPA_Plugin->deactivate) return; for (size_t i = 0; i < m_backupControlPortsIn.size(); ++i) { - m_backupControlPortsIn[i] = *m_controlPortsIn[i].second; + m_backupControlPortsIn[i] = *m_controlPortsIn[i].second; } m_descriptor->LADSPA_Plugin->deactivate(m_instanceHandle); @@ -1332,11 +1332,11 @@ if (!m_descriptor) return; if (!m_descriptor->LADSPA_Plugin->cleanup) { - cerr << "Bad plugin: plugin id " - << m_descriptor->LADSPA_Plugin->UniqueID - << ":" << m_descriptor->LADSPA_Plugin->Label - << " has no cleanup method!" << endl; - return; + cerr << "Bad plugin: plugin id " + << m_descriptor->LADSPA_Plugin->UniqueID + << ":" << m_descriptor->LADSPA_Plugin->Label + << " has no cleanup method!" << endl; + return; } m_descriptor->LADSPA_Plugin->cleanup(m_instanceHandle); diff -r d4a28d1479a8 -r 710e6250a401 plugin/DSSIPluginInstance.h --- a/plugin/DSSIPluginInstance.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/DSSIPluginInstance.h Mon Sep 17 13:51:14 2018 +0100 @@ -68,7 +68,7 @@ virtual std::string configure(std::string key, std::string value); virtual void sendEvent(const RealTime &eventTime, - const void *event); + const void *event); virtual void clearEvents(); virtual int getBufferSize() const { return m_blockSize; } @@ -107,13 +107,13 @@ // Constructor that creates the buffers internally // DSSIPluginInstance(RealTimePluginFactory *factory, - int client, - QString identifier, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int idealChannelCount, - const DSSI_Descriptor* descriptor); + int client, + QString identifier, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int idealChannelCount, + const DSSI_Descriptor* descriptor); void init(); void instantiate(sv_samplerate_t sampleRate); @@ -132,13 +132,13 @@ // For use in DSSIPluginFactory (set in the DSSI_Host_Descriptor): static int requestMidiSend(LADSPA_Handle instance, - unsigned char ports, - unsigned char channels); + unsigned char ports, + unsigned char channels); static void midiSend(LADSPA_Handle instance, - snd_seq_event_t *events, - unsigned long eventCount); + snd_seq_event_t *events, + unsigned long eventCount); static int requestNonRTThread(LADSPA_Handle instance, - void (*runFunction)(LADSPA_Handle)); + void (*runFunction)(LADSPA_Handle)); int m_client; int m_position; @@ -156,16 +156,16 @@ std::vector m_audioPortsOut; struct ProgramControl { - int msb; - int lsb; - int program; + int msb; + int lsb; + int program; }; ProgramControl m_pending; struct ProgramDescriptor { - int bank; - int program; - std::string name; + int bank; + int program; + std::string name; }; mutable std::vector m_cachedPrograms; mutable bool m_programCacheValid; @@ -203,19 +203,19 @@ class NonRTPluginThread : public Thread { public: - NonRTPluginThread(LADSPA_Handle handle, - void (*runFunction)(LADSPA_Handle)) : - m_handle(handle), - m_runFunction(runFunction), - m_exiting(false) { } + NonRTPluginThread(LADSPA_Handle handle, + void (*runFunction)(LADSPA_Handle)) : + m_handle(handle), + m_runFunction(runFunction), + m_exiting(false) { } - virtual void run(); - void setExiting() { m_exiting = true; } + virtual void run(); + void setExiting() { m_exiting = true; } protected: - LADSPA_Handle m_handle; - void (*m_runFunction)(LADSPA_Handle); - bool m_exiting; + LADSPA_Handle m_handle; + void (*m_runFunction)(LADSPA_Handle); + bool m_exiting; }; static std::map > m_threads; }; diff -r d4a28d1479a8 -r 710e6250a401 plugin/FeatureExtractionPluginFactory.h --- a/plugin/FeatureExtractionPluginFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/FeatureExtractionPluginFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -34,17 +34,13 @@ /** * Return all installed plugin identifiers. */ - virtual std::vector getPluginIdentifiers(QString &errorMessage) { - return instance()->getPluginIdentifiers(errorMessage); - } - + virtual std::vector getPluginIdentifiers(QString &errorMsg) = 0; + /** * Return static data for the given plugin. */ - virtual piper_vamp::PluginStaticData getPluginStaticData(QString identifier) { - return instance()->getPluginStaticData(identifier); - } - + virtual piper_vamp::PluginStaticData getPluginStaticData(QString ident) = 0; + /** * Instantiate (load) and return pointer to the plugin with the * given identifier, at the given sample rate. We don't set @@ -52,16 +48,20 @@ * via initialize() on the plugin itself after loading. */ virtual Vamp::Plugin *instantiatePlugin(QString identifier, - sv_samplerate_t inputSampleRate) { - return instance()->instantiatePlugin(identifier, inputSampleRate); - } - + sv_samplerate_t inputSampleRate) = 0; + /** * Get category metadata about a plugin (without instantiating it). */ - virtual QString getPluginCategory(QString identifier) { - return instance()->getPluginCategory(identifier); - } + virtual QString getPluginCategory(QString identifier) = 0; + + /** + * Get the full file path (including both directory and filename) + * of the library file that provides a given plugin + * identifier. Note getPluginIdentifiers() must have been called + * before this has access to the necessary information. + */ + virtual QString getPluginLibraryPath(QString identifier) = 0; }; #endif diff -r d4a28d1479a8 -r 710e6250a401 plugin/LADSPAPluginFactory.cpp --- a/plugin/LADSPAPluginFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/LADSPAPluginFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -40,6 +40,7 @@ #include "lrdf.h" #endif // HAVE_LRDF +using std::string; LADSPAPluginFactory::LADSPAPluginFactory() { @@ -51,9 +52,9 @@ LADSPAPluginFactory::~LADSPAPluginFactory() { for (std::set::iterator i = m_instances.begin(); - i != m_instances.end(); ++i) { - (*i)->setFactory(0); - delete *i; + i != m_instances.end(); ++i) { + (*i)->setFactory(0); + delete *i; } m_instances.clear(); unloadUnusedLibraries(); @@ -69,68 +70,74 @@ return m_identifiers; } +QString +LADSPAPluginFactory::getPluginLibraryPath(QString identifier) +{ + return m_libraries[identifier]; +} + void LADSPAPluginFactory::enumeratePlugins(std::vector &list) { Profiler profiler("LADSPAPluginFactory::enumeratePlugins"); for (std::vector::iterator i = m_identifiers.begin(); - i != m_identifiers.end(); ++i) { + i != m_identifiers.end(); ++i) { - const LADSPA_Descriptor *descriptor = getLADSPADescriptor(*i); + const LADSPA_Descriptor *descriptor = getLADSPADescriptor(*i); - if (!descriptor) { - cerr << "WARNING: LADSPAPluginFactory::enumeratePlugins: couldn't get descriptor for identifier " << *i << endl; - continue; - } - - list.push_back(*i); - list.push_back(descriptor->Name); - list.push_back(QString("%1").arg(descriptor->UniqueID)); - list.push_back(descriptor->Label); - list.push_back(descriptor->Maker); - list.push_back(descriptor->Copyright); - list.push_back("false"); // is synth - list.push_back("false"); // is grouped + if (!descriptor) { + cerr << "WARNING: LADSPAPluginFactory::enumeratePlugins: couldn't get descriptor for identifier " << *i << endl; + continue; + } + + list.push_back(*i); + list.push_back(descriptor->Name); + list.push_back(QString("%1").arg(descriptor->UniqueID)); + list.push_back(descriptor->Label); + list.push_back(descriptor->Maker); + list.push_back(descriptor->Copyright); + list.push_back("false"); // is synth + list.push_back("false"); // is grouped - if (m_taxonomy.find(*i) != m_taxonomy.end() && m_taxonomy[*i] != "") { -// cerr << "LADSPAPluginFactory: cat for " << *i << " found in taxonomy as " << m_taxonomy[descriptor->UniqueID] << endl; - list.push_back(m_taxonomy[*i]); - } else { - list.push_back(""); -// cerr << "LADSPAPluginFactory: cat for " << *i << " not found (despite having " << m_fallbackCategories.size() << " fallbacks)" << endl; - - } + if (m_taxonomy.find(*i) != m_taxonomy.end() && m_taxonomy[*i] != "") { +// cerr << "LADSPAPluginFactory: cat for " << *i << " found in taxonomy as " << m_taxonomy[descriptor->UniqueID] << endl; + list.push_back(m_taxonomy[*i]); + } else { + list.push_back(""); +// cerr << "LADSPAPluginFactory: cat for " << *i << " not found (despite having " << m_fallbackCategories.size() << " fallbacks)" << endl; + + } - list.push_back(QString("%1").arg(descriptor->PortCount)); + list.push_back(QString("%1").arg(descriptor->PortCount)); - for (int p = 0; p < (int)descriptor->PortCount; ++p) { + for (int p = 0; p < (int)descriptor->PortCount; ++p) { - int type = 0; - if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) { - type |= PortType::Control; - } else { - type |= PortType::Audio; - } - if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) { - type |= PortType::Input; - } else { - type |= PortType::Output; - } + int type = 0; + if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[p])) { + type |= PortType::Control; + } else { + type |= PortType::Audio; + } + if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[p])) { + type |= PortType::Input; + } else { + type |= PortType::Output; + } - list.push_back(QString("%1").arg(p)); - list.push_back(descriptor->PortNames[p]); - list.push_back(QString("%1").arg(type)); - list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p))); - list.push_back(QString("%1").arg(getPortMinimum(descriptor, p))); - list.push_back(QString("%1").arg(getPortMaximum(descriptor, p))); - list.push_back(QString("%1").arg(getPortDefault(descriptor, p))); - } + list.push_back(QString("%1").arg(p)); + list.push_back(descriptor->PortNames[p]); + list.push_back(QString("%1").arg(type)); + list.push_back(QString("%1").arg(getPortDisplayHint(descriptor, p))); + list.push_back(QString("%1").arg(getPortMinimum(descriptor, p))); + list.push_back(QString("%1").arg(getPortMaximum(descriptor, p))); + list.push_back(QString("%1").arg(getPortDefault(descriptor, p))); + } } unloadUnusedLibraries(); } - + const RealTimePluginDescriptor * LADSPAPluginFactory::getPluginDescriptor(QString identifier) const { @@ -148,20 +155,20 @@ LADSPAPluginFactory::getPortMinimum(const LADSPA_Descriptor *descriptor, int port) { LADSPA_PortRangeHintDescriptor d = - descriptor->PortRangeHints[port].HintDescriptor; + descriptor->PortRangeHints[port].HintDescriptor; float minimum = 0.f; - + if (LADSPA_IS_HINT_BOUNDED_BELOW(d)) { - float lb = descriptor->PortRangeHints[port].LowerBound; - minimum = lb; + float lb = descriptor->PortRangeHints[port].LowerBound; + minimum = lb; } else if (LADSPA_IS_HINT_BOUNDED_ABOVE(d)) { - float ub = descriptor->PortRangeHints[port].UpperBound; - minimum = std::min(0.f, ub - 1.f); + float ub = descriptor->PortRangeHints[port].UpperBound; + minimum = std::min(0.f, ub - 1.f); } if (LADSPA_IS_HINT_SAMPLE_RATE(d)) { - minimum = float(minimum * m_sampleRate); + minimum = float(minimum * m_sampleRate); } if (LADSPA_IS_HINT_LOGARITHMIC(d)) { @@ -175,20 +182,20 @@ LADSPAPluginFactory::getPortMaximum(const LADSPA_Descriptor *descriptor, int port) { LADSPA_PortRangeHintDescriptor d = - descriptor->PortRangeHints[port].HintDescriptor; + descriptor->PortRangeHints[port].HintDescriptor; float maximum = 1.f; if (LADSPA_IS_HINT_BOUNDED_ABOVE(d)) { - float ub = descriptor->PortRangeHints[port].UpperBound; - maximum = ub; + float ub = descriptor->PortRangeHints[port].UpperBound; + maximum = ub; } else { - float lb = descriptor->PortRangeHints[port].LowerBound; - maximum = lb + 1.f; + float lb = descriptor->PortRangeHints[port].LowerBound; + maximum = lb + 1.f; } if (LADSPA_IS_HINT_SAMPLE_RATE(d)) { - maximum = float(maximum * m_sampleRate); + maximum = float(maximum * m_sampleRate); } return maximum; @@ -202,19 +209,19 @@ float deft; if (m_portDefaults.find(descriptor->UniqueID) != - m_portDefaults.end()) { - if (m_portDefaults[descriptor->UniqueID].find(port) != - m_portDefaults[descriptor->UniqueID].end()) { + m_portDefaults.end()) { + if (m_portDefaults[descriptor->UniqueID].find(port) != + m_portDefaults[descriptor->UniqueID].end()) { - deft = m_portDefaults[descriptor->UniqueID][port]; - if (deft < minimum) deft = minimum; - if (deft > maximum) deft = maximum; - return deft; - } + deft = m_portDefaults[descriptor->UniqueID][port]; + if (deft < minimum) deft = minimum; + if (deft > maximum) deft = maximum; + return deft; + } } LADSPA_PortRangeHintDescriptor d = - descriptor->PortRangeHints[port].HintDescriptor; + descriptor->PortRangeHints[port].HintDescriptor; bool logarithmic = LADSPA_IS_HINT_LOGARITHMIC(d); @@ -230,61 +237,61 @@ // SVDEBUG << "LADSPAPluginFactory::getPortDefault: hint = " << d << endl; if (!LADSPA_IS_HINT_HAS_DEFAULT(d)) { - - deft = minimum; - + + deft = minimum; + } else if (LADSPA_IS_HINT_DEFAULT_MINIMUM(d)) { - - deft = minimum; - + + deft = minimum; + } else if (LADSPA_IS_HINT_DEFAULT_LOW(d)) { - - if (logarithmic) { - deft = powf(10, logmin * 0.75f + logmax * 0.25f); - } else { - deft = minimum * 0.75f + maximum * 0.25f; - } - + + if (logarithmic) { + deft = powf(10, logmin * 0.75f + logmax * 0.25f); + } else { + deft = minimum * 0.75f + maximum * 0.25f; + } + } else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(d)) { - - if (logarithmic) { - deft = powf(10, logmin * 0.5f + logmax * 0.5f); - } else { - deft = minimum * 0.5f + maximum * 0.5f; - } - + + if (logarithmic) { + deft = powf(10, logmin * 0.5f + logmax * 0.5f); + } else { + deft = minimum * 0.5f + maximum * 0.5f; + } + } else if (LADSPA_IS_HINT_DEFAULT_HIGH(d)) { - - if (logarithmic) { - deft = powf(10, logmin * 0.25f + logmax * 0.75f); - } else { - deft = minimum * 0.25f + maximum * 0.75f; - } - + + if (logarithmic) { + deft = powf(10, logmin * 0.25f + logmax * 0.75f); + } else { + deft = minimum * 0.25f + maximum * 0.75f; + } + } else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(d)) { - - deft = maximum; - + + deft = maximum; + } else if (LADSPA_IS_HINT_DEFAULT_0(d)) { - - deft = 0.0; - + + deft = 0.0; + } else if (LADSPA_IS_HINT_DEFAULT_1(d)) { - - deft = 1.0; - + + deft = 1.0; + } else if (LADSPA_IS_HINT_DEFAULT_100(d)) { - - deft = 100.0; - + + deft = 100.0; + } else if (LADSPA_IS_HINT_DEFAULT_440(d)) { - -// deft = 440.0; + +// deft = 440.0; deft = (float)Preferences::getInstance()->getTuningFrequency(); - + } else { - - deft = minimum; + + deft = minimum; } //!!! No -- the min and max have already been multiplied by the rate, @@ -292,7 +299,7 @@ //doesn't want to be multiplied by the rate either // if (LADSPA_IS_HINT_SAMPLE_RATE(d)) { -// deft *= m_sampleRate; +// deft *= m_sampleRate; // } return deft; @@ -316,7 +323,7 @@ LADSPAPluginFactory::getPortDisplayHint(const LADSPA_Descriptor *descriptor, int port) { LADSPA_PortRangeHintDescriptor d = - descriptor->PortRangeHints[port].HintDescriptor; + descriptor->PortRangeHints[port].HintDescriptor; int hint = PortHint::NoHint; if (LADSPA_IS_HINT_TOGGLED(d)) hint |= PortHint::Toggled; @@ -329,11 +336,11 @@ RealTimePluginInstance * LADSPAPluginFactory::instantiatePlugin(QString identifier, - int instrument, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int channels) + int instrument, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int channels) { Profiler profiler("LADSPAPluginFactory::instantiatePlugin"); @@ -341,19 +348,19 @@ if (descriptor) { - LADSPAPluginInstance *instance = - new LADSPAPluginInstance - (this, instrument, identifier, position, sampleRate, blockSize, channels, - descriptor); + LADSPAPluginInstance *instance = + new LADSPAPluginInstance + (this, instrument, identifier, position, sampleRate, blockSize, channels, + descriptor); - m_instances.insert(instance); + m_instances.insert(instance); #ifdef DEBUG_LADSPA_PLUGIN_FACTORY SVDEBUG << "LADSPAPluginFactory::instantiatePlugin(" << identifier << ": now have " << m_instances.size() << " instances" << endl; #endif - return instance; + return instance; } return 0; @@ -361,14 +368,14 @@ void LADSPAPluginFactory::releasePlugin(RealTimePluginInstance *instance, - QString identifier) + QString identifier) { Profiler profiler("LADSPAPluginFactory::releasePlugin"); if (m_instances.find(instance) == m_instances.end()) { - cerr << "WARNING: LADSPAPluginFactory::releasePlugin: Not one of mine!" - << endl; - return; + cerr << "WARNING: LADSPAPluginFactory::releasePlugin: Not one of mine!" + << endl; + return; } QString type, soname, label; @@ -379,16 +386,16 @@ bool stillInUse = false; for (std::set::iterator ii = m_instances.begin(); - ii != m_instances.end(); ++ii) { - QString itype, isoname, ilabel; - PluginIdentifier::parseIdentifier((*ii)->getPluginIdentifier(), itype, isoname, ilabel); - if (isoname == soname) { + ii != m_instances.end(); ++ii) { + QString itype, isoname, ilabel; + PluginIdentifier::parseIdentifier((*ii)->getPluginIdentifier(), itype, isoname, ilabel); + if (isoname == soname) { #ifdef DEBUG_LADSPA_PLUGIN_FACTORY - SVDEBUG << "LADSPAPluginFactory::releasePlugin: dll " << soname << " is still in use for plugin " << ilabel << endl; + SVDEBUG << "LADSPAPluginFactory::releasePlugin: dll " << soname << " is still in use for plugin " << ilabel << endl; #endif - stillInUse = true; - break; - } + stillInUse = true; + break; + } } if (!stillInUse) { @@ -413,32 +420,32 @@ PluginIdentifier::parseIdentifier(identifier, type, soname, label); if (m_libraryHandles.find(soname) == m_libraryHandles.end()) { - loadLibrary(soname); - if (m_libraryHandles.find(soname) == m_libraryHandles.end()) { - cerr << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: loadLibrary failed for " << soname << endl; - return 0; - } + loadLibrary(soname); + if (m_libraryHandles.find(soname) == m_libraryHandles.end()) { + SVCERR << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: loadLibrary failed for " << soname << endl; + return 0; + } } void *libraryHandle = m_libraryHandles[soname]; LADSPA_Descriptor_Function fn = (LADSPA_Descriptor_Function) - DLSYM(libraryHandle, "ladspa_descriptor"); + DLSYM(libraryHandle, "ladspa_descriptor"); if (!fn) { - cerr << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No descriptor function in library " << soname << endl; - return 0; + SVCERR << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No descriptor function in library " << soname << endl; + return 0; } const LADSPA_Descriptor *descriptor = 0; int index = 0; while ((descriptor = fn(index))) { - if (descriptor->Label == label) return descriptor; - ++index; + if (descriptor->Label == label) return descriptor; + ++index; } - cerr << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No such plugin as " << label << " in library " << soname << endl; + SVCERR << "WARNING: LADSPAPluginFactory::getLADSPADescriptor: No such plugin as " << label << " in library " << soname << endl; return 0; } @@ -455,7 +462,7 @@ if (QFileInfo(soName).exists()) { DLERROR(); - cerr << "LADSPAPluginFactory::loadLibrary: Library \"" << soName << "\" exists, but failed to load it" << endl; + SVCERR << "LADSPAPluginFactory::loadLibrary: Library \"" << soName << "\" exists, but failed to load it" << endl; return; } @@ -465,7 +472,7 @@ QString base = QFileInfo(soName).baseName(); for (std::vector::iterator i = pathList.begin(); - i != pathList.end(); ++i) { + i != pathList.end(); ++i) { #ifdef DEBUG_LADSPA_PLUGIN_FACTORY SVDEBUG << "Looking at: " << (*i) << endl; @@ -477,7 +484,7 @@ if (QFileInfo(dir.filePath(fileName)).exists()) { #ifdef DEBUG_LADSPA_PLUGIN_FACTORY - cerr << "Loading: " << fileName << endl; + SVDEBUG << "Loading: " << fileName << endl; #endif libraryHandle = DLOPEN(dir.filePath(fileName), RTLD_NOW); if (libraryHandle) { @@ -486,11 +493,11 @@ } } - for (unsigned int j = 0; j < dir.count(); ++j) { + for (unsigned int j = 0; j < dir.count(); ++j) { QString file = dir.filePath(dir[j]); if (QFileInfo(file).baseName() == base) { #ifdef DEBUG_LADSPA_PLUGIN_FACTORY - cerr << "Loading: " << file << endl; + SVDEBUG << "Loading: " << file << endl; #endif libraryHandle = DLOPEN(file, RTLD_NOW); if (libraryHandle) { @@ -501,7 +508,7 @@ } } - cerr << "LADSPAPluginFactory::loadLibrary: Failed to locate plugin library \"" << soName << "\"" << endl; + SVCERR << "LADSPAPluginFactory::loadLibrary: Failed to locate plugin library \"" << soName << "\"" << endl; } void @@ -509,9 +516,9 @@ { LibraryHandleMap::iterator li = m_libraryHandles.find(soName); if (li != m_libraryHandles.end()) { -// SVDEBUG << "unloading " << soname << endl; - DLCLOSE(m_libraryHandles[soName]); - m_libraryHandles.erase(li); +// SVDEBUG << "unloading " << soname << endl; + DLCLOSE(m_libraryHandles[soName]); + m_libraryHandles.erase(li); } } @@ -521,26 +528,26 @@ std::vector toUnload; for (LibraryHandleMap::iterator i = m_libraryHandles.begin(); - i != m_libraryHandles.end(); ++i) { + i != m_libraryHandles.end(); ++i) { - bool stillInUse = false; + bool stillInUse = false; - for (std::set::iterator ii = m_instances.begin(); - ii != m_instances.end(); ++ii) { + for (std::set::iterator ii = m_instances.begin(); + ii != m_instances.end(); ++ii) { - QString itype, isoname, ilabel; - PluginIdentifier::parseIdentifier((*ii)->getPluginIdentifier(), itype, isoname, ilabel); - if (isoname == i->first) { - stillInUse = true; - break; - } - } + QString itype, isoname, ilabel; + PluginIdentifier::parseIdentifier((*ii)->getPluginIdentifier(), itype, isoname, ilabel); + if (isoname == i->first) { + stillInUse = true; + break; + } + } - if (!stillInUse) toUnload.push_back(i->first); + if (!stillInUse) toUnload.push_back(i->first); } for (std::vector::iterator i = toUnload.begin(); - i != toUnload.end(); ++i) { + i != toUnload.end(); ++i) { if (*i != PluginIdentifier::BUILTIN_PLUGIN_SONAME) { unloadLibrary(*i); } @@ -560,42 +567,42 @@ LADSPAPluginFactory::getPluginPath() { std::vector pathList; - std::string path; + string path; - char *cpath = getenv("LADSPA_PATH"); - if (cpath) path = cpath; + (void)getEnvUtf8("LADSPA_PATH", path); if (path == "") { path = DEFAULT_LADSPA_PATH; - char *home = getenv("HOME"); - if (home) { - std::string::size_type f; - while ((f = path.find("$HOME")) != std::string::npos && + string home; + if (getEnvUtf8("HOME", home)) { + string::size_type f; + while ((f = path.find("$HOME")) != string::npos && f < path.length()) { path.replace(f, 5, home); } } #ifdef _WIN32 - const char *pfiles = getenv("ProgramFiles"); - if (!pfiles) pfiles = "C:\\Program Files"; - { - std::string::size_type f; - while ((f = path.find("%ProgramFiles%")) != std::string::npos && + string pfiles; + if (!getEnvUtf8("ProgramFiles", pfiles)) { + pfiles = "C:\\Program Files"; + } + + string::size_type f; + while ((f = path.find("%ProgramFiles%")) != string::npos && f < path.length()) { path.replace(f, 14, pfiles); } - } #endif } - std::string::size_type index = 0, newindex = 0; + string::size_type index = 0, newindex = 0; while ((newindex = path.find(PATH_SEPARATOR, index)) < path.size()) { - pathList.push_back(path.substr(index, newindex - index).c_str()); - index = newindex + 1; + pathList.push_back(path.substr(index, newindex - index).c_str()); + index = newindex + 1; } pathList.push_back(path.substr(index).c_str()); @@ -616,8 +623,8 @@ lrdfPaths.push_back("/usr/share/ladspa/rdf"); for (std::vector::iterator i = pathList.begin(); - i != pathList.end(); ++i) { - lrdfPaths.push_back(*i + "/rdf"); + i != pathList.end(); ++i) { + lrdfPaths.push_back(*i + "/rdf"); } baseUri = LADSPA_BASE; @@ -636,10 +643,10 @@ std::vector pathList = getPluginPath(); // SVDEBUG << "LADSPAPluginFactory::discoverPlugins - " -// << "discovering plugins; path is "; +// << "discovering plugins; path is "; // for (std::vector::iterator i = pathList.begin(); -// i != pathList.end(); ++i) { -// SVDEBUG << "[" << i-<< "] "; +// i != pathList.end(); ++i) { +// SVDEBUG << "[" << i-<< "] "; // } // SVDEBUG << endl; @@ -652,17 +659,17 @@ bool haveSomething = false; for (size_t i = 0; i < lrdfPaths.size(); ++i) { - QDir dir(lrdfPaths[i], "*.rdf;*.rdfs"); - for (unsigned int j = 0; j < dir.count(); ++j) { - if (!lrdf_read_file(QString("file:" + lrdfPaths[i] + "/" + dir[j]).toStdString().c_str())) { -// cerr << "LADSPAPluginFactory: read RDF file " << (lrdfPaths[i] + "/" + dir[j]) << endl; - haveSomething = true; - } - } + QDir dir(lrdfPaths[i], "*.rdf;*.rdfs"); + for (unsigned int j = 0; j < dir.count(); ++j) { + if (!lrdf_read_file(QString("file:" + lrdfPaths[i] + "/" + dir[j]).toStdString().c_str())) { +// cerr << "LADSPAPluginFactory: read RDF file " << (lrdfPaths[i] + "/" + dir[j]) << endl; + haveSomething = true; + } + } } if (haveSomething) { - generateTaxonomy(baseUri + "Plugin", ""); + generateTaxonomy(baseUri + "Plugin", ""); } #endif // HAVE_LRDF @@ -688,11 +695,11 @@ } LADSPA_Descriptor_Function fn = (LADSPA_Descriptor_Function) - DLSYM(libraryHandle, "ladspa_descriptor"); + DLSYM(libraryHandle, "ladspa_descriptor"); if (!fn) { - cerr << "WARNING: LADSPAPluginFactory::discoverPlugins: No descriptor function in " << soname << endl; - return; + cerr << "WARNING: LADSPAPluginFactory::discoverPlugins: No descriptor function in " << soname << endl; + return; } const LADSPA_Descriptor *descriptor = 0; @@ -712,67 +719,67 @@ rtd->audioOutputPortCount = 0; rtd->controlOutputPortCount = 0; - QString identifier = PluginIdentifier::createIdentifier - ("ladspa", soname, descriptor->Label); + QString identifier = PluginIdentifier::createIdentifier + ("ladspa", soname, descriptor->Label); #ifdef HAVE_LRDF - char *def_uri = 0; - lrdf_defaults *defs = 0; - + char *def_uri = 0; + lrdf_defaults *defs = 0; + if (m_lrdfTaxonomy[descriptor->UniqueID] != "") { m_taxonomy[identifier] = m_lrdfTaxonomy[descriptor->UniqueID]; // cerr << "set id \"" << identifier << "\" to cat \"" << m_taxonomy[identifier] << "\" from LRDF" << endl; // cout << identifier << "::" << m_taxonomy[identifier] << endl; } - QString category = m_taxonomy[identifier]; - - if (category == "") { - std::string name = rtd->name; - if (name.length() > 4 && - name.substr(name.length() - 4) == " VST") { - category = "VST effects"; - m_taxonomy[identifier] = category; - } - } - + QString category = m_taxonomy[identifier]; + + if (category == "") { + string name = rtd->name; + if (name.length() > 4 && + name.substr(name.length() - 4) == " VST") { + category = "VST effects"; + m_taxonomy[identifier] = category; + } + } + rtd->category = category.toStdString(); -// cerr << "Plugin id is " << descriptor->UniqueID -// << ", category is \"" << (category ? category : QString("(none)")) -// << "\", name is " << descriptor->Name -// << ", label is " << descriptor->Label -// << endl; - - def_uri = lrdf_get_default_uri(descriptor->UniqueID); - if (def_uri) { - defs = lrdf_get_setting_values(def_uri); - } +// cerr << "Plugin id is " << descriptor->UniqueID +// << ", category is \"" << (category ? category : QString("(none)")) +// << "\", name is " << descriptor->Name +// << ", label is " << descriptor->Label +// << endl; + + def_uri = lrdf_get_default_uri(descriptor->UniqueID); + if (def_uri) { + defs = lrdf_get_setting_values(def_uri); + } - unsigned int controlPortNumber = 1; - - for (int i = 0; i < (int)descriptor->PortCount; i++) { - - if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) { - - if (def_uri && defs) { - - for (unsigned int j = 0; j < defs->count; j++) { - if (defs->items[j].pid == controlPortNumber) { -// cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << descriptor->PortNames[i] << endl; - m_portDefaults[descriptor->UniqueID][i] = - defs->items[j].value; - } - } - } - - ++controlPortNumber; - } - } + unsigned int controlPortNumber = 1; + + for (int i = 0; i < (int)descriptor->PortCount; i++) { + + if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) { + + if (def_uri && defs) { + + for (unsigned int j = 0; j < defs->count; j++) { + if (defs->items[j].pid == controlPortNumber) { +// cerr << "Default for this port (" << defs->items[j].pid << ", " << defs->items[j].label << ") is " << defs->items[j].value << "; applying this to port number " << i << " with name " << descriptor->PortNames[i] << endl; + m_portDefaults[descriptor->UniqueID][i] = + defs->items[j].value; + } + } + } + + ++controlPortNumber; + } + } #endif // HAVE_LRDF - for (int i = 0; i < (int)descriptor->PortCount; i++) { - if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) { + for (int i = 0; i < (int)descriptor->PortCount; i++) { + if (LADSPA_IS_PORT_CONTROL(descriptor->PortDescriptors[i])) { if (LADSPA_IS_PORT_INPUT(descriptor->PortDescriptors[i])) { ++rtd->parameterCount; } else { @@ -792,11 +799,13 @@ } } - m_identifiers.push_back(identifier); + m_identifiers.push_back(identifier); + + m_libraries[identifier] = soname; m_rtDescriptors[identifier] = rtd; - ++index; + ++index; } if (DLCLOSE(libraryHandle) != 0) { @@ -812,44 +821,44 @@ std::vector path; for (size_t i = 0; i < pluginPath.size(); ++i) { - if (pluginPath[i].contains("/lib/")) { - QString p(pluginPath[i]); + if (pluginPath[i].contains("/lib/")) { + QString p(pluginPath[i]); path.push_back(p); - p.replace("/lib/", "/share/"); - path.push_back(p); -// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: path element " << p << endl; - } - path.push_back(pluginPath[i]); -// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: path element " << pluginPath[i] << endl; + p.replace("/lib/", "/share/"); + path.push_back(p); +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: path element " << p << endl; + } + path.push_back(pluginPath[i]); +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: path element " << pluginPath[i] << endl; } for (size_t i = 0; i < path.size(); ++i) { - QDir dir(path[i], "*.cat"); + QDir dir(path[i], "*.cat"); -// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl; - for (unsigned int j = 0; j < dir.count(); ++j) { +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl; + for (unsigned int j = 0; j < dir.count(); ++j) { - QFile file(path[i] + "/" + dir[j]); + QFile file(path[i] + "/" + dir[j]); -// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl; +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl; - if (file.open(QIODevice::ReadOnly)) { -// cerr << "...opened" << endl; - QTextStream stream(&file); - QString line; + if (file.open(QIODevice::ReadOnly)) { +// cerr << "...opened" << endl; + QTextStream stream(&file); + QString line; - while (!stream.atEnd()) { - line = stream.readLine(); -// cerr << "line is: \"" << line << "\"" << endl; - QString id = PluginIdentifier::canonicalise + while (!stream.atEnd()) { + line = stream.readLine(); +// cerr << "line is: \"" << line << "\"" << endl; + QString id = PluginIdentifier::canonicalise (line.section("::", 0, 0)); - QString cat = line.section("::", 1, 1); - m_taxonomy[id] = cat; -// cerr << "set id \"" << id << "\" to cat \"" << cat << "\"" << endl; - } - } - } + QString cat = line.section("::", 1, 1); + m_taxonomy[id] = cat; +// cerr << "set id \"" << id << "\" to cat \"" << cat << "\"" << endl; + } + } + } } } @@ -860,21 +869,21 @@ lrdf_uris *uris = lrdf_get_instances(uri.toStdString().c_str()); if (uris != NULL) { - for (unsigned int i = 0; i < uris->count; ++i) { - m_lrdfTaxonomy[lrdf_get_uid(uris->items[i])] = base; - } - lrdf_free_uris(uris); + for (unsigned int i = 0; i < uris->count; ++i) { + m_lrdfTaxonomy[lrdf_get_uid(uris->items[i])] = base; + } + lrdf_free_uris(uris); } uris = lrdf_get_subclasses(uri.toStdString().c_str()); if (uris != NULL) { - for (unsigned int i = 0; i < uris->count; ++i) { - char *label = lrdf_get_label(uris->items[i]); - generateTaxonomy(uris->items[i], - base + (base.length() > 0 ? " > " : "") + label); - } - lrdf_free_uris(uris); + for (unsigned int i = 0; i < uris->count; ++i) { + char *label = lrdf_get_label(uris->items[i]); + generateTaxonomy(uris->items[i], + base + (base.length() > 0 ? " > " : "") + label); + } + lrdf_free_uris(uris); } #else // avoid unused parameter diff -r d4a28d1479a8 -r 710e6250a401 plugin/LADSPAPluginFactory.h --- a/plugin/LADSPAPluginFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/LADSPAPluginFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -47,20 +47,24 @@ virtual const RealTimePluginDescriptor *getPluginDescriptor(QString identifier) const; virtual RealTimePluginInstance *instantiatePlugin(QString identifier, - int clientId, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int channels); + int clientId, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int channels); virtual QString getPluginCategory(QString identifier); + virtual QString getPluginLibraryPath(QString identifier); + float getPortMinimum(const LADSPA_Descriptor *, int port); float getPortMaximum(const LADSPA_Descriptor *, int port); float getPortDefault(const LADSPA_Descriptor *, int port); float getPortQuantization(const LADSPA_Descriptor *, int port); int getPortDisplayHint(const LADSPA_Descriptor *, int port); + static std::vector getPluginPath(); + protected: LADSPAPluginFactory(); friend class RealTimePluginFactory; @@ -69,8 +73,6 @@ return PluginScan::LADSPAPlugin; } - virtual std::vector getPluginPath(); - virtual std::vector getLRDFPath(QString &baseUri); virtual void discoverPluginsFrom(QString soName); @@ -86,6 +88,7 @@ void unloadUnusedLibraries(); std::vector m_identifiers; + std::map m_libraries; // identifier -> full file path std::map m_rtDescriptors; std::map m_taxonomy; diff -r d4a28d1479a8 -r 710e6250a401 plugin/LADSPAPluginInstance.cpp --- a/plugin/LADSPAPluginInstance.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/LADSPAPluginInstance.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -34,12 +34,12 @@ LADSPAPluginInstance::LADSPAPluginInstance(RealTimePluginFactory *factory, - int clientId, - QString identifier, + int clientId, + QString identifier, int position, - sv_samplerate_t sampleRate, - int blockSize, - int idealChannelCount, + sv_samplerate_t sampleRate, + int blockSize, + int idealChannelCount, const LADSPA_Descriptor* descriptor) : RealTimePluginInstance(factory, identifier), m_client(clientId), @@ -67,18 +67,18 @@ } for (size_t i = 0; i < m_instanceCount * m_audioPortsIn.size(); ++i) { - m_inputBuffers[i] = new sample_t[blockSize]; + m_inputBuffers[i] = new sample_t[blockSize]; } for (size_t i = 0; i < m_instanceCount * m_audioPortsOut.size(); ++i) { - m_outputBuffers[i] = new sample_t[blockSize]; + m_outputBuffers[i] = new sample_t[blockSize]; } m_ownBuffers = true; instantiate(sampleRate); if (isOK()) { - connectPorts(); - activate(); + connectPorts(); + activate(); } } @@ -222,7 +222,7 @@ { #ifdef DEBUG_LADSPA SVDEBUG << "LADSPAPluginInstance::init(" << idealChannelCount << "): plugin has " - << m_descriptor->PortCount << " ports" << endl; + << m_descriptor->PortCount << " ports" << endl; #endif // Discover ports numbers and identities @@ -233,44 +233,44 @@ if (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[i])) { #ifdef DEBUG_LADSPA - SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is audio in" << endl; + SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is audio in" << endl; #endif m_audioPortsIn.push_back(i); - } else { + } else { #ifdef DEBUG_LADSPA - SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is audio out" << endl; + SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is audio out" << endl; #endif m_audioPortsOut.push_back(i); - } + } } else if (LADSPA_IS_PORT_CONTROL(m_descriptor->PortDescriptors[i])) { - if (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[i])) { + if (LADSPA_IS_PORT_INPUT(m_descriptor->PortDescriptors[i])) { #ifdef DEBUG_LADSPA - SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is control in" << endl; + SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is control in" << endl; #endif - LADSPA_Data *data = new LADSPA_Data(0.0); - m_controlPortsIn.push_back( + LADSPA_Data *data = new LADSPA_Data(0.0); + m_controlPortsIn.push_back( std::pair(i, data)); - } else { + } else { #ifdef DEBUG_LADSPA - SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is control out" << endl; + SVDEBUG << "LADSPAPluginInstance::init: port " << i << " is control out" << endl; #endif - LADSPA_Data *data = new LADSPA_Data(0.0); - m_controlPortsOut.push_back( + LADSPA_Data *data = new LADSPA_Data(0.0); + m_controlPortsOut.push_back( std::pair(i, data)); - if (!strcmp(m_descriptor->PortNames[i], "latency") || - !strcmp(m_descriptor->PortNames[i], "_latency")) { + if (!strcmp(m_descriptor->PortNames[i], "latency") || + !strcmp(m_descriptor->PortNames[i], "_latency")) { #ifdef DEBUG_LADSPA - cerr << "Wooo! We have a latency port!" << endl; + cerr << "Wooo! We have a latency port!" << endl; #endif - m_latencyPort = data; - } + m_latencyPort = data; + } - } + } } #ifdef DEBUG_LADSPA else @@ -282,10 +282,10 @@ m_instanceCount = 1; if (idealChannelCount > 0) { - if (m_audioPortsIn.size() == 1) { - // mono plugin: duplicate it if need be - m_instanceCount = idealChannelCount; - } + if (m_audioPortsIn.size() == 1) { + // mono plugin: duplicate it if need be + m_instanceCount = idealChannelCount; + } } } @@ -293,7 +293,7 @@ LADSPAPluginInstance::getLatency() { if (m_latencyPort) { - if (!m_run) { + if (!m_run) { for (int i = 0; i < getAudioInputCount(); ++i) { for (int j = 0; j < m_blockSize; ++j) { m_inputBuffers[i][j] = 0.f; @@ -301,7 +301,7 @@ } run(Vamp::RealTime::zeroTime); } - if (*m_latencyPort > 0) return (sv_frame_t)*m_latencyPort; + if (*m_latencyPort > 0) return (sv_frame_t)*m_latencyPort; } return 0; } @@ -310,8 +310,8 @@ LADSPAPluginInstance::silence() { if (isOK()) { - deactivate(); - activate(); + deactivate(); + activate(); } } @@ -319,12 +319,12 @@ LADSPAPluginInstance::setIdealChannelCount(int channels) { if (m_audioPortsIn.size() != 1 || channels == m_instanceCount) { - silence(); - return; + silence(); + return; } if (isOK()) { - deactivate(); + deactivate(); } //!!! don't we need to reallocate inputBuffers and outputBuffers? @@ -333,8 +333,8 @@ m_instanceCount = channels; instantiate(m_sampleRate); if (isOK()) { - connectPorts(); - activate(); + connectPorts(); + activate(); } } @@ -346,7 +346,7 @@ #endif if (m_instanceHandles.size() != 0) { // "isOK()" - deactivate(); + deactivate(); } cleanup(); @@ -361,15 +361,15 @@ m_controlPortsOut.clear(); if (m_ownBuffers) { - for (size_t i = 0; i < m_instanceCount * m_audioPortsIn.size(); ++i) { - delete[] m_inputBuffers[i]; - } - for (size_t i = 0; i < m_instanceCount * m_audioPortsOut.size(); ++i) { - delete[] m_outputBuffers[i]; - } + for (size_t i = 0; i < m_instanceCount * m_audioPortsIn.size(); ++i) { + delete[] m_inputBuffers[i]; + } + for (size_t i = 0; i < m_instanceCount * m_audioPortsOut.size(); ++i) { + delete[] m_outputBuffers[i]; + } - delete[] m_inputBuffers; - delete[] m_outputBuffers; + delete[] m_inputBuffers; + delete[] m_outputBuffers; } m_audioPortsIn.clear(); @@ -388,10 +388,10 @@ #endif if (!m_descriptor->instantiate) { - cerr << "Bad plugin: plugin id " << m_descriptor->UniqueID - << ":" << m_descriptor->Label - << " has no instantiate method!" << endl; - return; + cerr << "Bad plugin: plugin id " << m_descriptor->UniqueID + << ":" << m_descriptor->Label + << " has no instantiate method!" << endl; + return; } unsigned long pluginRate = (unsigned long)(sampleRate); @@ -402,8 +402,8 @@ } for (int i = 0; i < m_instanceCount; ++i) { - m_instanceHandles.push_back - (m_descriptor->instantiate(m_descriptor, pluginRate)); + m_instanceHandles.push_back + (m_descriptor->instantiate(m_descriptor, pluginRate)); } } @@ -413,8 +413,8 @@ if (!m_descriptor || !m_descriptor->activate) return; for (std::vector::iterator hi = m_instanceHandles.begin(); - hi != m_instanceHandles.end(); ++hi) { - m_descriptor->activate(*hi); + hi != m_instanceHandles.end(); ++hi) { + m_descriptor->activate(*hi); } } @@ -430,44 +430,44 @@ int inbuf = 0, outbuf = 0; for (std::vector::iterator hi = m_instanceHandles.begin(); - hi != m_instanceHandles.end(); ++hi) { + hi != m_instanceHandles.end(); ++hi) { - for (unsigned int i = 0; i < m_audioPortsIn.size(); ++i) { - m_descriptor->connect_port(*hi, - m_audioPortsIn[i], - (LADSPA_Data *)m_inputBuffers[inbuf]); - ++inbuf; - } + for (unsigned int i = 0; i < m_audioPortsIn.size(); ++i) { + m_descriptor->connect_port(*hi, + m_audioPortsIn[i], + (LADSPA_Data *)m_inputBuffers[inbuf]); + ++inbuf; + } - for (unsigned int i = 0; i < m_audioPortsOut.size(); ++i) { - m_descriptor->connect_port(*hi, - m_audioPortsOut[i], - (LADSPA_Data *)m_outputBuffers[outbuf]); - ++outbuf; - } + for (unsigned int i = 0; i < m_audioPortsOut.size(); ++i) { + m_descriptor->connect_port(*hi, + m_audioPortsOut[i], + (LADSPA_Data *)m_outputBuffers[outbuf]); + ++outbuf; + } - // If there is more than one instance, they all share the same - // control port ins (and outs, for the moment, because we - // don't actually do anything with the outs anyway -- but they - // do have to be connected as the plugin can't know if they're - // not and will write to them anyway). + // If there is more than one instance, they all share the same + // control port ins (and outs, for the moment, because we + // don't actually do anything with the outs anyway -- but they + // do have to be connected as the plugin can't know if they're + // not and will write to them anyway). - for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) { - m_descriptor->connect_port(*hi, - m_controlPortsIn[i].first, - m_controlPortsIn[i].second); + for (unsigned int i = 0; i < m_controlPortsIn.size(); ++i) { + m_descriptor->connect_port(*hi, + m_controlPortsIn[i].first, + m_controlPortsIn[i].second); if (f) { float defaultValue = f->getPortDefault (m_descriptor, m_controlPortsIn[i].first); *m_controlPortsIn[i].second = defaultValue; } - } + } - for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i) { - m_descriptor->connect_port(*hi, - m_controlPortsOut[i].first, - m_controlPortsOut[i].second); - } + for (unsigned int i = 0; i < m_controlPortsOut.size(); ++i) { + m_descriptor->connect_port(*hi, + m_controlPortsOut[i].first, + m_controlPortsOut[i].second); + } } } @@ -486,12 +486,12 @@ LADSPAPluginFactory *f = dynamic_cast(m_factory); if (f) { - if (value < f->getPortMinimum(m_descriptor, portNumber)) { - value = f->getPortMinimum(m_descriptor, portNumber); - } - if (value > f->getPortMaximum(m_descriptor, portNumber)) { - value = f->getPortMaximum(m_descriptor, portNumber); - } + if (value < f->getPortMinimum(m_descriptor, portNumber)) { + value = f->getPortMinimum(m_descriptor, portNumber); + } + if (value > f->getPortMaximum(m_descriptor, portNumber)) { + value = f->getPortMaximum(m_descriptor, portNumber); + } } (*m_controlPortsIn[parameter].second) = value; @@ -518,9 +518,9 @@ LADSPAPluginFactory *f = dynamic_cast(m_factory); if (f) { - return f->getPortDefault(m_descriptor, m_controlPortsIn[parameter].first); + return f->getPortDefault(m_descriptor, m_controlPortsIn[parameter].first); } else { - return 0.0f; + return 0.0f; } } @@ -531,9 +531,9 @@ LADSPAPluginFactory *f = dynamic_cast(m_factory); if (f) { - return f->getPortDisplayHint(m_descriptor, m_controlPortsIn[parameter].first); + return f->getPortDisplayHint(m_descriptor, m_controlPortsIn[parameter].first); } else { - return PortHint::NoHint; + return PortHint::NoHint; } } @@ -545,7 +545,7 @@ if (count == 0) count = m_blockSize; for (std::vector::iterator hi = m_instanceHandles.begin(); - hi != m_instanceHandles.end(); ++hi) { + hi != m_instanceHandles.end(); ++hi) { m_descriptor->run(*hi, count); } @@ -559,7 +559,7 @@ if (!m_descriptor || !m_descriptor->deactivate) return; for (std::vector::iterator hi = m_instanceHandles.begin(); - hi != m_instanceHandles.end(); ++hi) { + hi != m_instanceHandles.end(); ++hi) { m_descriptor->deactivate(*hi); } } @@ -570,15 +570,15 @@ if (!m_descriptor) return; if (!m_descriptor->cleanup) { - cerr << "Bad plugin: plugin id " << m_descriptor->UniqueID - << ":" << m_descriptor->Label - << " has no cleanup method!" << endl; - return; + cerr << "Bad plugin: plugin id " << m_descriptor->UniqueID + << ":" << m_descriptor->Label + << " has no cleanup method!" << endl; + return; } for (std::vector::iterator hi = m_instanceHandles.begin(); - hi != m_instanceHandles.end(); ++hi) { - m_descriptor->cleanup(*hi); + hi != m_instanceHandles.end(); ++hi) { + m_descriptor->cleanup(*hi); } m_instanceHandles.clear(); diff -r d4a28d1479a8 -r 710e6250a401 plugin/LADSPAPluginInstance.h --- a/plugin/LADSPAPluginInstance.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/LADSPAPluginInstance.h Mon Sep 17 13:51:14 2018 +0100 @@ -89,12 +89,12 @@ // Constructor that creates the buffers internally // LADSPAPluginInstance(RealTimePluginFactory *factory, - int client, - QString identifier, + int client, + QString identifier, int position, - sv_samplerate_t sampleRate, - int blockSize, - int idealChannelCount, + sv_samplerate_t sampleRate, + int blockSize, + int idealChannelCount, const LADSPA_Descriptor* descriptor); void init(int idealChannelCount = 0); diff -r d4a28d1479a8 -r 710e6250a401 plugin/NativeVampPluginFactory.cpp --- a/plugin/NativeVampPluginFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/NativeVampPluginFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -95,8 +95,8 @@ QDir::Files | QDir::Readable); for (unsigned int i = 0; i < dir.count(); ++i) { - QString soname = dir.filePath(dir[i]); - candidates.push_back({ soname, "" }); + QString libpath = dir.filePath(dir[i]); + candidates.push_back({ libpath, "" }); } } @@ -121,14 +121,14 @@ for (auto candidate : candidates) { - QString soname = candidate.libraryPath; + QString libpath = candidate.libraryPath; - SVDEBUG << "INFO: Considering candidate Vamp plugin library " << soname << endl; + SVDEBUG << "INFO: Considering candidate Vamp plugin library " << libpath << endl; - void *libraryHandle = DLOPEN(soname, RTLD_LAZY | RTLD_LOCAL); + void *libraryHandle = DLOPEN(libpath, RTLD_LAZY | RTLD_LOCAL); if (!libraryHandle) { - SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to load library " << soname << ": " << DLERROR() << endl; + SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to load library " << libpath << ": " << DLERROR() << endl; continue; } @@ -136,9 +136,9 @@ DLSYM(libraryHandle, "vampGetPluginDescriptor"); if (!fn) { - SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << soname << endl; + SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: No descriptor function in " << libpath << endl; if (DLCLOSE(libraryHandle) != 0) { - SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << libpath << endl; } continue; } @@ -157,12 +157,12 @@ if (known.find(descriptor->identifier) != known.end()) { SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Plugin library " - << soname - << " returns the same plugin identifier \"" - << descriptor->identifier << "\" at indices " - << known[descriptor->identifier] << " and " - << index << endl; - SVDEBUG << "NativeVampPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; + << libpath + << " returns the same plugin identifier \"" + << descriptor->identifier << "\" at indices " + << known[descriptor->identifier] << " and " + << index << endl; + SVDEBUG << "NativeVampPluginFactory::getPluginIdentifiers: Avoiding this library (obsolete API?)" << endl; ok = false; break; } else { @@ -179,8 +179,9 @@ while ((descriptor = fn(VAMP_API_VERSION, index))) { QString id = PluginIdentifier::createIdentifier - ("vamp", soname, descriptor->identifier); + ("vamp", libpath, descriptor->identifier); m_identifiers.push_back(id); + m_libraries[id] = libpath; #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE cerr << "NativeVampPluginFactory::getPluginIdentifiers: Found plugin id " << id << " at index " << index << endl; #endif @@ -189,7 +190,7 @@ } if (DLCLOSE(libraryHandle) != 0) { - SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << soname << endl; + SVDEBUG << "WARNING: NativeVampPluginFactory::getPluginIdentifiers: Failed to unload library " << libpath << endl; } } @@ -231,7 +232,7 @@ return file; } - for (unsigned int j = 0; j < dir.count(); ++j) { + for (unsigned int j = 0; j < dir.count(); ++j) { file = dir.filePath(dir[j]); if (QFileInfo(file).baseName() == QFileInfo(soname).baseName()) { @@ -304,7 +305,7 @@ #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE cerr << "NativeVampPluginFactory::instantiatePlugin: Wrong factory for plugin type " << type << endl; #endif - return 0; + return 0; } QString found = findPluginFile(soname); @@ -392,6 +393,12 @@ return m_taxonomy[identifier]; } +QString +NativeVampPluginFactory::getPluginLibraryPath(QString identifier) +{ + return m_libraries[identifier]; +} + void NativeVampPluginFactory::generateTaxonomy() { @@ -399,42 +406,42 @@ vector path; for (size_t i = 0; i < pluginPath.size(); ++i) { - if (pluginPath[i].contains("/lib/")) { - QString p(pluginPath[i]); + if (pluginPath[i].contains("/lib/")) { + QString p(pluginPath[i]); path.push_back(p); - p.replace("/lib/", "/share/"); - path.push_back(p); - } - path.push_back(pluginPath[i]); + p.replace("/lib/", "/share/"); + path.push_back(p); + } + path.push_back(pluginPath[i]); } for (size_t i = 0; i < path.size(); ++i) { - QDir dir(path[i], "*.cat"); + QDir dir(path[i], "*.cat"); -// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl; - for (unsigned int j = 0; j < dir.count(); ++j) { +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: directory " << path[i] << " has " << dir.count() << " .cat files" << endl; + for (unsigned int j = 0; j < dir.count(); ++j) { - QFile file(path[i] + "/" + dir[j]); + QFile file(path[i] + "/" + dir[j]); -// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl; +// SVDEBUG << "LADSPAPluginFactory::generateFallbackCategories: about to open " << (path[i]+ "/" + dir[j]) << endl; - if (file.open(QIODevice::ReadOnly)) { -// cerr << "...opened" << endl; - QTextStream stream(&file); - QString line; + if (file.open(QIODevice::ReadOnly)) { +// cerr << "...opened" << endl; + QTextStream stream(&file); + QString line; - while (!stream.atEnd()) { - line = stream.readLine(); -// cerr << "line is: \"" << line << "\"" << endl; - QString id = PluginIdentifier::canonicalise + while (!stream.atEnd()) { + line = stream.readLine(); +// cerr << "line is: \"" << line << "\"" << endl; + QString id = PluginIdentifier::canonicalise (line.section("::", 0, 0)); - QString cat = line.section("::", 1, 1); - m_taxonomy[id] = cat; -// cerr << "NativeVampPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl; - } - } - } + QString cat = line.section("::", 1, 1); + m_taxonomy[id] = cat; +// cerr << "NativeVampPluginFactory: set id \"" << id << "\" to cat \"" << cat << "\"" << endl; + } + } + } } } diff -r d4a28d1479a8 -r 710e6250a401 plugin/NativeVampPluginFactory.h --- a/plugin/NativeVampPluginFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/NativeVampPluginFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -44,17 +44,17 @@ sv_samplerate_t inputSampleRate) override; - /** - * Get category metadata about a plugin (without instantiating it). - */ virtual QString getPluginCategory(QString identifier) override; + virtual QString getPluginLibraryPath(QString identifier) override; + protected: QMutex m_mutex; std::vector m_pluginPath; std::vector m_identifiers; std::map m_taxonomy; // identifier -> category string std::map m_pluginData; // identifier -> data (created opportunistically) + std::map m_libraries; // identifier -> full file path friend class PluginDeletionNotifyAdapter; void pluginDeleted(Vamp::Plugin *); diff -r d4a28d1479a8 -r 710e6250a401 plugin/PiperVampPluginFactory.cpp --- a/plugin/PiperVampPluginFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/PiperVampPluginFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -28,7 +28,9 @@ #define CAPNP_LITE 1 #endif -#include "vamp-client/AutoPlugin.h" +#include "vamp-client/qt/PiperAutoPlugin.h" +#include "vamp-client/qt/ProcessQtTransport.h" +#include "vamp-client/CapnpRRClient.h" #include #include @@ -41,9 +43,6 @@ #include "base/Profiler.h" #include "base/HelperExecPath.h" -#include "vamp-client/ProcessQtTransport.h" -#include "vamp-client/CapnpRRClient.h" - using namespace std; //#define DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 1 @@ -114,8 +113,7 @@ Profiler profiler("PiperVampPluginFactory::instantiatePlugin"); if (m_origins.find(identifier) == m_origins.end()) { - cerr << "ERROR: No known server for identifier " << identifier << endl; - SVDEBUG << "ERROR: No known server for identifier " << identifier << endl; + SVCERR << "ERROR: No known server for identifier " << identifier << endl; return 0; } @@ -124,10 +122,10 @@ return 0; } - SVDEBUG << "PiperVampPluginFactory: Creating Piper AutoPlugin for server " + SVDEBUG << "PiperVampPluginFactory: Creating PiperAutoPlugin for server " << m_origins[identifier] << ", identifier " << identifier << endl; - auto ap = new piper_vamp::client::AutoPlugin + auto ap = new piper_vamp::client::PiperAutoPlugin (m_origins[identifier].toStdString(), psd.pluginKey, float(inputSampleRate), @@ -162,6 +160,27 @@ } } +QString +PiperVampPluginFactory::getPluginLibraryPath(QString identifier) +{ + // What we want to return here is the file path of the library in + // which the plugin was actually found -- we want to be paranoid + // about that and not just query + // Vamp::HostExt::PluginLoader::getLibraryPathForPlugin to return + // what the SDK thinks the likely location would be (in case our + // search order turns out to have been different) + + QStringList bits = identifier.split(':'); + if (bits.size() > 1) { + QString soname = bits[bits.size() - 2]; + auto i = m_libraries.find(soname); + if (i != m_libraries.end()) { + return i->second; + } + } + return QString(); +} + void PiperVampPluginFactory::populate(QString &errorMessage) { @@ -198,6 +217,10 @@ string soname = QFileInfo(c.libraryPath).baseName().toStdString(); SVDEBUG << "INFO: For tag \"" << tag << "\" giving library " << soname << endl; from.push_back(soname); + QString qsoname = QString::fromStdString(soname); + if (m_libraries.find(qsoname) == m_libraries.end()) { + m_libraries[qsoname] = c.libraryPath; + } } } @@ -230,8 +253,8 @@ piper_vamp::ListResponse resp; try { - resp = client.listPluginData(req); - } catch (piper_vamp::client::ServerCrashed) { + resp = client.list(req); + } catch (const piper_vamp::client::ServerCrashed &) { SVDEBUG << "PiperVampPluginFactory: Piper server crashed" << endl; errorMessage = QObject::tr ("External plugin host exited unexpectedly while listing plugins"); diff -r d4a28d1479a8 -r 710e6250a401 plugin/PiperVampPluginFactory.h --- a/plugin/PiperVampPluginFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/PiperVampPluginFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -49,10 +49,13 @@ virtual QString getPluginCategory(QString identifier) override; + virtual QString getPluginLibraryPath(QString identifier) override; + protected: QMutex m_mutex; QList m_servers; // executable file paths std::map m_origins; // plugin identifier -> server path + std::map m_libraries; // soname -> full file path std::map m_pluginData; // identifier -> data std::map m_taxonomy; // identifier -> category string diff -r d4a28d1479a8 -r 710e6250a401 plugin/PluginIdentifier.cpp --- a/plugin/PluginIdentifier.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/PluginIdentifier.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -24,8 +24,8 @@ QString PluginIdentifier::createIdentifier(QString type, - QString soName, - QString label) + QString soName, + QString label) { QString identifier = type + ":" + QFileInfo(soName).baseName() + ":" + label; return identifier; @@ -41,9 +41,9 @@ void PluginIdentifier::parseIdentifier(QString identifier, - QString &type, - QString &soName, - QString &label) + QString &type, + QString &soName, + QString &label) { type = identifier.section(':', 0, 0); soName = identifier.section(':', 1, 1); @@ -61,7 +61,7 @@ if (type1 != type2 || label1 != label2) return false; bool similar = (soName1.section('/', -1).section('.', 0, 0) == - soName2.section('/', -1).section('.', 0, 0)); + soName2.section('/', -1).section('.', 0, 0)); return similar; } diff -r d4a28d1479a8 -r 710e6250a401 plugin/PluginIdentifier.h --- a/plugin/PluginIdentifier.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/PluginIdentifier.h Mon Sep 17 13:51:14 2018 +0100 @@ -36,7 +36,7 @@ static QString canonicalise(QString identifier); static void parseIdentifier(QString identifier, - QString &type, QString &soName, QString &label); + QString &type, QString &soName, QString &label); static bool areIdentifiersSimilar(QString id1, QString id2); diff -r d4a28d1479a8 -r 710e6250a401 plugin/PluginPathSetter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PluginPathSetter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,278 @@ +/* -*- 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 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 "PluginPathSetter.h" + +#include + +#include "RealTimePluginFactory.h" +#include "LADSPAPluginFactory.h" +#include "DSSIPluginFactory.h" + +#include +#include + +#include "system/System.h" +#include "base/Preferences.h" +#include "base/HelperExecPath.h" + +QMutex +PluginPathSetter::m_mutex; + +PluginPathSetter::Paths +PluginPathSetter::m_defaultPaths; + +PluginPathSetter::Paths +PluginPathSetter::m_environmentPaths; + +std::map +PluginPathSetter::m_originalEnvValues; + +PluginPathSetter::TypeKeys +PluginPathSetter::m_supportedKeys; + +using namespace std; + +PluginPathSetter::TypeKeys +PluginPathSetter::getSupportedKeys() +{ + QMutexLocker locker(&m_mutex); + + if (!m_supportedKeys.empty()) { + return m_supportedKeys; + } + + TypeKeys keys; + keys.push_back({ KnownPlugins::VampPlugin, KnownPlugins::FormatNative }); + + bool inProcess = Preferences::getInstance()->getRunPluginsInProcess(); + HelperExecPath hep(inProcess ? + HelperExecPath::NativeArchitectureOnly : + HelperExecPath::AllInstalled); + auto execs = hep.getHelperExecutables("vamp-plugin-load-checker"); + if (execs.size() > 1) { + keys.push_back({ + KnownPlugins::VampPlugin, KnownPlugins::FormatNonNative32Bit }); + } + + keys.push_back({ KnownPlugins::LADSPAPlugin, KnownPlugins::FormatNative }); + keys.push_back({ KnownPlugins::DSSIPlugin, KnownPlugins::FormatNative }); + + m_supportedKeys = keys; + return keys; +} + +// call with mutex held please +PluginPathSetter::Paths +PluginPathSetter::getEnvironmentPathsUncached(const TypeKeys &keys) +{ + Paths paths; + + for (auto k: keys) { + + KnownPlugins kp(k.second); + + auto path = kp.getPathFor(k.first); + QStringList qPath; + for (auto s: path) { + qPath.push_back(QString::fromStdString(s)); + } + + auto var = kp.getPathEnvironmentVariableFor(k.first); + QString qVar = QString::fromStdString(var); + + paths[k] = { qPath, qVar, true }; + } + + return paths; +} + +PluginPathSetter::Paths +PluginPathSetter::getDefaultPaths() +{ + TypeKeys keys = getSupportedKeys(); + + QMutexLocker locker(&m_mutex); + + Paths paths; + + for (auto k: keys) { + + KnownPlugins kp(k.second); + + auto path = kp.getDefaultPathFor(k.first); + QStringList qPath; + for (auto s: path) { + qPath.push_back(QString::fromStdString(s)); + } + + auto var = kp.getPathEnvironmentVariableFor(k.first); + QString qVar = QString::fromStdString(var); + + paths[k] = { qPath, qVar, true }; + } + + return paths; +} + +PluginPathSetter::Paths +PluginPathSetter::getEnvironmentPaths() +{ + TypeKeys keys = getSupportedKeys(); + + QMutexLocker locker(&m_mutex); + + if (!m_environmentPaths.empty()) { + return m_environmentPaths; + } + + m_environmentPaths = getEnvironmentPathsUncached(keys); + return m_environmentPaths; +} + +QString +PluginPathSetter::getSettingTagFor(TypeKey tk) +{ + string tag = KnownPlugins(tk.second).getTagFor(tk.first); + if (tk.second == KnownPlugins::FormatNonNative32Bit) { + tag += "-32"; + } + return QString::fromStdString(tag); +} + +PluginPathSetter::Paths +PluginPathSetter::getPaths() +{ + Paths paths = getEnvironmentPaths(); + + QSettings settings; + settings.beginGroup("Plugins"); + + for (auto p: paths) { + + TypeKey tk = p.first; + + QString settingTag = getSettingTagFor(tk); + + QStringList directories = + settings.value(QString("directories-%1").arg(settingTag), + p.second.directories) + .toStringList(); + QString envVariable = + settings.value(QString("env-variable-%1").arg(settingTag), + p.second.envVariable) + .toString(); + bool useEnvVariable = + settings.value(QString("use-env-variable-%1").arg(settingTag), + p.second.useEnvVariable) + .toBool(); + + string envVarStr = envVariable.toStdString(); + string currentValue; + (void)getEnvUtf8(envVarStr, currentValue); + + if (currentValue != "" && useEnvVariable) { + directories = QString::fromStdString(currentValue).split( +#ifdef Q_OS_WIN + ";" +#else + ":" +#endif + ); + } + + paths[tk] = { directories, envVariable, useEnvVariable }; + } + + settings.endGroup(); + + return paths; +} + +void +PluginPathSetter::savePathSettings(Paths paths) +{ + QSettings settings; + settings.beginGroup("Plugins"); + + for (auto p: paths) { + QString settingTag = getSettingTagFor(p.first); + settings.setValue(QString("directories-%1").arg(settingTag), + p.second.directories); + settings.setValue(QString("env-variable-%1").arg(settingTag), + p.second.envVariable); + settings.setValue(QString("use-env-variable-%1").arg(settingTag), + p.second.useEnvVariable); + } + + settings.endGroup(); +} + +QString +PluginPathSetter::getOriginalEnvironmentValue(QString envVariable) +{ + if (m_originalEnvValues.find(envVariable) != m_originalEnvValues.end()) { + return m_originalEnvValues.at(envVariable); + } else { + return QString(); + } +} + +void +PluginPathSetter::initialiseEnvironmentVariables() +{ + // Set the relevant environment variables from user configuration, + // so that later lookups through the standard APIs will follow the + // same paths as we have in the user config + + // First ensure the default paths have been recorded for later, so + // we don't erroneously re-read them from the environment + // variables we've just set + (void)getDefaultPaths(); + (void)getEnvironmentPaths(); + + Paths paths = getPaths(); + + for (auto p: paths) { + QString envVariable = p.second.envVariable; + string envVarStr = envVariable.toStdString(); + string currentValue; + getEnvUtf8(envVarStr, currentValue); + m_originalEnvValues[envVariable] = QString::fromStdString(currentValue); + if (currentValue != "" && p.second.useEnvVariable) { + // don't override + SVDEBUG << "PluginPathSetter: for environment variable " + << envVariable << ", useEnvVariable setting is false; " + << "leaving current value alone: it is \"" + << currentValue << "\"" << endl; + continue; + } + QString separator = +#ifdef Q_OS_WIN + ";" +#else + ":" +#endif + ; + QString proposedValue = p.second.directories.join(separator); + SVDEBUG << "PluginPathSetter: for environment variable " + << envVariable << ", useEnvVariable setting is true or " + << "variable is currently unset; " + << "changing value from \"" << currentValue + << "\" to setting preference of \"" << proposedValue + << "\"" << endl; + putEnvUtf8(envVarStr, proposedValue.toStdString()); + } +} + diff -r d4a28d1479a8 -r 710e6250a401 plugin/PluginPathSetter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugin/PluginPathSetter.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,85 @@ +/* -*- 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 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 SV_PLUGIN_PATH_SETTER_H +#define SV_PLUGIN_PATH_SETTER_H + +#include +#include +#include + +#include + +#include "checker/knownplugins.h" + +class PluginPathSetter +{ +public: + typedef std::pair TypeKey; + + typedef std::vector TypeKeys; + + struct PathConfig { + QStringList directories; // Actual list of directories arising + // from user settings, environment + // variables, and defaults as + // appropriate + + QString envVariable; // Name of env var, e.g. LADSPA_PATH + + bool useEnvVariable; // True if env variable should override + // any user settings for this + }; + + typedef std::map Paths; + + /// Update *_PATH environment variables from the settings, on + /// application startup. Must be called exactly once, before any + /// of the other functions in this class has been called + static void initialiseEnvironmentVariables(); + + /// Return default values of paths only, without any environment + /// variables or user-defined preferences + static Paths getDefaultPaths(); + + /// Return paths arising from environment variables only, falling + /// back to the defaults, without any user-defined preferences + static Paths getEnvironmentPaths(); + + /// Return paths arising from user settings + environment + /// variables + defaults as appropriate + static Paths getPaths(); + + /// Save the given paths to the settings + static void savePathSettings(Paths paths); + + /// Return the original value observed on startup for the given + /// environment variable, if it is one of the variables used by a + /// known path config. + static QString getOriginalEnvironmentValue(QString envVariable); + +private: + static Paths m_defaultPaths; + static Paths m_environmentPaths; + static std::map m_originalEnvValues; + static TypeKeys m_supportedKeys; + static QMutex m_mutex; + + static std::vector getSupportedKeys(); + static Paths getEnvironmentPathsUncached(const TypeKeys &keys); + static QString getSettingTagFor(TypeKey); +}; + +#endif diff -r d4a28d1479a8 -r 710e6250a401 plugin/PluginScan.cpp --- a/plugin/PluginScan.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/PluginScan.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -18,11 +18,7 @@ #include "base/Preferences.h" #include "base/HelperExecPath.h" -#ifdef HAVE_PLUGIN_CHECKER_HELPER -#include "checker/knownplugins.h" -#else -class KnownPlugins {}; -#endif +#include #include #include @@ -57,6 +53,7 @@ QMutexLocker locker(&m_mutex); clear(); delete m_logger; + SVDEBUG << "PluginScan::~PluginScan completed" << endl; } void @@ -72,7 +69,7 @@ HelperExecPath::NativeArchitectureOnly : HelperExecPath::AllInstalled); - QString helperName("plugin-checker-helper"); + QString helperName("vamp-plugin-load-checker"); auto helpers = hep.getHelperExecutables(helperName); clear(); @@ -91,7 +88,7 @@ for (auto p: helpers) { try { - KnownPlugins *kp = new KnownPlugins + KnownPluginCandidates *kp = new KnownPluginCandidates (p.executable.toStdString(), m_logger); if (m_kp.find(p.tag) != m_kp.end()) { SVDEBUG << "WARNING: PluginScan::scan: Duplicate tag " << p.tag @@ -106,6 +103,7 @@ } } + SVDEBUG << "PluginScan::scan complete" << endl; #endif } @@ -149,7 +147,7 @@ for (auto rec: m_kp) { - KnownPlugins *kp = rec.second; + KnownPluginCandidates *kp = rec.second; auto c = kp->getCandidateLibrariesFor(kpt); @@ -180,6 +178,95 @@ #endif } +#ifdef HAVE_PLUGIN_CHECKER_HELPER +QString +PluginScan::formatFailureReport(QString tag, + std::vector failures) const +{ + int n = int(failures.size()); + int i = 0; + + std::ostringstream os; + + os << "
    "; + for (auto f: failures) { + os << "
  • " + f.library; + + SVDEBUG << "PluginScan::formatFailureReport: tag is \"" << tag + << "\", failure code is " << int(f.code) << ", message is \"" + << f.message << "\"" << endl; + + QString userMessage = QString::fromStdString(f.message); + + switch (f.code) { + + case PluginCheckCode::FAIL_LIBRARY_NOT_FOUND: + userMessage = QObject::tr("Library file could not be opened"); + break; + + case PluginCheckCode::FAIL_WRONG_ARCHITECTURE: + if (tag == "64" || (sizeof(void *) == 8 && tag == "")) { + userMessage = QObject::tr + ("Library has wrong architecture - possibly a 32-bit plugin installed in a 64-bit plugin folder"); + } else if (tag == "32" || (sizeof(void *) == 4 && tag == "")) { + userMessage = QObject::tr + ("Library has wrong architecture - possibly a 64-bit plugin installed in a 32-bit plugin folder"); + } + break; + + case PluginCheckCode::FAIL_DEPENDENCY_MISSING: + userMessage = QObject::tr + ("Library depends on another library that cannot be found: %1") + .arg(userMessage); + break; + + case PluginCheckCode::FAIL_NOT_LOADABLE: + userMessage = QObject::tr + ("Library cannot be loaded: %1").arg(userMessage); + break; + + case PluginCheckCode::FAIL_DESCRIPTOR_MISSING: + userMessage = QObject::tr + ("Not a valid plugin library (no descriptor found)"); + break; + + case PluginCheckCode::FAIL_NO_PLUGINS: + userMessage = QObject::tr + ("Library contains no plugins"); + break; + + case PluginCheckCode::FAIL_OTHER: + if (userMessage == "") { + userMessage = QObject::tr + ("Unknown error"); + } + break; + + case PluginCheckCode::SUCCESS: + // success shouldn't happen here! + break; + } + + os << "
    " + userMessage.toStdString() + ""; + os << "
  • "; + + if (n > 10) { + if (++i == 5) { + os << "
  • "; + os << QObject::tr("... and %n further failure(s)", + "", n - i) + .toStdString(); + os << "
  • "; + break; + } + } + } + os << "
"; + + return QString::fromStdString(os.str()); +} +#endif + QString PluginScan::getStartupFailureReport() const { @@ -188,29 +275,32 @@ QMutexLocker locker(&m_mutex); if (!m_succeeded) { - return QObject::tr("Failed to scan for plugins" - "

Failed to scan for plugins at startup. Possibly " - "the plugin checker helper program was not correctly " + return QObject::tr("Failed to scan for plugins" + "

Failed to scan for plugins at startup. Possibly " + "the plugin checker program was not correctly " "installed alongside %1?

") .arg(QCoreApplication::applicationName()); } if (m_kp.empty()) { - return QObject::tr("Did not scan for plugins" - "

Apparently no scan for plugins was attempted " - "(internal error?)

"); + return QObject::tr("Did not scan for plugins" + "

Apparently no scan for plugins was attempted " + "(internal error?)

"); } QString report; for (auto kp: m_kp) { - report += QString::fromStdString(kp.second->getFailureReport()); + auto failures = kp.second->getFailures(); + if (!failures.empty()) { + report += formatFailureReport(kp.first, failures); + } } if (report == "") { - return report; + return report; } return QObject::tr("Failed to load plugins" - "

Failed to load one or more plugin libraries:

") - + report + "

Failed to load one or more plugin libraries:

") + + report + QObject::tr("

These plugins may be incompatible with the system, " "and will be ignored during this run of %1.

") .arg(QCoreApplication::applicationName()); diff -r d4a28d1479a8 -r 710e6250a401 plugin/PluginScan.h --- a/plugin/PluginScan.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/PluginScan.h Mon Sep 17 13:51:14 2018 +0100 @@ -20,7 +20,11 @@ #include #include -class KnownPlugins; +#ifdef HAVE_PLUGIN_CHECKER_HELPER +#include "checker/knownplugincandidates.h" +#else +class KnownPluginCandidates {}; +#endif class PluginScan { @@ -45,9 +49,9 @@ bool scanSucceeded() const; enum PluginType { - VampPlugin, - LADSPAPlugin, - DSSIPlugin + VampPlugin, + LADSPAPlugin, + DSSIPlugin }; struct Candidate { QString libraryPath; // full path, not just soname @@ -73,9 +77,15 @@ void clear(); +#ifdef HAVE_PLUGIN_CHECKER_HELPER + QString formatFailureReport(QString helperTag, + std::vector) + const; +#endif + mutable QMutex m_mutex; // while scanning; definitely can't multi-thread this - std::map m_kp; // tag -> KnownPlugins client + std::map m_kp; // tag -> KnownPlugins client bool m_succeeded; class Logger; diff -r d4a28d1479a8 -r 710e6250a401 plugin/RealTimePluginFactory.cpp --- a/plugin/RealTimePluginFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/RealTimePluginFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -43,21 +43,21 @@ RealTimePluginFactory::instance(QString pluginType) { if (pluginType == "ladspa") { - if (!_ladspaInstance) { -// SVDEBUG << "RealTimePluginFactory::instance(" << pluginType// << "): creating new LADSPAPluginFactory" << endl; - _ladspaInstance = new LADSPAPluginFactory(); - _ladspaInstance->discoverPlugins(); - } - return _ladspaInstance; + if (!_ladspaInstance) { +// SVDEBUG << "RealTimePluginFactory::instance(" << pluginType// << "): creating new LADSPAPluginFactory" << endl; + _ladspaInstance = new LADSPAPluginFactory(); + _ladspaInstance->discoverPlugins(); + } + return _ladspaInstance; } else if (pluginType == "dssi") { - if (!_dssiInstance) { -// SVDEBUG << "RealTimePluginFactory::instance(" << pluginType// << "): creating new DSSIPluginFactory" << endl; - _dssiInstance = new DSSIPluginFactory(); - _dssiInstance->discoverPlugins(); - } - return _dssiInstance; + if (!_dssiInstance) { +// SVDEBUG << "RealTimePluginFactory::instance(" << pluginType// << "): creating new DSSIPluginFactory" << endl; + _dssiInstance = new DSSIPluginFactory(); + _dssiInstance->discoverPlugins(); + } + return _dssiInstance; } - + else return 0; } @@ -86,18 +86,18 @@ factory = instance("dssi"); if (factory) { - const std::vector &tmp = factory->getPluginIdentifiers(); - for (size_t i = 0; i < tmp.size(); ++i) { - rv.push_back(tmp[i]); - } + const std::vector &tmp = factory->getPluginIdentifiers(); + for (size_t i = 0; i < tmp.size(); ++i) { + rv.push_back(tmp[i]); + } } factory = instance("ladspa"); if (factory) { - const std::vector &tmp = factory->getPluginIdentifiers(); - for (size_t i = 0; i < tmp.size(); ++i) { - rv.push_back(tmp[i]); - } + const std::vector &tmp = factory->getPluginIdentifiers(); + for (size_t i = 0; i < tmp.size(); ++i) { + rv.push_back(tmp[i]); + } } // Plugins can change the locale, revert it to default. diff -r d4a28d1479a8 -r 710e6250a401 plugin/RealTimePluginFactory.h --- a/plugin/RealTimePluginFactory.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/RealTimePluginFactory.h Mon Sep 17 13:51:14 2018 +0100 @@ -86,17 +86,25 @@ * Instantiate a plugin. */ virtual RealTimePluginInstance *instantiatePlugin(QString identifier, - int clientId, - int position, - sv_samplerate_t sampleRate, - int blockSize, - int channels) = 0; + int clientId, + int position, + sv_samplerate_t sampleRate, + int blockSize, + int channels) = 0; /** * Get category metadata about a plugin (without instantiating it). */ virtual QString getPluginCategory(QString identifier) = 0; + /** + * Get the full file path (including both directory and filename) + * of the library file that provides a given plugin + * identifier. Note getPluginIdentifiers() must have been called + * before this has access to the necessary information. + */ + virtual QString getPluginLibraryPath(QString identifier) = 0; + protected: RealTimePluginFactory() { } diff -r d4a28d1479a8 -r 710e6250a401 plugin/RealTimePluginInstance.cpp --- a/plugin/RealTimePluginInstance.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/RealTimePluginInstance.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -31,9 +31,9 @@ // SVDEBUG << "RealTimePluginInstance::~RealTimePluginInstance" << endl; if (m_factory) { -// SVDEBUG << "Asking factory to release " << m_identifier << endl; +// SVDEBUG << "Asking factory to release " << m_identifier << endl; - m_factory->releasePlugin(this, m_identifier); + m_factory->releasePlugin(this, m_identifier); } } diff -r d4a28d1479a8 -r 710e6250a401 plugin/RealTimePluginInstance.h --- a/plugin/RealTimePluginInstance.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/RealTimePluginInstance.h Mon Sep 17 13:51:14 2018 +0100 @@ -33,7 +33,7 @@ #include class RealTimePluginFactory; - + /** * RealTimePluginInstance is an interface that an audio process can * use to refer to an instance of a plugin without needing to know @@ -121,7 +121,7 @@ virtual std::string configure(std::string /* key */, std::string /* value */) { return std::string(); } virtual void sendEvent(const RealTime & /* eventTime */, - const void * /* event */) { } + const void * /* event */) { } virtual void clearEvents() { } virtual bool isBypassed() const = 0; @@ -145,7 +145,7 @@ protected: RealTimePluginInstance(RealTimePluginFactory *factory, QString identifier) : - m_factory(factory), m_identifier(identifier) { } + m_factory(factory), m_identifier(identifier) { } RealTimePluginFactory *m_factory; QString m_identifier; diff -r d4a28d1479a8 -r 710e6250a401 plugin/api/alsa/asoundef.h --- a/plugin/api/alsa/asoundef.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/api/alsa/asoundef.h Mon Sep 17 13:51:14 2018 +0100 @@ -44,8 +44,8 @@ * \{ */ -#define MIDI_CHANNELS 16 /**< Number of channels per port/cable. */ -#define MIDI_GM_DRUM_CHANNEL (10-1) /**< Channel number for GM drums. */ +#define MIDI_CHANNELS 16 /**< Number of channels per port/cable. */ +#define MIDI_GM_DRUM_CHANNEL (10-1) /**< Channel number for GM drums. */ /** * \defgroup MIDI_Commands MIDI Commands @@ -53,26 +53,26 @@ * \{ */ -#define MIDI_CMD_NOTE_OFF 0x80 /**< note off */ -#define MIDI_CMD_NOTE_ON 0x90 /**< note on */ -#define MIDI_CMD_NOTE_PRESSURE 0xa0 /**< key pressure */ -#define MIDI_CMD_CONTROL 0xb0 /**< control change */ -#define MIDI_CMD_PGM_CHANGE 0xc0 /**< program change */ -#define MIDI_CMD_CHANNEL_PRESSURE 0xd0 /**< channel pressure */ -#define MIDI_CMD_BENDER 0xe0 /**< pitch bender */ +#define MIDI_CMD_NOTE_OFF 0x80 /**< note off */ +#define MIDI_CMD_NOTE_ON 0x90 /**< note on */ +#define MIDI_CMD_NOTE_PRESSURE 0xa0 /**< key pressure */ +#define MIDI_CMD_CONTROL 0xb0 /**< control change */ +#define MIDI_CMD_PGM_CHANGE 0xc0 /**< program change */ +#define MIDI_CMD_CHANNEL_PRESSURE 0xd0 /**< channel pressure */ +#define MIDI_CMD_BENDER 0xe0 /**< pitch bender */ -#define MIDI_CMD_COMMON_SYSEX 0xf0 /**< sysex (system exclusive) begin */ -#define MIDI_CMD_COMMON_MTC_QUARTER 0xf1 /**< MTC quarter frame */ -#define MIDI_CMD_COMMON_SONG_POS 0xf2 /**< song position */ -#define MIDI_CMD_COMMON_SONG_SELECT 0xf3 /**< song select */ -#define MIDI_CMD_COMMON_TUNE_REQUEST 0xf6 /**< tune request */ -#define MIDI_CMD_COMMON_SYSEX_END 0xf7 /**< end of sysex */ -#define MIDI_CMD_COMMON_CLOCK 0xf8 /**< clock */ -#define MIDI_CMD_COMMON_START 0xfa /**< start */ -#define MIDI_CMD_COMMON_CONTINUE 0xfb /**< continue */ -#define MIDI_CMD_COMMON_STOP 0xfc /**< stop */ -#define MIDI_CMD_COMMON_SENSING 0xfe /**< active sensing */ -#define MIDI_CMD_COMMON_RESET 0xff /**< reset */ +#define MIDI_CMD_COMMON_SYSEX 0xf0 /**< sysex (system exclusive) begin */ +#define MIDI_CMD_COMMON_MTC_QUARTER 0xf1 /**< MTC quarter frame */ +#define MIDI_CMD_COMMON_SONG_POS 0xf2 /**< song position */ +#define MIDI_CMD_COMMON_SONG_SELECT 0xf3 /**< song select */ +#define MIDI_CMD_COMMON_TUNE_REQUEST 0xf6 /**< tune request */ +#define MIDI_CMD_COMMON_SYSEX_END 0xf7 /**< end of sysex */ +#define MIDI_CMD_COMMON_CLOCK 0xf8 /**< clock */ +#define MIDI_CMD_COMMON_START 0xfa /**< start */ +#define MIDI_CMD_COMMON_CONTINUE 0xfb /**< continue */ +#define MIDI_CMD_COMMON_STOP 0xfc /**< stop */ +#define MIDI_CMD_COMMON_SENSING 0xfe /**< active sensing */ +#define MIDI_CMD_COMMON_RESET 0xff /**< reset */ /** \} */ @@ -82,78 +82,78 @@ * \{ */ -#define MIDI_CTL_MSB_BANK 0x00 /**< Bank selection */ -#define MIDI_CTL_MSB_MODWHEEL 0x01 /**< Modulation */ -#define MIDI_CTL_MSB_BREATH 0x02 /**< Breath */ -#define MIDI_CTL_MSB_FOOT 0x04 /**< Foot */ -#define MIDI_CTL_MSB_PORTAMENTO_TIME 0x05 /**< Portamento time */ -#define MIDI_CTL_MSB_DATA_ENTRY 0x06 /**< Data entry */ -#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 /**< Main volume */ -#define MIDI_CTL_MSB_BALANCE 0x08 /**< Balance */ -#define MIDI_CTL_MSB_PAN 0x0a /**< Panpot */ -#define MIDI_CTL_MSB_EXPRESSION 0x0b /**< Expression */ -#define MIDI_CTL_MSB_EFFECT1 0x0c /**< Effect1 */ -#define MIDI_CTL_MSB_EFFECT2 0x0d /**< Effect2 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 /**< General purpose 1 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 /**< General purpose 2 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 /**< General purpose 3 */ -#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 /**< General purpose 4 */ -#define MIDI_CTL_LSB_BANK 0x20 /**< Bank selection */ -#define MIDI_CTL_LSB_MODWHEEL 0x21 /**< Modulation */ -#define MIDI_CTL_LSB_BREATH 0x22 /**< Breath */ -#define MIDI_CTL_LSB_FOOT 0x24 /**< Foot */ -#define MIDI_CTL_LSB_PORTAMENTO_TIME 0x25 /**< Portamento time */ -#define MIDI_CTL_LSB_DATA_ENTRY 0x26 /**< Data entry */ -#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 /**< Main volume */ -#define MIDI_CTL_LSB_BALANCE 0x28 /**< Balance */ -#define MIDI_CTL_LSB_PAN 0x2a /**< Panpot */ -#define MIDI_CTL_LSB_EXPRESSION 0x2b /**< Expression */ -#define MIDI_CTL_LSB_EFFECT1 0x2c /**< Effect1 */ -#define MIDI_CTL_LSB_EFFECT2 0x2d /**< Effect2 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 /**< General purpose 1 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 /**< General purpose 2 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 /**< General purpose 3 */ -#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 /**< General purpose 4 */ -#define MIDI_CTL_SUSTAIN 0x40 /**< Sustain pedal */ -#define MIDI_CTL_PORTAMENTO 0x41 /**< Portamento */ -#define MIDI_CTL_SUSTENUTO 0x42 /**< Sostenuto */ -#define MIDI_CTL_SOFT_PEDAL 0x43 /**< Soft pedal */ -#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 /**< Legato foot switch */ -#define MIDI_CTL_HOLD2 0x45 /**< Hold2 */ -#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 /**< SC1 Sound Variation */ -#define MIDI_CTL_SC2_TIMBRE 0x47 /**< SC2 Timbre */ -#define MIDI_CTL_SC3_RELEASE_TIME 0x48 /**< SC3 Release Time */ -#define MIDI_CTL_SC4_ATTACK_TIME 0x49 /**< SC4 Attack Time */ -#define MIDI_CTL_SC5_BRIGHTNESS 0x4a /**< SC5 Brightness */ -#define MIDI_CTL_SC6 0x4b /**< SC6 */ -#define MIDI_CTL_SC7 0x4c /**< SC7 */ -#define MIDI_CTL_SC8 0x4d /**< SC8 */ -#define MIDI_CTL_SC9 0x4e /**< SC9 */ -#define MIDI_CTL_SC10 0x4f /**< SC10 */ -#define MIDI_CTL_GENERAL_PURPOSE5 0x50 /**< General purpose 5 */ -#define MIDI_CTL_GENERAL_PURPOSE6 0x51 /**< General purpose 6 */ -#define MIDI_CTL_GENERAL_PURPOSE7 0x52 /**< General purpose 7 */ -#define MIDI_CTL_GENERAL_PURPOSE8 0x53 /**< General purpose 8 */ -#define MIDI_CTL_PORTAMENTO_CONTROL 0x54 /**< Portamento control */ -#define MIDI_CTL_E1_REVERB_DEPTH 0x5b /**< E1 Reverb Depth */ -#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5c /**< E2 Tremolo Depth */ -#define MIDI_CTL_E3_CHORUS_DEPTH 0x5d /**< E3 Chorus Depth */ -#define MIDI_CTL_E4_DETUNE_DEPTH 0x5e /**< E4 Detune Depth */ -#define MIDI_CTL_E5_PHASER_DEPTH 0x5f /**< E5 Phaser Depth */ -#define MIDI_CTL_DATA_INCREMENT 0x60 /**< Data Increment */ -#define MIDI_CTL_DATA_DECREMENT 0x61 /**< Data Decrement */ -#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 /**< Non-registered parameter number */ -#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 /**< Non-registered parameter number */ -#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 /**< Registered parameter number */ -#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 /**< Registered parameter number */ -#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 /**< All sounds off */ -#define MIDI_CTL_RESET_CONTROLLERS 0x79 /**< Reset Controllers */ -#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7a /**< Local control switch */ -#define MIDI_CTL_ALL_NOTES_OFF 0x7b /**< All notes off */ -#define MIDI_CTL_OMNI_OFF 0x7c /**< Omni off */ -#define MIDI_CTL_OMNI_ON 0x7d /**< Omni on */ -#define MIDI_CTL_MONO1 0x7e /**< Mono1 */ -#define MIDI_CTL_MONO2 0x7f /**< Mono2 */ +#define MIDI_CTL_MSB_BANK 0x00 /**< Bank selection */ +#define MIDI_CTL_MSB_MODWHEEL 0x01 /**< Modulation */ +#define MIDI_CTL_MSB_BREATH 0x02 /**< Breath */ +#define MIDI_CTL_MSB_FOOT 0x04 /**< Foot */ +#define MIDI_CTL_MSB_PORTAMENTO_TIME 0x05 /**< Portamento time */ +#define MIDI_CTL_MSB_DATA_ENTRY 0x06 /**< Data entry */ +#define MIDI_CTL_MSB_MAIN_VOLUME 0x07 /**< Main volume */ +#define MIDI_CTL_MSB_BALANCE 0x08 /**< Balance */ +#define MIDI_CTL_MSB_PAN 0x0a /**< Panpot */ +#define MIDI_CTL_MSB_EXPRESSION 0x0b /**< Expression */ +#define MIDI_CTL_MSB_EFFECT1 0x0c /**< Effect1 */ +#define MIDI_CTL_MSB_EFFECT2 0x0d /**< Effect2 */ +#define MIDI_CTL_MSB_GENERAL_PURPOSE1 0x10 /**< General purpose 1 */ +#define MIDI_CTL_MSB_GENERAL_PURPOSE2 0x11 /**< General purpose 2 */ +#define MIDI_CTL_MSB_GENERAL_PURPOSE3 0x12 /**< General purpose 3 */ +#define MIDI_CTL_MSB_GENERAL_PURPOSE4 0x13 /**< General purpose 4 */ +#define MIDI_CTL_LSB_BANK 0x20 /**< Bank selection */ +#define MIDI_CTL_LSB_MODWHEEL 0x21 /**< Modulation */ +#define MIDI_CTL_LSB_BREATH 0x22 /**< Breath */ +#define MIDI_CTL_LSB_FOOT 0x24 /**< Foot */ +#define MIDI_CTL_LSB_PORTAMENTO_TIME 0x25 /**< Portamento time */ +#define MIDI_CTL_LSB_DATA_ENTRY 0x26 /**< Data entry */ +#define MIDI_CTL_LSB_MAIN_VOLUME 0x27 /**< Main volume */ +#define MIDI_CTL_LSB_BALANCE 0x28 /**< Balance */ +#define MIDI_CTL_LSB_PAN 0x2a /**< Panpot */ +#define MIDI_CTL_LSB_EXPRESSION 0x2b /**< Expression */ +#define MIDI_CTL_LSB_EFFECT1 0x2c /**< Effect1 */ +#define MIDI_CTL_LSB_EFFECT2 0x2d /**< Effect2 */ +#define MIDI_CTL_LSB_GENERAL_PURPOSE1 0x30 /**< General purpose 1 */ +#define MIDI_CTL_LSB_GENERAL_PURPOSE2 0x31 /**< General purpose 2 */ +#define MIDI_CTL_LSB_GENERAL_PURPOSE3 0x32 /**< General purpose 3 */ +#define MIDI_CTL_LSB_GENERAL_PURPOSE4 0x33 /**< General purpose 4 */ +#define MIDI_CTL_SUSTAIN 0x40 /**< Sustain pedal */ +#define MIDI_CTL_PORTAMENTO 0x41 /**< Portamento */ +#define MIDI_CTL_SUSTENUTO 0x42 /**< Sostenuto */ +#define MIDI_CTL_SOFT_PEDAL 0x43 /**< Soft pedal */ +#define MIDI_CTL_LEGATO_FOOTSWITCH 0x44 /**< Legato foot switch */ +#define MIDI_CTL_HOLD2 0x45 /**< Hold2 */ +#define MIDI_CTL_SC1_SOUND_VARIATION 0x46 /**< SC1 Sound Variation */ +#define MIDI_CTL_SC2_TIMBRE 0x47 /**< SC2 Timbre */ +#define MIDI_CTL_SC3_RELEASE_TIME 0x48 /**< SC3 Release Time */ +#define MIDI_CTL_SC4_ATTACK_TIME 0x49 /**< SC4 Attack Time */ +#define MIDI_CTL_SC5_BRIGHTNESS 0x4a /**< SC5 Brightness */ +#define MIDI_CTL_SC6 0x4b /**< SC6 */ +#define MIDI_CTL_SC7 0x4c /**< SC7 */ +#define MIDI_CTL_SC8 0x4d /**< SC8 */ +#define MIDI_CTL_SC9 0x4e /**< SC9 */ +#define MIDI_CTL_SC10 0x4f /**< SC10 */ +#define MIDI_CTL_GENERAL_PURPOSE5 0x50 /**< General purpose 5 */ +#define MIDI_CTL_GENERAL_PURPOSE6 0x51 /**< General purpose 6 */ +#define MIDI_CTL_GENERAL_PURPOSE7 0x52 /**< General purpose 7 */ +#define MIDI_CTL_GENERAL_PURPOSE8 0x53 /**< General purpose 8 */ +#define MIDI_CTL_PORTAMENTO_CONTROL 0x54 /**< Portamento control */ +#define MIDI_CTL_E1_REVERB_DEPTH 0x5b /**< E1 Reverb Depth */ +#define MIDI_CTL_E2_TREMOLO_DEPTH 0x5c /**< E2 Tremolo Depth */ +#define MIDI_CTL_E3_CHORUS_DEPTH 0x5d /**< E3 Chorus Depth */ +#define MIDI_CTL_E4_DETUNE_DEPTH 0x5e /**< E4 Detune Depth */ +#define MIDI_CTL_E5_PHASER_DEPTH 0x5f /**< E5 Phaser Depth */ +#define MIDI_CTL_DATA_INCREMENT 0x60 /**< Data Increment */ +#define MIDI_CTL_DATA_DECREMENT 0x61 /**< Data Decrement */ +#define MIDI_CTL_NONREG_PARM_NUM_LSB 0x62 /**< Non-registered parameter number */ +#define MIDI_CTL_NONREG_PARM_NUM_MSB 0x63 /**< Non-registered parameter number */ +#define MIDI_CTL_REGIST_PARM_NUM_LSB 0x64 /**< Registered parameter number */ +#define MIDI_CTL_REGIST_PARM_NUM_MSB 0x65 /**< Registered parameter number */ +#define MIDI_CTL_ALL_SOUNDS_OFF 0x78 /**< All sounds off */ +#define MIDI_CTL_RESET_CONTROLLERS 0x79 /**< Reset Controllers */ +#define MIDI_CTL_LOCAL_CONTROL_SWITCH 0x7a /**< Local control switch */ +#define MIDI_CTL_ALL_NOTES_OFF 0x7b /**< All notes off */ +#define MIDI_CTL_OMNI_OFF 0x7c /**< Omni off */ +#define MIDI_CTL_OMNI_ON 0x7d /**< Omni on */ +#define MIDI_CTL_MONO1 0x7e /**< Mono1 */ +#define MIDI_CTL_MONO2 0x7f /**< Mono2 */ /** \} */ diff -r d4a28d1479a8 -r 710e6250a401 plugin/api/alsa/seq.h --- a/plugin/api/alsa/seq.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/api/alsa/seq.h Mon Sep 17 13:51:14 2018 +0100 @@ -48,34 +48,34 @@ /* event type macros */ enum { - SND_SEQ_EVFLG_RESULT, - SND_SEQ_EVFLG_NOTE, - SND_SEQ_EVFLG_CONTROL, - SND_SEQ_EVFLG_QUEUE, - SND_SEQ_EVFLG_SYSTEM, - SND_SEQ_EVFLG_MESSAGE, - SND_SEQ_EVFLG_CONNECTION, - SND_SEQ_EVFLG_SAMPLE, - SND_SEQ_EVFLG_USERS, - SND_SEQ_EVFLG_INSTR, - SND_SEQ_EVFLG_QUOTE, - SND_SEQ_EVFLG_NONE, - SND_SEQ_EVFLG_RAW, - SND_SEQ_EVFLG_FIXED, - SND_SEQ_EVFLG_VARIABLE, - SND_SEQ_EVFLG_VARUSR + SND_SEQ_EVFLG_RESULT, + SND_SEQ_EVFLG_NOTE, + SND_SEQ_EVFLG_CONTROL, + SND_SEQ_EVFLG_QUEUE, + SND_SEQ_EVFLG_SYSTEM, + SND_SEQ_EVFLG_MESSAGE, + SND_SEQ_EVFLG_CONNECTION, + SND_SEQ_EVFLG_SAMPLE, + SND_SEQ_EVFLG_USERS, + SND_SEQ_EVFLG_INSTR, + SND_SEQ_EVFLG_QUOTE, + SND_SEQ_EVFLG_NONE, + SND_SEQ_EVFLG_RAW, + SND_SEQ_EVFLG_FIXED, + SND_SEQ_EVFLG_VARIABLE, + SND_SEQ_EVFLG_VARUSR }; enum { - SND_SEQ_EVFLG_NOTE_ONEARG, - SND_SEQ_EVFLG_NOTE_TWOARG + SND_SEQ_EVFLG_NOTE_ONEARG, + SND_SEQ_EVFLG_NOTE_TWOARG }; enum { - SND_SEQ_EVFLG_QUEUE_NOARG, - SND_SEQ_EVFLG_QUEUE_TICK, - SND_SEQ_EVFLG_QUEUE_TIME, - SND_SEQ_EVFLG_QUEUE_VALUE + SND_SEQ_EVFLG_QUEUE_NOARG, + SND_SEQ_EVFLG_QUEUE_TICK, + SND_SEQ_EVFLG_QUEUE_TIME, + SND_SEQ_EVFLG_QUEUE_VALUE }; /** @@ -85,99 +85,99 @@ */ extern const unsigned int snd_seq_event_types[]; -#define _SND_SEQ_TYPE(x) (1<<(x)) /**< master type - 24bit */ -#define _SND_SEQ_TYPE_OPT(x) ((x)<<24) /**< optional type - 8bit */ +#define _SND_SEQ_TYPE(x) (1<<(x)) /**< master type - 24bit */ +#define _SND_SEQ_TYPE_OPT(x) ((x)<<24) /**< optional type - 8bit */ /** check the event type */ #define snd_seq_type_check(ev,x) (snd_seq_event_types[(ev)->type] & _SND_SEQ_TYPE(x)) /** event type check: result events */ #define snd_seq_ev_is_result_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_RESULT) + snd_seq_type_check(ev, SND_SEQ_EVFLG_RESULT) /** event type check: note events */ #define snd_seq_ev_is_note_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_NOTE) + snd_seq_type_check(ev, SND_SEQ_EVFLG_NOTE) /** event type check: control events */ #define snd_seq_ev_is_control_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_CONTROL) + snd_seq_type_check(ev, SND_SEQ_EVFLG_CONTROL) /** event type check: channel specific events */ #define snd_seq_ev_is_channel_type(ev) \ - (snd_seq_event_types[(ev)->type] & (_SND_SEQ_TYPE(SND_SEQ_EVFLG_NOTE) | _SND_SEQ_TYPE(SND_SEQ_EVFLG_CONTROL))) + (snd_seq_event_types[(ev)->type] & (_SND_SEQ_TYPE(SND_SEQ_EVFLG_NOTE) | _SND_SEQ_TYPE(SND_SEQ_EVFLG_CONTROL))) /** event type check: queue control events */ #define snd_seq_ev_is_queue_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_QUEUE) + snd_seq_type_check(ev, SND_SEQ_EVFLG_QUEUE) /** event type check: system status messages */ #define snd_seq_ev_is_message_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_MESSAGE) + snd_seq_type_check(ev, SND_SEQ_EVFLG_MESSAGE) /** event type check: system status messages */ #define snd_seq_ev_is_subscribe_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_CONNECTION) + snd_seq_type_check(ev, SND_SEQ_EVFLG_CONNECTION) /** event type check: sample messages */ #define snd_seq_ev_is_sample_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_SAMPLE) + snd_seq_type_check(ev, SND_SEQ_EVFLG_SAMPLE) /** event type check: user-defined messages */ #define snd_seq_ev_is_user_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_USERS) + snd_seq_type_check(ev, SND_SEQ_EVFLG_USERS) /** event type check: instrument layer events */ #define snd_seq_ev_is_instr_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_INSTR) + snd_seq_type_check(ev, SND_SEQ_EVFLG_INSTR) /** event type check: fixed length events */ #define snd_seq_ev_is_fixed_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_FIXED) + snd_seq_type_check(ev, SND_SEQ_EVFLG_FIXED) /** event type check: variable length events */ -#define snd_seq_ev_is_variable_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_VARIABLE) +#define snd_seq_ev_is_variable_type(ev) \ + snd_seq_type_check(ev, SND_SEQ_EVFLG_VARIABLE) /** event type check: user pointer events */ #define snd_seq_ev_is_varusr_type(ev) \ - snd_seq_type_check(ev, SND_SEQ_EVFLG_VARUSR) + snd_seq_type_check(ev, SND_SEQ_EVFLG_VARUSR) /** event type check: reserved for kernel */ #define snd_seq_ev_is_reserved(ev) \ - (! snd_seq_event_types[(ev)->type]) + (! snd_seq_event_types[(ev)->type]) /** * macros to check event flags */ /** prior events */ -#define snd_seq_ev_is_prior(ev) \ - (((ev)->flags & SND_SEQ_PRIORITY_MASK) == SND_SEQ_PRIORITY_HIGH) +#define snd_seq_ev_is_prior(ev) \ + (((ev)->flags & SND_SEQ_PRIORITY_MASK) == SND_SEQ_PRIORITY_HIGH) /** get the data length type */ #define snd_seq_ev_length_type(ev) \ - ((ev)->flags & SND_SEQ_EVENT_LENGTH_MASK) + ((ev)->flags & SND_SEQ_EVENT_LENGTH_MASK) /** fixed length events */ -#define snd_seq_ev_is_fixed(ev) \ - (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_FIXED) +#define snd_seq_ev_is_fixed(ev) \ + (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_FIXED) /** variable length events */ #define snd_seq_ev_is_variable(ev) \ - (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARIABLE) + (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARIABLE) /** variable length on user-space */ #define snd_seq_ev_is_varusr(ev) \ - (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARUSR) + (snd_seq_ev_length_type(ev) == SND_SEQ_EVENT_LENGTH_VARUSR) /** time-stamp type */ #define snd_seq_ev_timestamp_type(ev) \ - ((ev)->flags & SND_SEQ_TIME_STAMP_MASK) + ((ev)->flags & SND_SEQ_TIME_STAMP_MASK) /** event is in tick time */ #define snd_seq_ev_is_tick(ev) \ - (snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_TICK) + (snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_TICK) /** event is in real-time */ #define snd_seq_ev_is_real(ev) \ - (snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_REAL) + (snd_seq_ev_timestamp_type(ev) == SND_SEQ_TIME_STAMP_REAL) /** time-mode type */ #define snd_seq_ev_timemode_type(ev) \ - ((ev)->flags & SND_SEQ_TIME_MODE_MASK) + ((ev)->flags & SND_SEQ_TIME_MODE_MASK) /** scheduled in absolute time */ #define snd_seq_ev_is_abstime(ev) \ - (snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_ABS) + (snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_ABS) /** scheduled in relative time */ #define snd_seq_ev_is_reltime(ev) \ - (snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_REL) + (snd_seq_ev_timemode_type(ev) == SND_SEQ_TIME_MODE_REL) /** direct dispatched events */ #define snd_seq_ev_is_direct(ev) \ - ((ev)->queue == SND_SEQ_QUEUE_DIRECT) + ((ev)->queue == SND_SEQ_QUEUE_DIRECT) /** \} */ diff -r d4a28d1479a8 -r 710e6250a401 plugin/api/alsa/seq_event.h --- a/plugin/api/alsa/seq_event.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/api/alsa/seq_event.h Mon Sep 17 13:51:14 2018 +0100 @@ -48,213 +48,213 @@ /** Sequencer event type */ enum snd_seq_event_type { - /** system status; event data type = #snd_seq_result_t */ - SND_SEQ_EVENT_SYSTEM = 0, - /** returned result status; event data type = #snd_seq_result_t */ - SND_SEQ_EVENT_RESULT, + /** system status; event data type = #snd_seq_result_t */ + SND_SEQ_EVENT_SYSTEM = 0, + /** returned result status; event data type = #snd_seq_result_t */ + SND_SEQ_EVENT_RESULT, - /** note on and off with duration; event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_NOTE = 5, - /** note on; event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_NOTEON, - /** note off; event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_NOTEOFF, - /** key pressure change (aftertouch); event data type = #snd_seq_ev_note_t */ - SND_SEQ_EVENT_KEYPRESS, - - /** controller; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_CONTROLLER = 10, - /** program change; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_PGMCHANGE, - /** channel pressure; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_CHANPRESS, - /** pitchwheel; event data type = #snd_seq_ev_ctrl_t; data is from -8192 to 8191) */ - SND_SEQ_EVENT_PITCHBEND, - /** 14 bit controller value; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_CONTROL14, - /** 14 bit NRPN; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_NONREGPARAM, - /** 14 bit RPN; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_REGPARAM, + /** note on and off with duration; event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_NOTE = 5, + /** note on; event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_NOTEON, + /** note off; event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_NOTEOFF, + /** key pressure change (aftertouch); event data type = #snd_seq_ev_note_t */ + SND_SEQ_EVENT_KEYPRESS, + + /** controller; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_CONTROLLER = 10, + /** program change; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_PGMCHANGE, + /** channel pressure; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_CHANPRESS, + /** pitchwheel; event data type = #snd_seq_ev_ctrl_t; data is from -8192 to 8191) */ + SND_SEQ_EVENT_PITCHBEND, + /** 14 bit controller value; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_CONTROL14, + /** 14 bit NRPN; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_NONREGPARAM, + /** 14 bit RPN; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_REGPARAM, - /** SPP with LSB and MSB values; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_SONGPOS = 20, - /** Song Select with song ID number; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_SONGSEL, - /** midi time code quarter frame; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_QFRAME, - /** SMF Time Signature event; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_TIMESIGN, - /** SMF Key Signature event; event data type = #snd_seq_ev_ctrl_t */ - SND_SEQ_EVENT_KEYSIGN, - - /** MIDI Real Time Start message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_START = 30, - /** MIDI Real Time Continue message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_CONTINUE, - /** MIDI Real Time Stop message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_STOP, - /** Set tick queue position; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_SETPOS_TICK, - /** Set real-time queue position; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_SETPOS_TIME, - /** (SMF) Tempo event; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_TEMPO, - /** MIDI Real Time Clock message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_CLOCK, - /** MIDI Real Time Tick message; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_TICK, - /** Queue timer skew; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_QUEUE_SKEW, - /** Sync position changed; event data type = #snd_seq_ev_queue_control_t */ - SND_SEQ_EVENT_SYNC_POS, + /** SPP with LSB and MSB values; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_SONGPOS = 20, + /** Song Select with song ID number; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_SONGSEL, + /** midi time code quarter frame; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_QFRAME, + /** SMF Time Signature event; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_TIMESIGN, + /** SMF Key Signature event; event data type = #snd_seq_ev_ctrl_t */ + SND_SEQ_EVENT_KEYSIGN, + + /** MIDI Real Time Start message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_START = 30, + /** MIDI Real Time Continue message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_CONTINUE, + /** MIDI Real Time Stop message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_STOP, + /** Set tick queue position; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_SETPOS_TICK, + /** Set real-time queue position; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_SETPOS_TIME, + /** (SMF) Tempo event; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_TEMPO, + /** MIDI Real Time Clock message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_CLOCK, + /** MIDI Real Time Tick message; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_TICK, + /** Queue timer skew; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_QUEUE_SKEW, + /** Sync position changed; event data type = #snd_seq_ev_queue_control_t */ + SND_SEQ_EVENT_SYNC_POS, - /** Tune request; event data type = none */ - SND_SEQ_EVENT_TUNE_REQUEST = 40, - /** Reset to power-on state; event data type = none */ - SND_SEQ_EVENT_RESET, - /** Active sensing event; event data type = none */ - SND_SEQ_EVENT_SENSING, + /** Tune request; event data type = none */ + SND_SEQ_EVENT_TUNE_REQUEST = 40, + /** Reset to power-on state; event data type = none */ + SND_SEQ_EVENT_RESET, + /** Active sensing event; event data type = none */ + SND_SEQ_EVENT_SENSING, - /** Echo-back event; event data type = any type */ - SND_SEQ_EVENT_ECHO = 50, - /** OSS emulation raw event; event data type = any type */ - SND_SEQ_EVENT_OSS, + /** Echo-back event; event data type = any type */ + SND_SEQ_EVENT_ECHO = 50, + /** OSS emulation raw event; event data type = any type */ + SND_SEQ_EVENT_OSS, - /** New client has connected; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_CLIENT_START = 60, - /** Client has left the system; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_CLIENT_EXIT, - /** Client status/info has changed; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_CLIENT_CHANGE, - /** New port was created; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_PORT_START, - /** Port was deleted from system; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_PORT_EXIT, - /** Port status/info has changed; event data type = #snd_seq_addr_t */ - SND_SEQ_EVENT_PORT_CHANGE, + /** New client has connected; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_CLIENT_START = 60, + /** Client has left the system; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_CLIENT_EXIT, + /** Client status/info has changed; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_CLIENT_CHANGE, + /** New port was created; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_PORT_START, + /** Port was deleted from system; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_PORT_EXIT, + /** Port status/info has changed; event data type = #snd_seq_addr_t */ + SND_SEQ_EVENT_PORT_CHANGE, - /** Ports connected; event data type = #snd_seq_connect_t */ - SND_SEQ_EVENT_PORT_SUBSCRIBED, - /** Ports disconnected; event data type = #snd_seq_connect_t */ - SND_SEQ_EVENT_PORT_UNSUBSCRIBED, + /** Ports connected; event data type = #snd_seq_connect_t */ + SND_SEQ_EVENT_PORT_SUBSCRIBED, + /** Ports disconnected; event data type = #snd_seq_connect_t */ + SND_SEQ_EVENT_PORT_UNSUBSCRIBED, - /** Sample select; event data type = #snd_seq_ev_sample_control_t */ - SND_SEQ_EVENT_SAMPLE = 70, - /** Sample cluster select; event data type = #snd_seq_ev_sample_control_t */ - SND_SEQ_EVENT_SAMPLE_CLUSTER, - /** voice start */ - SND_SEQ_EVENT_SAMPLE_START, - /** voice stop */ - SND_SEQ_EVENT_SAMPLE_STOP, - /** playback frequency */ - SND_SEQ_EVENT_SAMPLE_FREQ, - /** volume and balance */ - SND_SEQ_EVENT_SAMPLE_VOLUME, - /** sample loop */ - SND_SEQ_EVENT_SAMPLE_LOOP, - /** sample position */ - SND_SEQ_EVENT_SAMPLE_POSITION, - /** private (hardware dependent) event */ - SND_SEQ_EVENT_SAMPLE_PRIVATE1, + /** Sample select; event data type = #snd_seq_ev_sample_control_t */ + SND_SEQ_EVENT_SAMPLE = 70, + /** Sample cluster select; event data type = #snd_seq_ev_sample_control_t */ + SND_SEQ_EVENT_SAMPLE_CLUSTER, + /** voice start */ + SND_SEQ_EVENT_SAMPLE_START, + /** voice stop */ + SND_SEQ_EVENT_SAMPLE_STOP, + /** playback frequency */ + SND_SEQ_EVENT_SAMPLE_FREQ, + /** volume and balance */ + SND_SEQ_EVENT_SAMPLE_VOLUME, + /** sample loop */ + SND_SEQ_EVENT_SAMPLE_LOOP, + /** sample position */ + SND_SEQ_EVENT_SAMPLE_POSITION, + /** private (hardware dependent) event */ + SND_SEQ_EVENT_SAMPLE_PRIVATE1, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR0 = 90, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR1, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR2, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR3, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR4, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR5, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR6, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR7, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR8, - /** user-defined event; event data type = any (fixed size) */ - SND_SEQ_EVENT_USR9, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR0 = 90, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR1, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR2, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR3, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR4, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR5, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR6, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR7, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR8, + /** user-defined event; event data type = any (fixed size) */ + SND_SEQ_EVENT_USR9, - /** begin of instrument management */ - SND_SEQ_EVENT_INSTR_BEGIN = 100, - /** end of instrument management */ - SND_SEQ_EVENT_INSTR_END, - /** query instrument interface info */ - SND_SEQ_EVENT_INSTR_INFO, - /** result of instrument interface info */ - SND_SEQ_EVENT_INSTR_INFO_RESULT, - /** query instrument format info */ - SND_SEQ_EVENT_INSTR_FINFO, - /** result of instrument format info */ - SND_SEQ_EVENT_INSTR_FINFO_RESULT, - /** reset instrument instrument memory */ - SND_SEQ_EVENT_INSTR_RESET, - /** get instrument interface status */ - SND_SEQ_EVENT_INSTR_STATUS, - /** result of instrument interface status */ - SND_SEQ_EVENT_INSTR_STATUS_RESULT, - /** put an instrument to port */ - SND_SEQ_EVENT_INSTR_PUT, - /** get an instrument from port */ - SND_SEQ_EVENT_INSTR_GET, - /** result of instrument query */ - SND_SEQ_EVENT_INSTR_GET_RESULT, - /** free instrument(s) */ - SND_SEQ_EVENT_INSTR_FREE, - /** get instrument list */ - SND_SEQ_EVENT_INSTR_LIST, - /** result of instrument list */ - SND_SEQ_EVENT_INSTR_LIST_RESULT, - /** set cluster parameters */ - SND_SEQ_EVENT_INSTR_CLUSTER, - /** get cluster parameters */ - SND_SEQ_EVENT_INSTR_CLUSTER_GET, - /** result of cluster parameters */ - SND_SEQ_EVENT_INSTR_CLUSTER_RESULT, - /** instrument change */ - SND_SEQ_EVENT_INSTR_CHANGE, + /** begin of instrument management */ + SND_SEQ_EVENT_INSTR_BEGIN = 100, + /** end of instrument management */ + SND_SEQ_EVENT_INSTR_END, + /** query instrument interface info */ + SND_SEQ_EVENT_INSTR_INFO, + /** result of instrument interface info */ + SND_SEQ_EVENT_INSTR_INFO_RESULT, + /** query instrument format info */ + SND_SEQ_EVENT_INSTR_FINFO, + /** result of instrument format info */ + SND_SEQ_EVENT_INSTR_FINFO_RESULT, + /** reset instrument instrument memory */ + SND_SEQ_EVENT_INSTR_RESET, + /** get instrument interface status */ + SND_SEQ_EVENT_INSTR_STATUS, + /** result of instrument interface status */ + SND_SEQ_EVENT_INSTR_STATUS_RESULT, + /** put an instrument to port */ + SND_SEQ_EVENT_INSTR_PUT, + /** get an instrument from port */ + SND_SEQ_EVENT_INSTR_GET, + /** result of instrument query */ + SND_SEQ_EVENT_INSTR_GET_RESULT, + /** free instrument(s) */ + SND_SEQ_EVENT_INSTR_FREE, + /** get instrument list */ + SND_SEQ_EVENT_INSTR_LIST, + /** result of instrument list */ + SND_SEQ_EVENT_INSTR_LIST_RESULT, + /** set cluster parameters */ + SND_SEQ_EVENT_INSTR_CLUSTER, + /** get cluster parameters */ + SND_SEQ_EVENT_INSTR_CLUSTER_GET, + /** result of cluster parameters */ + SND_SEQ_EVENT_INSTR_CLUSTER_RESULT, + /** instrument change */ + SND_SEQ_EVENT_INSTR_CHANGE, - /** system exclusive data (variable length); event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_SYSEX = 130, - /** error event; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_BOUNCE, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR0 = 135, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR1, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR2, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR3, - /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ - SND_SEQ_EVENT_USR_VAR4, + /** system exclusive data (variable length); event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_SYSEX = 130, + /** error event; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_BOUNCE, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR0 = 135, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR1, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR2, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR3, + /** reserved for user apps; event data type = #snd_seq_ev_ext_t */ + SND_SEQ_EVENT_USR_VAR4, - /** NOP; ignored in any case */ - SND_SEQ_EVENT_NONE = 255 + /** NOP; ignored in any case */ + SND_SEQ_EVENT_NONE = 255 }; /** Sequencer event address */ typedef struct snd_seq_addr { - unsigned char client; /**< Client id */ - unsigned char port; /**< Port id */ + unsigned char client; /**< Client id */ + unsigned char port; /**< Port id */ } snd_seq_addr_t; /** Connection (subscription) between ports */ typedef struct snd_seq_connect { - snd_seq_addr_t sender; /**< sender address */ - snd_seq_addr_t dest; /**< destination address */ + snd_seq_addr_t sender; /**< sender address */ + snd_seq_addr_t dest; /**< destination address */ } snd_seq_connect_t; /** Real-time data record */ typedef struct snd_seq_real_time { - unsigned int tv_sec; /**< seconds */ - unsigned int tv_nsec; /**< nanoseconds */ + unsigned int tv_sec; /**< seconds */ + unsigned int tv_nsec; /**< nanoseconds */ } snd_seq_real_time_t; /** (MIDI) Tick-time data record */ @@ -262,8 +262,8 @@ /** unioned time stamp */ typedef union snd_seq_timestamp { - snd_seq_tick_time_t tick; /**< tick-time */ - struct snd_seq_real_time time; /**< real-time */ + snd_seq_tick_time_t tick; /**< tick-time */ + struct snd_seq_real_time time; /**< real-time */ } snd_seq_timestamp_t; @@ -272,55 +272,55 @@ * * NOTE: only 8 bits available! */ -#define SND_SEQ_TIME_STAMP_TICK (0<<0) /**< timestamp in clock ticks */ -#define SND_SEQ_TIME_STAMP_REAL (1<<0) /**< timestamp in real time */ -#define SND_SEQ_TIME_STAMP_MASK (1<<0) /**< mask for timestamp bits */ +#define SND_SEQ_TIME_STAMP_TICK (0<<0) /**< timestamp in clock ticks */ +#define SND_SEQ_TIME_STAMP_REAL (1<<0) /**< timestamp in real time */ +#define SND_SEQ_TIME_STAMP_MASK (1<<0) /**< mask for timestamp bits */ -#define SND_SEQ_TIME_MODE_ABS (0<<1) /**< absolute timestamp */ -#define SND_SEQ_TIME_MODE_REL (1<<1) /**< relative to current time */ -#define SND_SEQ_TIME_MODE_MASK (1<<1) /**< mask for time mode bits */ +#define SND_SEQ_TIME_MODE_ABS (0<<1) /**< absolute timestamp */ +#define SND_SEQ_TIME_MODE_REL (1<<1) /**< relative to current time */ +#define SND_SEQ_TIME_MODE_MASK (1<<1) /**< mask for time mode bits */ -#define SND_SEQ_EVENT_LENGTH_FIXED (0<<2) /**< fixed event size */ -#define SND_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /**< variable event size */ -#define SND_SEQ_EVENT_LENGTH_VARUSR (2<<2) /**< variable event size - user memory space */ -#define SND_SEQ_EVENT_LENGTH_MASK (3<<2) /**< mask for event length bits */ +#define SND_SEQ_EVENT_LENGTH_FIXED (0<<2) /**< fixed event size */ +#define SND_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /**< variable event size */ +#define SND_SEQ_EVENT_LENGTH_VARUSR (2<<2) /**< variable event size - user memory space */ +#define SND_SEQ_EVENT_LENGTH_MASK (3<<2) /**< mask for event length bits */ -#define SND_SEQ_PRIORITY_NORMAL (0<<4) /**< normal priority */ -#define SND_SEQ_PRIORITY_HIGH (1<<4) /**< event should be processed before others */ -#define SND_SEQ_PRIORITY_MASK (1<<4) /**< mask for priority bits */ +#define SND_SEQ_PRIORITY_NORMAL (0<<4) /**< normal priority */ +#define SND_SEQ_PRIORITY_HIGH (1<<4) /**< event should be processed before others */ +#define SND_SEQ_PRIORITY_MASK (1<<4) /**< mask for priority bits */ /** Note event */ typedef struct snd_seq_ev_note { - unsigned char channel; /**< channel number */ - unsigned char note; /**< note */ - unsigned char velocity; /**< velocity */ - unsigned char off_velocity; /**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */ - unsigned int duration; /**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */ + unsigned char channel; /**< channel number */ + unsigned char note; /**< note */ + unsigned char velocity; /**< velocity */ + unsigned char off_velocity; /**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */ + unsigned int duration; /**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */ } snd_seq_ev_note_t; /** Controller event */ typedef struct snd_seq_ev_ctrl { - unsigned char channel; /**< channel number */ - unsigned char unused[3]; /**< reserved */ - unsigned int param; /**< control parameter */ - signed int value; /**< control value */ + unsigned char channel; /**< channel number */ + unsigned char unused[3]; /**< reserved */ + unsigned int param; /**< control parameter */ + signed int value; /**< control value */ } snd_seq_ev_ctrl_t; /** generic set of bytes (12x8 bit) */ typedef struct snd_seq_ev_raw8 { - unsigned char d[12]; /**< 8 bit value */ + unsigned char d[12]; /**< 8 bit value */ } snd_seq_ev_raw8_t; /** generic set of integers (3x32 bit) */ typedef struct snd_seq_ev_raw32 { - unsigned int d[3]; /**< 32 bit value */ + unsigned int d[3]; /**< 32 bit value */ } snd_seq_ev_raw32_t; /** external stored data */ typedef struct snd_seq_ev_ext { - unsigned int len; /**< length of data */ - void *ptr; /**< pointer to data (note: can be 64-bit) */ + unsigned int len; /**< length of data */ + void *ptr; /**< pointer to data (note: can be 64-bit) */ } #ifdef __GNUC__ __attribute__((packed)) @@ -332,22 +332,22 @@ /** Instrument type */ typedef struct snd_seq_instr { - snd_seq_instr_cluster_t cluster; /**< cluster id */ - unsigned int std; /**< instrument standard id; the upper byte means a private instrument (owner - client id) */ - unsigned short bank; /**< instrument bank id */ - unsigned short prg; /**< instrument program id */ + snd_seq_instr_cluster_t cluster; /**< cluster id */ + unsigned int std; /**< instrument standard id; the upper byte means a private instrument (owner - client id) */ + unsigned short bank; /**< instrument bank id */ + unsigned short prg; /**< instrument program id */ } snd_seq_instr_t; /** sample number */ typedef struct snd_seq_ev_sample { - unsigned int std; /**< sample standard id */ - unsigned short bank; /**< sample bank id */ - unsigned short prg; /**< sample program id */ + unsigned int std; /**< sample standard id */ + unsigned short bank; /**< sample bank id */ + unsigned short prg; /**< sample program id */ } snd_seq_ev_sample_t; /** sample cluster */ typedef struct snd_seq_ev_cluster { - snd_seq_instr_cluster_t cluster; /**< cluster id */ + snd_seq_instr_cluster_t cluster; /**< cluster id */ } snd_seq_ev_cluster_t; /** sample position */ @@ -355,9 +355,9 @@ /** sample stop mode */ typedef enum snd_seq_stop_mode { - SND_SEQ_SAMPLE_STOP_IMMEDIATELY = 0, /**< terminate playing immediately */ - SND_SEQ_SAMPLE_STOP_VENVELOPE = 1, /**< finish volume envelope */ - SND_SEQ_SAMPLE_STOP_LOOP = 2 /**< terminate loop and finish wave */ + SND_SEQ_SAMPLE_STOP_IMMEDIATELY = 0, /**< terminate playing immediately */ + SND_SEQ_SAMPLE_STOP_VENVELOPE = 1, /**< finish volume envelope */ + SND_SEQ_SAMPLE_STOP_LOOP = 2 /**< terminate loop and finish wave */ } snd_seq_stop_mode_t; /** sample frequency */ @@ -365,94 +365,94 @@ /** sample volume control; if any value is set to -1 == do not change */ typedef struct snd_seq_ev_volume { - signed short volume; /**< range: 0-16383 */ - signed short lr; /**< left-right balance; range: 0-16383 */ - signed short fr; /**< front-rear balance; range: 0-16383 */ - signed short du; /**< down-up balance; range: 0-16383 */ + signed short volume; /**< range: 0-16383 */ + signed short lr; /**< left-right balance; range: 0-16383 */ + signed short fr; /**< front-rear balance; range: 0-16383 */ + signed short du; /**< down-up balance; range: 0-16383 */ } snd_seq_ev_volume_t; /** simple loop redefinition */ typedef struct snd_seq_ev_loop { - unsigned int start; /**< loop start (in samples) * 16 */ - unsigned int end; /**< loop end (in samples) * 16 */ + unsigned int start; /**< loop start (in samples) * 16 */ + unsigned int end; /**< loop end (in samples) * 16 */ } snd_seq_ev_loop_t; /** Sample control events */ typedef struct snd_seq_ev_sample_control { - unsigned char channel; /**< channel */ - unsigned char unused[3]; /**< reserved */ - union { - snd_seq_ev_sample_t sample; /**< sample number */ - snd_seq_ev_cluster_t cluster; /**< cluster number */ - snd_seq_position_t position; /**< position */ - snd_seq_stop_mode_t stop_mode; /**< stop mode */ - snd_seq_frequency_t frequency; /**< frequency */ - snd_seq_ev_volume_t volume; /**< volume */ - snd_seq_ev_loop_t loop; /**< loop control */ - unsigned char raw8[8]; /**< raw 8-bit */ - } param; /**< control parameters */ + unsigned char channel; /**< channel */ + unsigned char unused[3]; /**< reserved */ + union { + snd_seq_ev_sample_t sample; /**< sample number */ + snd_seq_ev_cluster_t cluster; /**< cluster number */ + snd_seq_position_t position; /**< position */ + snd_seq_stop_mode_t stop_mode; /**< stop mode */ + snd_seq_frequency_t frequency; /**< frequency */ + snd_seq_ev_volume_t volume; /**< volume */ + snd_seq_ev_loop_t loop; /**< loop control */ + unsigned char raw8[8]; /**< raw 8-bit */ + } param; /**< control parameters */ } snd_seq_ev_sample_control_t; /** INSTR_BEGIN event */ typedef struct snd_seq_ev_instr_begin { - int timeout; /**< zero = forever, otherwise timeout in ms */ + int timeout; /**< zero = forever, otherwise timeout in ms */ } snd_seq_ev_instr_begin_t; /** Result events */ typedef struct snd_seq_result { - int event; /**< processed event type */ - int result; /**< status */ + int event; /**< processed event type */ + int result; /**< status */ } snd_seq_result_t; /** Queue skew values */ typedef struct snd_seq_queue_skew { - unsigned int value; /**< skew value */ - unsigned int base; /**< skew base */ + unsigned int value; /**< skew value */ + unsigned int base; /**< skew base */ } snd_seq_queue_skew_t; /** queue timer control */ typedef struct snd_seq_ev_queue_control { - unsigned char queue; /**< affected queue */ - unsigned char unused[3]; /**< reserved */ - union { - signed int value; /**< affected value (e.g. tempo) */ - snd_seq_timestamp_t time; /**< time */ - unsigned int position; /**< sync position */ - snd_seq_queue_skew_t skew; /**< queue skew */ - unsigned int d32[2]; /**< any data */ - unsigned char d8[8]; /**< any data */ - } param; /**< data value union */ + unsigned char queue; /**< affected queue */ + unsigned char unused[3]; /**< reserved */ + union { + signed int value; /**< affected value (e.g. tempo) */ + snd_seq_timestamp_t time; /**< time */ + unsigned int position; /**< sync position */ + snd_seq_queue_skew_t skew; /**< queue skew */ + unsigned int d32[2]; /**< any data */ + unsigned char d8[8]; /**< any data */ + } param; /**< data value union */ } snd_seq_ev_queue_control_t; /** Sequencer event */ typedef struct snd_seq_event { - snd_seq_event_type_t type; /**< event type */ - unsigned char flags; /**< event flags */ - unsigned char tag; /**< tag */ - - unsigned char queue; /**< schedule queue */ - snd_seq_timestamp_t time; /**< schedule time */ + snd_seq_event_type_t type; /**< event type */ + unsigned char flags; /**< event flags */ + unsigned char tag; /**< tag */ + + unsigned char queue; /**< schedule queue */ + snd_seq_timestamp_t time; /**< schedule time */ - snd_seq_addr_t source; /**< source address */ - snd_seq_addr_t dest; /**< destination address */ + snd_seq_addr_t source; /**< source address */ + snd_seq_addr_t dest; /**< destination address */ - union { - snd_seq_ev_note_t note; /**< note information */ - snd_seq_ev_ctrl_t control; /**< MIDI control information */ - snd_seq_ev_raw8_t raw8; /**< raw8 data */ - snd_seq_ev_raw32_t raw32; /**< raw32 data */ - snd_seq_ev_ext_t ext; /**< external data */ - snd_seq_ev_queue_control_t queue; /**< queue control */ - snd_seq_timestamp_t time; /**< timestamp */ - snd_seq_addr_t addr; /**< address */ - snd_seq_connect_t connect; /**< connect information */ - snd_seq_result_t result; /**< operation result code */ - snd_seq_ev_instr_begin_t instr_begin; /**< instrument */ - snd_seq_ev_sample_control_t sample; /**< sample control */ - } data; /**< event data... */ + union { + snd_seq_ev_note_t note; /**< note information */ + snd_seq_ev_ctrl_t control; /**< MIDI control information */ + snd_seq_ev_raw8_t raw8; /**< raw8 data */ + snd_seq_ev_raw32_t raw32; /**< raw32 data */ + snd_seq_ev_ext_t ext; /**< external data */ + snd_seq_ev_queue_control_t queue; /**< queue control */ + snd_seq_timestamp_t time; /**< timestamp */ + snd_seq_addr_t addr; /**< address */ + snd_seq_connect_t connect; /**< connect information */ + snd_seq_result_t result; /**< operation result code */ + snd_seq_ev_instr_begin_t instr_begin; /**< instrument */ + snd_seq_ev_sample_control_t sample; /**< sample control */ + } data; /**< event data... */ } snd_seq_event_t; diff -r d4a28d1479a8 -r 710e6250a401 plugin/api/alsa/sound/asequencer.h --- a/plugin/api/alsa/sound/asequencer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/api/alsa/sound/asequencer.h Mon Sep 17 13:51:14 2018 +0100 @@ -28,11 +28,11 @@ #ifndef __SOUND_ASEQUENCER_H #define __SOUND_ASEQUENCER_H -#define SNDRV_SEQ_EVENT_SYSEX 130 /* system exclusive data (variable length) */ +#define SNDRV_SEQ_EVENT_SYSEX 130 /* system exclusive data (variable length) */ -#define SNDRV_SEQ_EVENT_LENGTH_FIXED (0<<2) /* fixed event size */ -#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /* variable event size */ +#define SNDRV_SEQ_EVENT_LENGTH_FIXED (0<<2) /* fixed event size */ +#define SNDRV_SEQ_EVENT_LENGTH_VARIABLE (1<<2) /* variable event size */ -#define SNDRV_SEQ_EVENT_LENGTH_MASK (3<<2) +#define SNDRV_SEQ_EVENT_LENGTH_MASK (3<<2) #endif /* __SOUND_ASEQUENCER_H */ diff -r d4a28d1479a8 -r 710e6250a401 plugin/api/dssi.h --- a/plugin/api/dssi.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/api/dssi.h Mon Sep 17 13:51:14 2018 +0100 @@ -71,9 +71,9 @@ unsigned long Bank; /** Program number (unique within its bank) for this program. - There is no restriction on the set of available programs: the - numbers do not need to be contiguous, there does not need to - be a program 0, etc. */ + There is no restriction on the set of available programs: the + numbers do not need to be contiguous, there does not need to + be a program 0, etc. */ unsigned long Program; /** Name of the program. */ @@ -248,13 +248,13 @@ * See also the configure OSC call documentation in RFC.txt. */ char *(*configure)(LADSPA_Handle Instance, - const char *Key, - const char *Value); + const char *Key, + const char *Value); #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:" #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:" #define DSSI_PROJECT_DIRECTORY_KEY \ - DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" + DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" /** * get_program() @@ -278,7 +278,7 @@ * programs as well as their properties. */ const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance, - unsigned long Index); + unsigned long Index); /** * select_program() @@ -308,8 +308,8 @@ * which a DSSI plugin is allowed to modify its own input ports.) */ void (*select_program)(LADSPA_Handle Instance, - unsigned long Bank, - unsigned long Program); + unsigned long Bank, + unsigned long Program); /** * get_midi_controller_for_port() @@ -338,7 +338,7 @@ * controllers 0 or 32 (MIDI Bank Select MSB and LSB). */ int (*get_midi_controller_for_port)(LADSPA_Handle Instance, - unsigned long Port); + unsigned long Port); /** * run_synth() @@ -388,9 +388,9 @@ * select controller to a plugin via run_synth. */ void (*run_synth)(LADSPA_Handle Instance, - unsigned long SampleCount, - snd_seq_event_t *Events, - unsigned long EventCount); + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); /** * run_synth_adding() @@ -402,9 +402,9 @@ * that does not provide it must set this member to NULL. */ void (*run_synth_adding)(LADSPA_Handle Instance, - unsigned long SampleCount, - snd_seq_event_t *Events, - unsigned long EventCount); + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); /** * run_multiple_synths() @@ -609,7 +609,7 @@ */ int (*request_non_rt_thread)(LADSPA_Handle Instance, - void (*RunFunction)(LADSPA_Handle Instance)); + void (*RunFunction)(LADSPA_Handle Instance)); }; /** @@ -641,19 +641,19 @@ * get_midi_controller_for_port() */ -#define DSSI_CC_BITS 0x20000000 -#define DSSI_NRPN_BITS 0x40000000 +#define DSSI_CC_BITS 0x20000000 +#define DSSI_NRPN_BITS 0x40000000 -#define DSSI_NONE -1 -#define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) +#define DSSI_NONE -1 +#define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) -#define DSSI_CC(n) (DSSI_CC_BITS | (n)) -#define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) -#define DSSI_CC_NUMBER(n) ((n) & 0x7f) +#define DSSI_CC(n) (DSSI_CC_BITS | (n)) +#define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) +#define DSSI_CC_NUMBER(n) ((n) & 0x7f) -#define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) -#define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) -#define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) +#define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) +#define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) +#define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) #ifdef __cplusplus } diff -r d4a28d1479a8 -r 710e6250a401 plugin/plugins/SamplePlayer.cpp --- a/plugin/plugins/SamplePlayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/plugins/SamplePlayer.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -29,6 +29,11 @@ #include #include +#ifdef Q_OS_WIN +#include +#define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#endif + #include #include #include @@ -152,17 +157,17 @@ SamplePlayer::instantiate(const LADSPA_Descriptor *, unsigned long rate) { if (!hostDescriptor || !hostDescriptor->request_non_rt_thread) { - SVDEBUG << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << endl; - return 0; + SVDEBUG << "SamplePlayer::instantiate: Host does not provide request_non_rt_thread, not instantiating" << endl; + return 0; } SamplePlayer *player = new SamplePlayer(int(rate)); - // std::cerr << "Instantiated sample player " << std::endl; + // std::cerr << "Instantiated sample player " << std::endl; if (hostDescriptor->request_non_rt_thread(player, workThreadCallback)) { - SVDEBUG << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << endl; - delete player; - return 0; + SVDEBUG << "SamplePlayer::instantiate: Host rejected request_non_rt_thread call, not instantiating" << endl; + delete player; + return 0; } return player; @@ -170,17 +175,17 @@ void SamplePlayer::connectPort(LADSPA_Handle handle, - unsigned long port, LADSPA_Data *location) + unsigned long port, LADSPA_Data *location) { SamplePlayer *player = (SamplePlayer *)handle; float **ports[PortCount] = { - &player->m_output, - &player->m_retune, - &player->m_basePitch, + &player->m_output, + &player->m_retune, + &player->m_basePitch, &player->m_concertA, - &player->m_sustain, - &player->m_release + &player->m_sustain, + &player->m_release }; *ports[port] = (float *)location; @@ -195,9 +200,9 @@ player->m_sampleNo = 0; for (size_t i = 0; i < Polyphony; ++i) { - player->m_ons[i] = -1; - player->m_offs[i] = -1; - player->m_velocities[i] = 0; + player->m_ons[i] = -1; + player->m_offs[i] = -1; + player->m_velocities[i] = 0; } } @@ -226,7 +231,7 @@ SamplePlayer *player = (SamplePlayer *)handle; - QMutexLocker locker(&player->m_mutex); + QMutexLocker locker(&player->m_mutex); if (QFileInfo(value).exists() && QFileInfo(value).isDir()) { @@ -256,17 +261,17 @@ SamplePlayer *player = (SamplePlayer *)handle; if (!player->m_sampleSearchComplete) { - QMutexLocker locker(&player->m_mutex); - if (!player->m_sampleSearchComplete) { - player->searchSamples(); - } + QMutexLocker locker(&player->m_mutex); + if (!player->m_sampleSearchComplete) { + player->searchSamples(); + } } if (program >= player->m_samples.size()) return 0; static DSSI_Program_Descriptor descriptor; static char name[60]; - strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 60); + strncpy(name, player->m_samples[program].first.toLocal8Bit().data(), 59); name[59] = '\0'; descriptor.Bank = 0; @@ -278,8 +283,8 @@ void SamplePlayer::selectProgram(LADSPA_Handle handle, - unsigned long, - unsigned long program) + unsigned long, + unsigned long program) { SamplePlayer *player = (SamplePlayer *)handle; player->m_pendingProgramChange = (int)program; @@ -289,11 +294,11 @@ SamplePlayer::getMidiController(LADSPA_Handle, unsigned long port) { int controllers[PortCount] = { - DSSI_NONE, - DSSI_CC(12), - DSSI_CC(13), - DSSI_CC(64), - DSSI_CC(72) + DSSI_NONE, + DSSI_CC(12), + DSSI_CC(13), + DSSI_CC(64), + DSSI_CC(72) }; return controllers[port]; @@ -301,7 +306,7 @@ void SamplePlayer::runSynth(LADSPA_Handle handle, unsigned long samples, - snd_seq_event_t *events, unsigned long eventCount) + snd_seq_event_t *events, unsigned long eventCount) { SamplePlayer *player = (SamplePlayer *)handle; @@ -322,38 +327,38 @@ if (player->m_pendingProgramChange >= 0) { #ifdef DEBUG_SAMPLE_PLAYER - SVDEBUG << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << endl; + SVDEBUG << "SamplePlayer::workThreadCallback: pending program change " << player->m_pendingProgramChange << endl; #endif - player->m_mutex.lock(); + player->m_mutex.lock(); - int program = player->m_pendingProgramChange; - player->m_pendingProgramChange = -1; + int program = player->m_pendingProgramChange; + player->m_pendingProgramChange = -1; - if (!player->m_sampleSearchComplete) { - player->searchSamples(); - } - - if (program < int(player->m_samples.size())) { - QString path = player->m_samples[program].second; - QString programName = player->m_samples[program].first; - if (programName != player->m_program) { - player->m_program = programName; - player->m_mutex.unlock(); - player->loadSampleData(path); - } else { - player->m_mutex.unlock(); - } - } + if (!player->m_sampleSearchComplete) { + player->searchSamples(); + } + + if (program < int(player->m_samples.size())) { + QString path = player->m_samples[program].second; + QString programName = player->m_samples[program].first; + if (programName != player->m_program) { + player->m_program = programName; + player->m_mutex.unlock(); + player->loadSampleData(path); + } else { + player->m_mutex.unlock(); + } + } } if (!player->m_sampleSearchComplete) { - QMutexLocker locker(&player->m_mutex); + QMutexLocker locker(&player->m_mutex); - if (!player->m_sampleSearchComplete) { - player->searchSamples(); - } + if (!player->m_sampleSearchComplete) { + player->searchSamples(); + } } } @@ -366,7 +371,7 @@ #ifdef DEBUG_SAMPLE_PLAYER SVDEBUG << "SamplePlayer::searchSamples: Directory is \"" - << m_sampleDir << "\"" << endl; + << m_sampleDir << "\"" << endl; #endif QDir dir(m_sampleDir, "*.wav"); @@ -395,12 +400,16 @@ size_t i; info.format = 0; +#ifdef Q_OS_WIN + file = sf_wchar_open((LPCWSTR)path.utf16(), SFM_READ, &info); +#else file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info); +#endif if (!file) { - cerr << "SamplePlayer::loadSampleData: Failed to open file " - << path << ": " - << sf_strerror(file) << endl; - return; + cerr << "SamplePlayer::loadSampleData: Failed to open file " + << path << ": " + << sf_strerror(file) << endl; + return; } samples = info.frames; @@ -413,47 +422,47 @@ tmpResamples = 0; if (info.samplerate != m_sampleRate) { - - double ratio = (double)m_sampleRate / (double)info.samplerate; - size_t target = (size_t)(double(info.frames) * ratio); - SRC_DATA data; + + double ratio = (double)m_sampleRate / (double)info.samplerate; + size_t target = (size_t)(double(info.frames) * ratio); + SRC_DATA data; - tmpResamples = (float *)malloc(target * info.channels * sizeof(float)); - if (!tmpResamples) { - free(tmpFrames); - return; - } + tmpResamples = (float *)malloc(target * info.channels * sizeof(float)); + if (!tmpResamples) { + free(tmpFrames); + return; + } - memset(tmpResamples, 0, target * info.channels * sizeof(float)); + memset(tmpResamples, 0, target * info.channels * sizeof(float)); - data.data_in = tmpFrames; - data.data_out = tmpResamples; - data.input_frames = info.frames; - data.output_frames = target; - data.src_ratio = ratio; + data.data_in = tmpFrames; + data.data_out = tmpResamples; + data.input_frames = info.frames; + data.output_frames = target; + data.src_ratio = ratio; - if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) { - free(tmpFrames); - tmpFrames = tmpResamples; - samples = target; - } else { - free(tmpResamples); - } + if (!src_simple(&data, SRC_SINC_BEST_QUALITY, info.channels)) { + free(tmpFrames); + tmpFrames = tmpResamples; + samples = target; + } else { + free(tmpResamples); + } } /* add an extra sample for linear interpolation */ tmpSamples = (float *)malloc((samples + 1) * sizeof(float)); if (!tmpSamples) { - free(tmpFrames); - return; + free(tmpFrames); + return; } for (i = 0; i < samples; ++i) { - int j; - tmpSamples[i] = 0.0f; - for (j = 0; j < info.channels; ++j) { - tmpSamples[i] += tmpFrames[i * info.channels + j]; - } + int j; + tmpSamples[i] = 0.0f; + for (j = 0; j < info.channels; ++j) { + tmpSamples[i] += tmpFrames[i * info.channels + j]; + } } free(tmpFrames); @@ -468,9 +477,9 @@ m_sampleCount = samples; for (i = 0; i < Polyphony; ++i) { - m_ons[i] = -1; - m_offs[i] = -1; - m_velocities[i] = 0; + m_ons[i] = -1; + m_offs[i] = -1; + m_velocities[i] = 0; } if (tmpOld) free(tmpOld); @@ -480,8 +489,8 @@ void SamplePlayer::runImpl(unsigned long sampleCount, - snd_seq_event_t *events, - unsigned long eventCount) + snd_seq_event_t *events, + unsigned long eventCount) { unsigned long pos; unsigned long count; @@ -493,67 +502,67 @@ if (!m_mutex.tryLock()) return; if (!m_sampleData || !m_sampleCount) { - m_sampleNo += sampleCount; - m_mutex.unlock(); - return; + m_sampleNo += sampleCount; + m_mutex.unlock(); + return; } for (pos = 0, event_pos = 0; pos < sampleCount; ) { - while (event_pos < eventCount - && pos >= events[event_pos].time.tick) { + while (event_pos < eventCount + && pos >= events[event_pos].time.tick) { - if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) { + if (events[event_pos].type == SND_SEQ_EVENT_NOTEON) { #ifdef DEBUG_SAMPLE_PLAYER cerr << "SamplePlayer: found NOTEON at time " << events[event_pos].time.tick << endl; #endif - snd_seq_ev_note_t n = events[event_pos].data.note; - if (n.velocity > 0) { - m_ons[n.note] = - m_sampleNo + events[event_pos].time.tick; - m_offs[n.note] = -1; - m_velocities[n.note] = n.velocity; - } else { - if (!m_sustain || (*m_sustain < 0.001)) { - m_offs[n.note] = - m_sampleNo + events[event_pos].time.tick; - } - } - } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF && - (!m_sustain || (*m_sustain < 0.001))) { + snd_seq_ev_note_t n = events[event_pos].data.note; + if (n.velocity > 0) { + m_ons[n.note] = + m_sampleNo + events[event_pos].time.tick; + m_offs[n.note] = -1; + m_velocities[n.note] = n.velocity; + } else { + if (!m_sustain || (*m_sustain < 0.001)) { + m_offs[n.note] = + m_sampleNo + events[event_pos].time.tick; + } + } + } else if (events[event_pos].type == SND_SEQ_EVENT_NOTEOFF && + (!m_sustain || (*m_sustain < 0.001))) { #ifdef DEBUG_SAMPLE_PLAYER cerr << "SamplePlayer: found NOTEOFF at time " << events[event_pos].time.tick << endl; #endif - snd_seq_ev_note_t n = events[event_pos].data.note; - m_offs[n.note] = - m_sampleNo + events[event_pos].time.tick; - } + snd_seq_ev_note_t n = events[event_pos].data.note; + m_offs[n.note] = + m_sampleNo + events[event_pos].time.tick; + } - ++event_pos; - } + ++event_pos; + } - count = sampleCount - pos; - if (event_pos < eventCount && - events[event_pos].time.tick < sampleCount) { - count = events[event_pos].time.tick - pos; - } + count = sampleCount - pos; + if (event_pos < eventCount && + events[event_pos].time.tick < sampleCount) { + count = events[event_pos].time.tick - pos; + } int notecount = 0; - for (i = 0; i < Polyphony; ++i) { - if (m_ons[i] >= 0) { + for (i = 0; i < Polyphony; ++i) { + if (m_ons[i] >= 0) { ++notecount; - addSample(i, pos, count); - } - } + addSample(i, pos, count); + } + } #ifdef DEBUG_SAMPLE_PLAYER cerr << "SamplePlayer: have " << notecount << " note(s) sounding currently" << endl; #endif - pos += count; + pos += count; } m_sampleNo += sampleCount; @@ -571,9 +580,9 @@ if (m_concertA) { ratio *= *m_concertA / 440.f; } - if (m_basePitch && float(n) != *m_basePitch) { - ratio *= powf(1.059463094f, float(n) - *m_basePitch); - } + if (m_basePitch && float(n) != *m_basePitch) { + ratio *= powf(1.059463094f, float(n) - *m_basePitch); + } } if (long(pos + m_sampleNo) < m_ons[n]) return; @@ -581,49 +590,49 @@ gain = (float)m_velocities[n] / 127.0f; for (i = 0, s = pos + m_sampleNo - m_ons[n]; - i < count; - ++i, ++s) { + i < count; + ++i, ++s) { - float lgain = gain; - float rs = float(s) * ratio; - unsigned long rsi = lrintf(floorf(rs)); + float lgain = gain; + float rs = float(s) * ratio; + unsigned long rsi = lrintf(floorf(rs)); - if (rsi >= m_sampleCount) { + if (rsi >= m_sampleCount) { #ifdef DEBUG_SAMPLE_PLAYER cerr << "Note " << n << " has run out of samples (were " << m_sampleCount << " available at ratio " << ratio << "), ending" << endl; #endif - m_ons[n] = -1; - break; - } + m_ons[n] = -1; + break; + } - if (m_offs[n] >= 0 && - long(pos + i + m_sampleNo) > m_offs[n]) { + if (m_offs[n] >= 0 && + long(pos + i + m_sampleNo) > m_offs[n]) { - unsigned long dist = - pos + i + m_sampleNo - m_offs[n]; + unsigned long dist = + pos + i + m_sampleNo - m_offs[n]; - unsigned long releaseFrames = 200; - if (m_release) { - releaseFrames = long(*m_release * float(m_sampleRate) + 0.0001f); - } + unsigned long releaseFrames = 200; + if (m_release) { + releaseFrames = long(*m_release * float(m_sampleRate) + 0.0001f); + } - if (dist > releaseFrames) { + if (dist > releaseFrames) { #ifdef DEBUG_SAMPLE_PLAYER cerr << "Note " << n << " has expired its release time (" << releaseFrames << " frames), ending" << endl; #endif - m_ons[n] = -1; - break; - } else { - lgain = lgain * (float)(releaseFrames - dist) / - (float)releaseFrames; - } - } - - float sample = m_sampleData[rsi] + - ((m_sampleData[rsi + 1] - - m_sampleData[rsi]) * - (rs - (float)rsi)); + m_ons[n] = -1; + break; + } else { + lgain = lgain * (float)(releaseFrames - dist) / + (float)releaseFrames; + } + } + + float sample = m_sampleData[rsi] + + ((m_sampleData[rsi + 1] - + m_sampleData[rsi]) * + (rs - (float)rsi)); - m_output[pos + i] += lgain * sample; + m_output[pos + i] += lgain * sample; } } diff -r d4a28d1479a8 -r 710e6250a401 plugin/plugins/SamplePlayer.h --- a/plugin/plugins/SamplePlayer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/plugin/plugins/SamplePlayer.h Mon Sep 17 13:51:14 2018 +0100 @@ -37,17 +37,17 @@ ~SamplePlayer(); enum { - OutputPort = 0, - RetunePort = 1, - BasePitchPort = 2, + OutputPort = 0, + RetunePort = 1, + BasePitchPort = 2, ConcertAPort = 3, - SustainPort = 4, - ReleasePort = 5, - PortCount = 6 + SustainPort = 4, + ReleasePort = 5, + PortCount = 6 }; enum { - Polyphony = 128 + Polyphony = 128 }; static const char *const portNames[PortCount]; @@ -69,7 +69,7 @@ static void selectProgram(LADSPA_Handle, unsigned long, unsigned long); static int getMidiController(LADSPA_Handle, unsigned long); static void runSynth(LADSPA_Handle, unsigned long, - snd_seq_event_t *, unsigned long); + snd_seq_event_t *, unsigned long); static void receiveHostDescriptor(const DSSI_Host_Descriptor *descriptor); static void workThreadCallback(LADSPA_Handle); diff -r d4a28d1479a8 -r 710e6250a401 rdf/RDFFeatureWriter.cpp --- a/rdf/RDFFeatureWriter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/rdf/RDFFeatureWriter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -146,14 +146,14 @@ m_rdfDescriptions[pluginId] = PluginRDFDescription(pluginId); if (m_rdfDescriptions[pluginId].haveDescription()) { - cerr << "NOTE: Have RDF description for plugin ID \"" + SVCERR << "NOTE: Have RDF description for plugin ID \"" << pluginId << "\"" << endl; } else { - cerr << "NOTE: No RDF description for plugin ID \"" + SVCERR << "NOTE: No RDF description for plugin ID \"" << pluginId << "\"" << endl; if (!m_network) { - cerr << " Consider using the --rdf-network option to retrieve plugin descriptions" << endl; - cerr << " from the network where possible." << endl; + SVCERR << " Consider using the --rdf-network option to retrieve plugin descriptions" << endl; + SVCERR << " from the network where possible." << endl; } } } @@ -194,7 +194,7 @@ QString timelineURI = m_trackTimelineURIs[trackId]; if (timelineURI == "") { - cerr << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl; + SVCERR << "RDFFeatureWriter: INTERNAL ERROR: writing features without having established a timeline URI!" << endl; exit(1); } @@ -211,7 +211,7 @@ QString signalURI = m_trackSignalURIs[trackId]; if (signalURI == "") { - cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl; + SVCERR << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having established a signal URI!" << endl; exit(1); } @@ -229,7 +229,7 @@ QString signalURI = m_trackSignalURIs[trackId]; if (signalURI == "") { - cerr << "RDFFeatureWriter: INTERNAL ERROR: writing track-level features without having established a signal URI!" << endl; + SVCERR << "RDFFeatureWriter: INTERNAL ERROR: writing track-level features without having established a signal URI!" << endl; exit(1); } @@ -702,19 +702,19 @@ sampleRate = transform.getSampleRate(); if (sampleRate == 0.f) { - cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl; + SVCERR << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the sample rate properly!" << endl; return; } stepSize = transform.getStepSize(); if (stepSize == 0) { - cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl; + SVCERR << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the step size properly!" << endl; return; } blockSize = transform.getBlockSize(); if (blockSize == 0) { - cerr << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl; + SVCERR << "RDFFeatureWriter: INTERNAL ERROR: writing dense features without having set the block size properly!" << endl; return; } } diff -r d4a28d1479a8 -r 710e6250a401 rdf/RDFImporter.cpp --- a/rdf/RDFImporter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/rdf/RDFImporter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -818,7 +818,7 @@ store = BasicStore::load(QUrl(url)); Triple t = store->matchOnce(Triple()); if (t != Triple()) haveRDF = true; - } catch (std::exception &e) { + } catch (std::exception &) { // nothing; haveRDF will be false so the next bit catches it } diff -r d4a28d1479a8 -r 710e6250a401 system/System.cpp --- a/system/System.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/system/System.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -1,16 +1,16 @@ /* -*- 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 Chris Cannam and QMUL. + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2006-2018 Chris Cannam and 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. + 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 "System.h" @@ -39,16 +39,16 @@ #ifdef __APPLE__ extern "C" { -void * -rpl_realloc (void *p, size_t n) -{ - p = realloc(p, n); - if (p == 0 && n == 0) + void * + rpl_realloc (void *p, size_t n) { - p = malloc(0); + p = realloc(p, n); + if (p == 0 && n == 0) + { + p = malloc(0); + } + return p; } - return p; -} } #endif @@ -57,24 +57,24 @@ extern "C" { #ifdef _MSC_VER -void usleep(unsigned long usec) -{ - ::Sleep(usec / 1000); -} + void usleep(unsigned long usec) + { + ::Sleep(usec / 1000); + } #endif -int gettimeofday(struct timeval *tv, void *tz) -{ - union { - long long ns100; - FILETIME ft; - } now; + int gettimeofday(struct timeval *tv, void * /* tz */) + { + union { + long long ns100; + FILETIME ft; + } now; - ::GetSystemTimeAsFileTime(&now.ft); - tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL); - tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); - return 0; -} + ::GetSystemTimeAsFileTime(&now.ft); + tv->tv_usec = (long)((now.ns100 / 10LL) % 1000000LL); + tv->tv_sec = (long)((now.ns100 - 116444736000000000LL) / 10000000LL); + return 0; + } } @@ -153,10 +153,10 @@ if (exFound) { lMEMORYSTATUSEX lms; - lms.dwLength = sizeof(lms); - if (!ex(&lms)) { + lms.dwLength = sizeof(lms); + if (!ex(&lms)) { cerr << "WARNING: GlobalMemoryStatusEx failed: error code " - << GetLastError() << endl; + << GetLastError() << endl; return; } wavail = lms.ullAvailPhys; @@ -167,9 +167,9 @@ /* Fall back to GlobalMemoryStatus which is always available. but returns wrong results for physical memory > 4GB */ - MEMORYSTATUS ms; - GlobalMemoryStatus(&ms); - wavail = ms.dwAvailPhys; + MEMORYSTATUS ms; + GlobalMemoryStatus(&ms); + wavail = ms.dwAvailPhys; wtotal = ms.dwTotalPhys; } @@ -211,7 +211,10 @@ char buf[256]; while (!feof(meminfo)) { - fgets(buf, 256, meminfo); + if (!fgets(buf, 256, meminfo)) { + fclose(meminfo); + return; + } bool isMemFree = (strncmp(buf, "MemFree:", 8) == 0); bool isMemTotal = (!isMemFree && (strncmp(buf, "MemTotal:", 9) == 0)); if (isMemFree || isMemTotal) { @@ -249,13 +252,13 @@ #ifdef _WIN32 ULARGE_INTEGER available, total, totalFree; if (GetDiskFreeSpaceExA(path, &available, &total, &totalFree)) { - __int64 a = available.QuadPart; + __int64 a = available.QuadPart; a /= 1048576; if (a > INT_MAX) a = INT_MAX; return ssize_t(a); } else { cerr << "WARNING: GetDiskFreeSpaceEx failed: error code " - << GetLastError() << endl; + << GetLastError() << endl; return -1; } #else @@ -286,7 +289,7 @@ #endif } #else /* !_WIN32 */ -#if !defined(__APPLE__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ == 0)) +#if !defined(__APPLE__) && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ == 0)) void SystemMemoryBarrier() { @@ -325,4 +328,109 @@ double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } float princargf(float a) { return float(princarg(a)); } +bool +getEnvUtf8(std::string variable, std::string &value) +{ + value = ""; + +#ifdef _WIN32 + int wvarlen = MultiByteToWideChar(CP_UTF8, 0, + variable.c_str(), int(variable.length()), + 0, 0); + if (wvarlen < 0) { + SVCERR << "WARNING: Unable to convert environment variable name " + << variable << " to wide characters" << endl; + return false; + } + + wchar_t *wvarbuf = new wchar_t[wvarlen + 1]; + (void)MultiByteToWideChar(CP_UTF8, 0, + variable.c_str(), int(variable.length()), + wvarbuf, wvarlen); + wvarbuf[wvarlen] = L'\0'; + + wchar_t *wvalue = _wgetenv(wvarbuf); + delete[] wvarbuf; + + if (!wvalue) { + return false; + } + + int wvallen = int(wcslen(wvalue)); + int vallen = WideCharToMultiByte(CP_UTF8, 0, + wvalue, wvallen, + 0, 0, 0, 0); + if (vallen < 0) { + SVCERR << "WARNING: Unable to convert environment value to UTF-8" + << endl; + return false; + } + + char *val = new char[vallen + 1]; + (void)WideCharToMultiByte(CP_UTF8, 0, + wvalue, wvallen, + val, vallen, 0, 0); + val[vallen] = '\0'; + + value = val; + + delete[] val; + return true; + +#else + + char *val = getenv(variable.c_str()); + if (!val) { + return false; + } + + value = val; + return true; + +#endif +} + +bool +putEnvUtf8(std::string variable, std::string value) +{ +#ifdef _WIN32 + std::string entry = variable + "=" + value; + + int wentlen = MultiByteToWideChar(CP_UTF8, 0, + entry.c_str(), int(entry.length()), + 0, 0); + if (wentlen < 0) { + SVCERR << "WARNING: Unable to convert environment entry to " + << "wide characters" << endl; + return false; + } + + wchar_t *wentbuf = new wchar_t[wentlen + 1]; + (void)MultiByteToWideChar(CP_UTF8, 0, + entry.c_str(), int(entry.length()), + wentbuf, wentlen); + wentbuf[wentlen] = L'\0'; + + int rv = _wputenv(wentbuf); + + delete[] wentbuf; + + if (rv != 0) { + SVCERR << "WARNING: Failed to set environment entry" << endl; + return false; + } + return true; + +#else + + int rv = setenv(variable.c_str(), value.c_str(), 1); + if (rv != 0) { + SVCERR << "WARNING: Failed to set environment entry" << endl; + return false; + } + return true; + +#endif +} + diff -r d4a28d1479a8 -r 710e6250a401 system/System.h --- a/system/System.h Mon Dec 12 15:18:52 2016 +0000 +++ b/system/System.h Mon Sep 17 13:51:14 2018 +0100 @@ -4,7 +4,7 @@ Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. - This file copyright 2006 Chris Cannam and QMUL. + This file copyright 2006-2018 Chris Cannam and QMUL. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _SYSTEM_H_ -#define _SYSTEM_H_ +#ifndef SV_SYSTEM_H +#define SV_SYSTEM_H #include "base/Debug.h" @@ -154,7 +154,8 @@ extern ProcessStatus GetProcessStatus(int pid); // Return a vague approximation to the number of free megabytes of real memory. -// Return -1 if unknown. (Hence signed args) +// Return -1 if unknown. (Hence signed args.) Note that this could be more than +// is actually addressable, e.g. for a 32-bit process on a 64-bit system. extern void GetRealMemoryMBAvailable(ssize_t &available, ssize_t &total); // Return a vague approximation to the number of free megabytes of @@ -181,6 +182,18 @@ #define powf pow #endif +/** Return the value of the given environment variable by reference. + Return true if successfully retrieved, false if unset or on error. + Both the variable name and the returned value are UTF-8 encoded. +*/ +extern bool getEnvUtf8(std::string variable, std::string &value); + +/** Set the value of the given environment variable. + Return true if successfully set, false on error. + Both the variable name and the value must be UTF-8 encoded. +*/ +extern bool putEnvUtf8(std::string variable, std::string value); + #endif /* ! _SYSTEM_H_ */ diff -r d4a28d1479a8 -r 710e6250a401 system/test/TestEnv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system/test/TestEnv.h Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,107 @@ +/* -*- 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 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 TEST_ENV_H +#define TEST_ENV_H + +#include "../System.h" + +#include +#include +#include + +#include + +using namespace std; + +const std::string utf8_name_sprkt = "\343\202\271\343\203\235\343\203\274\343\202\257\343\201\256\345\257\272\351\231\242"; + +class TestEnv : public QObject +{ + Q_OBJECT + +private slots: + void getAbsent() + { + string value = "blather"; + bool rv = getEnvUtf8("nonexistent_environment_variable_I_sincerely_hope_including_a_missspellling_just_to_be_sure", + value); + QCOMPARE(rv, false); + QCOMPARE(value, string()); + } + + void getExpected() + { + string value; + bool rv = getEnvUtf8("PATH", value); + QCOMPARE(rv, true); + QVERIFY(value != ""); + QVERIFY(value.size() > 5); // Not quite but nearly certain, + // and weeds out an unfortunate + // case where we accidentally + // returned the variable's name + // instead of its value! + } + + void roundTripAsciiAscii() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_A", "EXPECTED_VALUE"); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_A", value); + QCOMPARE(rv, true); + QCOMPARE(value, string("EXPECTED_VALUE")); + } + + void roundTripAsciiUtf8() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_U", utf8_name_sprkt); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_A_U", value); + QCOMPARE(rv, true); + QCOMPARE(value, utf8_name_sprkt); + } + + void roundTripUtf8Ascii() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", "EXPECTED_VALUE"); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", value); + QCOMPARE(rv, true); + QCOMPARE(value, string("EXPECTED_VALUE")); + } + + void roundTripUtf8Utf8() + { + bool rv = false; + rv = putEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", utf8_name_sprkt); + QCOMPARE(rv, true); + string value; + rv = getEnvUtf8("SV_CORE_TEST_SYSTEM_RT_\351\207\215\345\272\206_A", value); + QCOMPARE(rv, true); + QCOMPARE(value, utf8_name_sprkt); + } +}; + +#endif + + + + + diff -r d4a28d1479a8 -r 710e6250a401 system/test/files.pri --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system/test/files.pri Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,5 @@ +TEST_HEADERS = \ + TestEnv.h + +TEST_SOURCES += \ + svcore-system-test.cpp diff -r d4a28d1479a8 -r 710e6250a401 system/test/svcore-system-test.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system/test/svcore-system-test.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -0,0 +1,41 @@ +/* -*- 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 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 "TestEnv.h" + +#include + +#include + +int main(int argc, char *argv[]) +{ + int good = 0, bad = 0; + + QCoreApplication app(argc, argv); + app.setOrganizationName("sonic-visualiser"); + app.setApplicationName("test-svcore-system"); + + { + TestEnv t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + + if (bad > 0) { + SVCERR << "\n********* " << bad << " test suite(s) failed!\n" << endl; + return 1; + } else { + SVCERR << "All tests passed" << endl; + return 0; + } +} diff -r d4a28d1479a8 -r 710e6250a401 transform/CSVFeatureWriter.cpp --- a/transform/CSVFeatureWriter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/CSVFeatureWriter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -117,8 +117,8 @@ } else if (i->first == "digits") { int digits = atoi(i->second.c_str()); if (digits <= 0 || digits > 100) { - cerr << "CSVFeatureWriter: ERROR: Invalid or out-of-range value for number of significant digits: " << i->second << endl; - cerr << "CSVFeatureWriter: NOTE: Continuing with default settings" << endl; + SVCERR << "CSVFeatureWriter: ERROR: Invalid or out-of-range value for number of significant digits: " << i->second << endl; + SVCERR << "CSVFeatureWriter: NOTE: Continuing with default settings" << endl; } else { m_digits = digits; } diff -r d4a28d1479a8 -r 710e6250a401 transform/FeatureExtractionModelTransformer.cpp --- a/transform/FeatureExtractionModelTransformer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/FeatureExtractionModelTransformer.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -85,6 +85,7 @@ for (int j = 1; j < (int)m_transforms.size(); ++j) { if (!areTransformsSimilar(m_transforms[0], m_transforms[j])) { m_message = tr("Transforms supplied to a single FeatureExtractionModelTransformer instance must be similar in every respect except plugin output"); + SVCERR << m_message << endl; return false; } } @@ -98,12 +99,14 @@ if (!factory) { m_message = tr("No factory available for feature extraction plugin id \"%1\" (unknown plugin type, or internal error?)").arg(pluginId); - return false; + SVCERR << m_message << endl; + return false; } DenseTimeValueModel *input = getConformingInput(); if (!input) { m_message = tr("Input model for feature extraction plugin \"%1\" is of wrong type (internal error?)").arg(pluginId); + SVCERR << m_message << endl; return false; } @@ -113,18 +116,19 @@ m_plugin = factory->instantiatePlugin(pluginId, input->getSampleRate()); if (!m_plugin) { m_message = tr("Failed to instantiate plugin \"%1\"").arg(pluginId); - return false; + SVCERR << m_message << endl; + return false; } TransformFactory::getInstance()->makeContextConsistentWithPlugin (primaryTransform, m_plugin); - + TransformFactory::getInstance()->setPluginParameters (primaryTransform, m_plugin); - + int channelCount = input->getChannelCount(); if ((int)m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; + channelCount = 1; } if ((int)m_plugin->getMinChannelCount() > channelCount) { m_message = tr("Cannot provide enough channels to feature extraction plugin \"%1\" (plugin min is %2, max %3; input model has %4)") @@ -132,59 +136,60 @@ .arg(m_plugin->getMinChannelCount()) .arg(m_plugin->getMaxChannelCount()) .arg(input->getChannelCount()); - return false; + SVCERR << m_message << endl; + return false; } + int step = primaryTransform.getStepSize(); + int block = primaryTransform.getBlockSize(); + SVDEBUG << "Initialising feature extraction plugin with channels = " - << channelCount << ", step = " << primaryTransform.getStepSize() - << ", block = " << primaryTransform.getBlockSize() << endl; + << channelCount << ", step = " << step + << ", block = " << block << endl; - if (!m_plugin->initialise(channelCount, - primaryTransform.getStepSize(), - primaryTransform.getBlockSize())) { + if (!m_plugin->initialise(channelCount, step, block)) { + + int preferredStep = int(m_plugin->getPreferredStepSize()); + int preferredBlock = int(m_plugin->getPreferredBlockSize()); - int pstep = primaryTransform.getStepSize(); - int pblock = primaryTransform.getBlockSize(); + if (step != preferredStep || block != preferredBlock) { -///!!! hang on, this isn't right -- we're modifying a copy - primaryTransform.setStepSize(0); - primaryTransform.setBlockSize(0); - TransformFactory::getInstance()->makeContextConsistentWithPlugin - (primaryTransform, m_plugin); - - if (primaryTransform.getStepSize() != pstep || - primaryTransform.getBlockSize() != pblock) { - - SVDEBUG << "Initialisation failed, trying again with default step = " - << primaryTransform.getStepSize() - << ", block = " << primaryTransform.getBlockSize() << endl; + SVDEBUG << "Initialisation failed, trying again with preferred step = " + << preferredStep << ", block = " << preferredBlock << endl; - if (!m_plugin->initialise(channelCount, - primaryTransform.getStepSize(), - primaryTransform.getBlockSize())) { + if (!m_plugin->initialise(channelCount, preferredStep, preferredBlock)) { SVDEBUG << "Initialisation failed again" << endl; m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); + SVCERR << m_message << endl; return false; } else { SVDEBUG << "Initialisation succeeded this time" << endl; + + // Set these values into the primary transform in the list + m_transforms[0].setStepSize(preferredStep); + m_transforms[0].setBlockSize(preferredBlock); 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(primaryTransform.getStepSize()) - .arg(primaryTransform.getBlockSize()); + .arg(step) + .arg(block) + .arg(preferredStep) + .arg(preferredBlock); + SVCERR << m_message << endl; } } else { - SVDEBUG << "Initialisation failed" << endl; + SVDEBUG << "Initialisation failed (with step = " << step + << " and block = " << block + << ", both matching the plugin's preference)" << endl; m_message = tr("Failed to initialise feature extraction plugin \"%1\"").arg(pluginId); + SVCERR << m_message << endl; return false; } } else { @@ -203,6 +208,7 @@ } else { m_message = vm; } + SVCERR << m_message << endl; } } @@ -210,7 +216,8 @@ if (outputs.empty()) { m_message = tr("Plugin \"%1\" has no outputs").arg(pluginId); - return false; + SVCERR << m_message << endl; + return false; } for (int j = 0; j < (int)m_transforms.size(); ++j) { @@ -230,6 +237,7 @@ m_message = tr("Plugin \"%1\" has no output named \"%2\"") .arg(pluginId) .arg(m_transforms[j].getOutput()); + SVCERR << m_message << endl; return false; } } @@ -251,8 +259,18 @@ { SVDEBUG << "FeatureExtractionModelTransformer: deleting plugin for transform in thread " << QThread::currentThreadId() << endl; - - delete m_plugin; + + try { + delete m_plugin; + } catch (const std::exception &e) { + // A destructor shouldn't throw an exception. But at one point + // (now fixed) our plugin stub destructor could have + // accidentally done so, so just in case: + SVCERR << "FeatureExtractionModelTransformer: caught exception while deleting plugin: " << e.what() << endl; + m_message = e.what(); + } + m_plugin = 0; + for (int j = 0; j < (int)m_descriptors.size(); ++j) { delete m_descriptors[j]; } @@ -272,17 +290,17 @@ bool haveBinCount = m_descriptors[n]->hasFixedBinCount; if (haveBinCount) { - binCount = (int)m_descriptors[n]->binCount; + binCount = (int)m_descriptors[n]->binCount; } m_needAdditionalModels[n] = false; // cerr << "FeatureExtractionModelTransformer: output bin count " -// << binCount << endl; +// << binCount << endl; if (binCount > 0 && m_descriptors[n]->hasKnownExtents) { - minValue = m_descriptors[n]->minValue; - maxValue = m_descriptors[n]->maxValue; + minValue = m_descriptors[n]->minValue; + maxValue = m_descriptors[n]->maxValue; haveExtents = true; } @@ -312,14 +330,14 @@ switch (m_descriptors[n]->sampleType) { case Vamp::Plugin::OutputDescriptor::VariableSampleRate: - if (outputRate != 0.0) { - modelResolution = int(round(modelRate / outputRate)); - } - break; + if (outputRate != 0.0) { + modelResolution = int(round(modelRate / outputRate)); + } + break; case Vamp::Plugin::OutputDescriptor::OneSamplePerStep: - modelResolution = m_transforms[n].getStepSize(); - break; + modelResolution = m_transforms[n].getStepSize(); + break; case Vamp::Plugin::OutputDescriptor::FixedSampleRate: if (outputRate <= 0.0) { @@ -329,7 +347,7 @@ modelResolution = int(round(modelRate / outputRate)); // cerr << "modelRate = " << modelRate << ", descriptor rate = " << outputRate << ", modelResolution = " << modelResolution << endl; } - break; + break; } bool preDurationPlugin = (m_plugin->getVampApiVersion() < 2); @@ -498,13 +516,13 @@ EditableDenseThreeDimensionalModel::BasicMultirateCompression, false); - if (!m_descriptors[n]->binNames.empty()) { - std::vector names; - for (int i = 0; i < (int)m_descriptors[n]->binNames.size(); ++i) { - names.push_back(m_descriptors[n]->binNames[i].c_str()); - } - model->setBinNames(names); - } + if (!m_descriptors[n]->binNames.empty()) { + std::vector names; + for (int i = 0; i < (int)m_descriptors[n]->binNames.size(); ++i) { + names.push_back(m_descriptors[n]->binNames[i].c_str()); + } + model->setBinNames(names); + } out = model; @@ -522,8 +540,8 @@ FeatureExtractionModelTransformer::awaitOutputModels() { m_outputMutex.lock(); - while (!m_haveOutputs) { - m_outputsCondition.wait(&m_outputMutex); + while (!m_haveOutputs && !m_abandoned) { + m_outputsCondition.wait(&m_outputMutex, 500); } m_outputMutex.unlock(); } @@ -603,9 +621,9 @@ // SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: input model is " << getInputModel() << endl; DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); + dynamic_cast(getInputModel()); if (!dtvm) { - SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl; + SVDEBUG << "FeatureExtractionModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl; } return dtvm; } @@ -613,12 +631,27 @@ void FeatureExtractionModelTransformer::run() { - initialise(); + try { + if (!initialise()) { + abandon(); + return; + } + } catch (const std::exception &e) { + abandon(); + m_message = e.what(); + return; + } DenseTimeValueModel *input = getConformingInput(); - if (!input) return; + if (!input) { + abandon(); + return; + } - if (m_outputs.empty()) return; + if (m_outputs.empty()) { + abandon(); + return; + } Transform primaryTransform = m_transforms[0]; @@ -632,12 +665,12 @@ int channelCount = input->getChannelCount(); if ((int)m_plugin->getMaxChannelCount() < channelCount) { - channelCount = 1; + channelCount = 1; } float **buffers = new float*[channelCount]; for (int ch = 0; ch < channelCount; ++ch) { - buffers[ch] = new float[primaryTransform.getBlockSize() + 2]; + buffers[ch] = new float[primaryTransform.getBlockSize() + 2]; } int stepSize = primaryTransform.getStepSize(); @@ -710,85 +743,93 @@ QString error = ""; - while (!m_abandoned) { + try { + while (!m_abandoned) { - if (frequencyDomain) { - if (blockFrame - int(blockSize)/2 > - contextStart + contextDuration) break; - } else { - if (blockFrame >= - contextStart + contextDuration) break; + if (frequencyDomain) { + if (blockFrame - int(blockSize)/2 > + contextStart + contextDuration) break; + } else { + if (blockFrame >= + contextStart + contextDuration) break; + } + +// SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame " +// << blockFrame << ", endFrame " << endFrame << ", blockSize " +// << blockSize << endl; + + int completion = int + ((((blockFrame - contextStart) / stepSize) * 99) / + (contextDuration / stepSize + 1)); + + // channelCount is either m_input.getModel()->channelCount or 1 + + if (frequencyDomain) { + for (int ch = 0; ch < channelCount; ++ch) { + int column = int((blockFrame - startFrame) / stepSize); + if (fftModels[ch]->getValuesAt(column, reals, imaginaries)) { + for (int i = 0; i <= blockSize/2; ++i) { + buffers[ch][i*2] = reals[i]; + buffers[ch][i*2+1] = imaginaries[i]; + } + } else { + for (int i = 0; i <= blockSize/2; ++i) { + buffers[ch][i*2] = 0.f; + buffers[ch][i*2+1] = 0.f; + } + } + error = fftModels[ch]->getError(); + if (error != "") { + SVCERR << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl; + m_abandoned = true; + m_message = error; + break; + } + } + } else { + getFrames(channelCount, blockFrame, blockSize, buffers); + } + + if (m_abandoned) break; + + Vamp::Plugin::FeatureSet features = m_plugin->process + (buffers, RealTime::frame2RealTime(blockFrame, sampleRate).toVampRealTime()); + + if (m_abandoned) break; + + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) { + Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; + addFeature(j, blockFrame, feature); + } + } + + if (blockFrame == contextStart || completion > prevCompletion) { + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + setCompletion(j, completion); + } + prevCompletion = completion; + } + + blockFrame += stepSize; + } -// SVDEBUG << "FeatureExtractionModelTransformer::run: blockFrame " -// << blockFrame << ", endFrame " << endFrame << ", blockSize " -// << blockSize << endl; + if (!m_abandoned) { + Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); - int completion = int - ((((blockFrame - contextStart) / stepSize) * 99) / - (contextDuration / stepSize + 1)); - - // channelCount is either m_input.getModel()->channelCount or 1 - - if (frequencyDomain) { - for (int ch = 0; ch < channelCount; ++ch) { - int column = int((blockFrame - startFrame) / stepSize); - if (fftModels[ch]->getValuesAt(column, reals, imaginaries)) { - for (int i = 0; i <= blockSize/2; ++i) { - buffers[ch][i*2] = reals[i]; - buffers[ch][i*2+1] = imaginaries[i]; - } - } else { - for (int i = 0; i <= blockSize/2; ++i) { - buffers[ch][i*2] = 0.f; - buffers[ch][i*2+1] = 0.f; - } - } - error = fftModels[ch]->getError(); - if (error != "") { - SVDEBUG << "FeatureExtractionModelTransformer::run: Abandoning, error is " << error << endl; - m_abandoned = true; - m_message = error; - break; + for (int j = 0; j < (int)m_outputNos.size(); ++j) { + for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) { + Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; + addFeature(j, blockFrame, feature); } } - } else { - getFrames(channelCount, blockFrame, blockSize, buffers); } - - if (m_abandoned) break; - - Vamp::Plugin::FeatureSet features = m_plugin->process - (buffers, RealTime::frame2RealTime(blockFrame, sampleRate).toVampRealTime()); - - if (m_abandoned) break; - - for (int j = 0; j < (int)m_outputNos.size(); ++j) { - for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) { - Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; - addFeature(j, blockFrame, feature); - } - } - - if (blockFrame == contextStart || completion > prevCompletion) { - for (int j = 0; j < (int)m_outputNos.size(); ++j) { - setCompletion(j, completion); - } - prevCompletion = completion; - } - - blockFrame += stepSize; - } - - if (!m_abandoned) { - Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures(); - - for (int j = 0; j < (int)m_outputNos.size(); ++j) { - for (int fi = 0; fi < (int)features[m_outputNos[j]].size(); ++fi) { - Vamp::Plugin::Feature feature = features[m_outputNos[j]][fi]; - addFeature(j, blockFrame, feature); - } - } + } catch (const std::exception &e) { + SVCERR << "FeatureExtractionModelTransformer::run: Exception caught: " + << e.what() << endl; + m_abandoned = true; + m_message = e.what(); } for (int j = 0; j < (int)m_outputNos.size(); ++j) { @@ -886,23 +927,23 @@ sv_frame_t frame = blockFrame; if (m_descriptors[n]->sampleType == - Vamp::Plugin::OutputDescriptor::VariableSampleRate) { + Vamp::Plugin::OutputDescriptor::VariableSampleRate) { - if (!feature.hasTimestamp) { - SVDEBUG - << "WARNING: FeatureExtractionModelTransformer::addFeature: " - << "Feature has variable sample rate but no timestamp!" - << endl; - return; - } else { - frame = RealTime::realTime2Frame(feature.timestamp, inputRate); - } + if (!feature.hasTimestamp) { + SVDEBUG + << "WARNING: FeatureExtractionModelTransformer::addFeature: " + << "Feature has variable sample rate but no timestamp!" + << endl; + return; + } else { + frame = RealTime::realTime2Frame(feature.timestamp, inputRate); + } // cerr << "variable sample rate: timestamp = " << feature.timestamp // << " at input rate " << inputRate << " -> " << frame << endl; } else if (m_descriptors[n]->sampleType == - Vamp::Plugin::OutputDescriptor::FixedSampleRate) { + Vamp::Plugin::OutputDescriptor::FixedSampleRate) { sv_samplerate_t rate = m_descriptors[n]->sampleRate; if (rate <= 0.0) { @@ -943,16 +984,16 @@ SparseOneDimensionalModel *model = getConformingOutput(n); - if (!model) return; + if (!model) return; model->addPoint(SparseOneDimensionalModel::Point (frame, feature.label.c_str())); - + } else if (isOutput(n)) { - SparseTimeValueModel *model = + SparseTimeValueModel *model = getConformingOutput(n); - if (!model) return; + if (!model) return; for (int i = 0; i < (int)feature.values.size(); ++i) { @@ -1010,7 +1051,7 @@ duration, velocity / 127.f, feature.label.c_str())); - // GF: end -- added for flexi note model + // GF: end -- added for flexi note model } else if (isOutput(n)) { float velocity = 100; @@ -1055,14 +1096,14 @@ feature.label.c_str())); } } - + } else if (isOutput(n)) { - - DenseThreeDimensionalModel::Column values = feature.values; - - EditableDenseThreeDimensionalModel *model = + + DenseThreeDimensionalModel::Column values = feature.values; + + EditableDenseThreeDimensionalModel *model = getConformingOutput(n); - if (!model) return; + if (!model) return; // cerr << "(note: model resolution = " << model->getResolution() << ")" // << endl; @@ -1086,48 +1127,48 @@ if (isOutput(n)) { - SparseOneDimensionalModel *model = + SparseOneDimensionalModel *model = getConformingOutput(n); - if (!model) return; + if (!model) return; if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); + model->setCompletion(completion, true); } else if (isOutput(n)) { - SparseTimeValueModel *model = + SparseTimeValueModel *model = getConformingOutput(n); - if (!model) return; + if (!model) return; if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); + model->setCompletion(completion, true); } else if (isOutput(n)) { - NoteModel *model = getConformingOutput(n); - if (!model) return; + NoteModel *model = getConformingOutput(n); + if (!model) return; if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); - + model->setCompletion(completion, true); + } else if (isOutput(n)) { - FlexiNoteModel *model = getConformingOutput(n); - if (!model) return; + FlexiNoteModel *model = getConformingOutput(n); + if (!model) return; if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); + model->setCompletion(completion, true); } else if (isOutput(n)) { - RegionModel *model = getConformingOutput(n); - if (!model) return; + RegionModel *model = getConformingOutput(n); + if (!model) return; if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); + model->setCompletion(completion, true); } else if (isOutput(n)) { - EditableDenseThreeDimensionalModel *model = + EditableDenseThreeDimensionalModel *model = getConformingOutput(n); - if (!model) return; + if (!model) return; if (model->isAbandoning()) abandon(); - model->setCompletion(completion, true); //!!!m_context.updates); + model->setCompletion(completion, true); //!!!m_context.updates); } } diff -r d4a28d1479a8 -r 710e6250a401 transform/FeatureExtractionModelTransformer.h --- a/transform/FeatureExtractionModelTransformer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/FeatureExtractionModelTransformer.h Mon Sep 17 13:51:14 2018 +0100 @@ -70,7 +70,7 @@ void addFeature(int n, sv_frame_t blockFrame, - const Vamp::Plugin::Feature &feature); + const Vamp::Plugin::Feature &feature); void setCompletion(int, int); diff -r d4a28d1479a8 -r 710e6250a401 transform/FileFeatureWriter.cpp --- a/transform/FileFeatureWriter.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/FileFeatureWriter.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -46,7 +46,7 @@ } else if (m_support & SupportOneFileTotal) { m_singleFileName = QString("output.%1").arg(m_extension); } else { - cerr << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl; + SVCERR << "FileFeatureWriter::FileFeatureWriter: ERROR: Invalid support specification " << support << endl; } } } @@ -130,7 +130,7 @@ if (m_support & SupportOneFilePerTrackTransform && m_support & SupportOneFilePerTrack) { if (m_singleFileName != "") { - cerr << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl; + SVCERR << "FileFeatureWriter::setParameters: WARNING: Both one-file and many-files parameters provided, ignoring many-files" << endl; } else { m_manyFiles = true; } @@ -144,7 +144,7 @@ // OneFilePerTrack), so we need to be able to // override it // if (m_manyFiles) { -// cerr << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl; +// SVCERR << "FileFeatureWriter::setParameters: WARNING: Both many-files and one-file parameters provided, ignoring one-file" << endl; // } else { m_singleFileName = i->second.c_str(); // } @@ -153,7 +153,7 @@ } else if (i->first == "stdout") { if (m_support & SupportStdOut) { if (m_singleFileName != "") { - cerr << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl; + SVCERR << "FileFeatureWriter::setParameters: WARNING: Both stdout and one-file provided, ignoring stdout" << endl; } else { m_stdout = true; } @@ -172,8 +172,8 @@ { if (m_singleFileName != "") { if (QFileInfo(m_singleFileName).exists() && !(m_force || m_append)) { - cerr << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName << "\" exists and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append flag is specified -- not overwriting" << endl; - cerr << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; + SVCERR << endl << "FileFeatureWriter: ERROR: Specified output file \"" << m_singleFileName << "\" exists and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append flag is specified -- not overwriting" << endl; + SVCERR << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; return ""; } return m_singleFileName; @@ -219,8 +219,8 @@ filename = QDir(dirname).filePath(filename); if (QFileInfo(filename).exists() && !(m_force || m_append)) { - cerr << endl << "FileFeatureWriter: ERROR: Output file \"" << filename << "\" exists (for input file or URL \"" << trackId << "\" and transform \"" << transformId << "\") and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append is specified -- not overwriting" << endl; - cerr << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; + SVCERR << endl << "FileFeatureWriter: ERROR: Output file \"" << filename << "\" exists (for input file or URL \"" << trackId << "\" and transform \"" << transformId << "\") and neither --" << getWriterTag() << "-force nor --" << getWriterTag() << "-append is specified -- not overwriting" << endl; + SVCERR << "NOTE: To find out how to fix this problem, read the help for the --" << getWriterTag() << "-force" << endl << "and --" << getWriterTag() << "-append options" << endl; return ""; } @@ -300,7 +300,7 @@ if (m_append) mode |= QIODevice::Append; if (!file->open(mode)) { - cerr << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename + SVCERR << "FileFeatureWriter: ERROR: Failed to open output file \"" << filename << "\" for writing" << endl; delete file; m_files[key] = 0; diff -r d4a28d1479a8 -r 710e6250a401 transform/ModelTransformer.h --- a/transform/ModelTransformer.h Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/ModelTransformer.h Mon Sep 17 13:51:14 2018 +0100 @@ -13,8 +13,8 @@ COPYING included with this distribution for more information. */ -#ifndef _TRANSFORMER_H_ -#define _TRANSFORMER_H_ +#ifndef SV_MODEL_TRANSFORMER_H +#define SV_MODEL_TRANSFORMER_H #include "base/Thread.h" diff -r d4a28d1479a8 -r 710e6250a401 transform/ModelTransformerFactory.cpp --- a/transform/ModelTransformerFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/ModelTransformerFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -261,8 +261,8 @@ // SVDEBUG << "ModelTransformerFactory::transformerFinished(" << transformer << ")" << endl; if (!transformer) { - cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl; - return; + cerr << "WARNING: ModelTransformerFactory::transformerFinished: sender is not a transformer" << endl; + return; } if (m_runningTransformers.find(transformer) == m_runningTransformers.end()) { diff -r d4a28d1479a8 -r 710e6250a401 transform/RealTimeEffectModelTransformer.cpp --- a/transform/RealTimeEffectModelTransformer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/RealTimeEffectModelTransformer.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -47,15 +47,15 @@ QString pluginId = transform.getPluginIdentifier(); -// SVDEBUG << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId << ", output " << output << endl; + SVDEBUG << "RealTimeEffectModelTransformer::RealTimeEffectModelTransformer: plugin " << pluginId << ", output " << transform.getOutput() << endl; RealTimePluginFactory *factory = - RealTimePluginFactory::instanceFor(pluginId); + RealTimePluginFactory::instanceFor(pluginId); if (!factory) { - cerr << "RealTimeEffectModelTransformer: No factory available for plugin id \"" - << pluginId << "\"" << endl; - return; + SVCERR << "RealTimeEffectModelTransformer: No factory available for plugin id \"" + << pluginId << "\"" << endl; + return; } DenseTimeValueModel *input = getConformingInput(); @@ -67,9 +67,9 @@ input->getChannelCount()); if (!m_plugin) { - cerr << "RealTimeEffectModelTransformer: Failed to instantiate plugin \"" - << pluginId << "\"" << endl; - return; + SVCERR << "RealTimeEffectModelTransformer: Failed to instantiate plugin \"" + << pluginId << "\"" << endl; + return; } TransformFactory::getInstance()->setPluginParameters(transform, m_plugin); @@ -93,7 +93,7 @@ m_outputs.push_back(model); } else { - + SparseTimeValueModel *model = new SparseTimeValueModel (input->getSampleRate(), transform.getBlockSize(), 0.0, 0.0, false); @@ -112,9 +112,9 @@ RealTimeEffectModelTransformer::getConformingInput() { DenseTimeValueModel *dtvm = - dynamic_cast(getInputModel()); + dynamic_cast(getInputModel()); if (!dtvm) { - SVDEBUG << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl; + SVDEBUG << "RealTimeEffectModelTransformer::getConformingInput: WARNING: Input model is not conformable to DenseTimeValueModel" << endl; } return dtvm; } @@ -129,13 +129,25 @@ SVDEBUG << "RealTimeEffectModelTransformer::run: Waiting for input model to be ready..." << endl; usleep(500000); } - if (m_abandoned) return; + if (m_abandoned) { + return; + } + if (m_outputs.empty()) { + return; + } + + SparseTimeValueModel *stvm = + dynamic_cast(m_outputs[0]); + WritableWaveFileModel *wwfm = + dynamic_cast(m_outputs[0]); - SparseTimeValueModel *stvm = dynamic_cast(m_outputs[0]); - WritableWaveFileModel *wwfm = dynamic_cast(m_outputs[0]); - if (!stvm && !wwfm) return; + if (!stvm && !wwfm) { + return; + } - if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return; + if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) { + return; + } sv_samplerate_t sampleRate = input->getSampleRate(); int channelCount = input->getChannelCount(); @@ -183,13 +195,13 @@ while (blockFrame < contextStart + contextDuration + latency && !m_abandoned) { - int completion = int - ((((blockFrame - contextStart) / blockSize) * 99) / + int completion = int + ((((blockFrame - contextStart) / blockSize) * 99) / (1 + ((contextDuration) / blockSize))); - sv_frame_t got = 0; + sv_frame_t got = 0; - if (channelCount == 1) { + if (channelCount == 1) { if (inbufs && inbufs[0]) { auto data = input->getData (m_input.getChannel(), blockFrame, blockSize); @@ -206,7 +218,7 @@ } } } - } else { + } else { if (inbufs && inbufs[0]) { auto data = input->getMultiChannelData (0, channelCount - 1, blockFrame, blockSize); @@ -228,7 +240,7 @@ } } } - } + } /* cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< endl; @@ -281,15 +293,15 @@ } } - if (blockFrame == contextStart || completion > prevCompletion) { + if (blockFrame == contextStart || completion > prevCompletion) { // This setCompletion is probably misusing the completion // terminology, just as it was for WritableWaveFileModel - if (stvm) stvm->setCompletion(completion); - if (wwfm) wwfm->setWriteProportion(completion); - prevCompletion = completion; - } + if (stvm) stvm->setCompletion(completion); + if (wwfm) wwfm->setWriteProportion(completion); + prevCompletion = completion; + } - blockFrame += blockSize; + blockFrame += blockSize; } if (m_abandoned) return; diff -r d4a28d1479a8 -r 710e6250a401 transform/TransformFactory.cpp --- a/transform/TransformFactory.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/transform/TransformFactory.cpp Mon Sep 17 13:51:14 2018 +0100 @@ -112,20 +112,20 @@ std::set dset; for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { + i != m_transforms.end(); ++i) { #ifdef DEBUG_TRANSFORM_FACTORY cerr << "inserting transform into set: id = " << i->second.identifier << endl; #endif - dset.insert(i->second); + dset.insert(i->second); } TransformList list; for (std::set::const_iterator i = dset.begin(); - i != dset.end(); ++i) { + i != dset.end(); ++i) { #ifdef DEBUG_TRANSFORM_FACTORY cerr << "inserting transform into list: id = " << i->identifier << endl; #endif - list.push_back(*i); + list.push_back(*i); } return list; @@ -158,20 +158,20 @@ std::set dset; for (TransformDescriptionMap::const_iterator i = m_uninstalledTransforms.begin(); - i != m_uninstalledTransforms.end(); ++i) { + i != m_uninstalledTransforms.end(); ++i) { #ifdef DEBUG_TRANSFORM_FACTORY cerr << "inserting transform into set: id = " << i->second.identifier << endl; #endif - dset.insert(i->second); + dset.insert(i->second); } TransformList list; for (std::set::const_iterator i = dset.begin(); - i != dset.end(); ++i) { + i != dset.end(); ++i) { #ifdef DEBUG_TRANSFORM_FACTORY cerr << "inserting transform into uninstalled list: id = " << i->identifier << endl; #endif - list.push_back(*i); + list.push_back(*i); } return list; @@ -248,7 +248,7 @@ std::set types; for (TransformDescriptionMap::const_iterator i = m_transforms.begin(); - i != m_transforms.end(); ++i) { + i != m_transforms.end(); ++i) { types.insert(i->second.type); } @@ -376,17 +376,17 @@ i != transforms.end(); ++i) { TransformDescription desc = i->second; - QString identifier = desc.identifier; + QString identifier = desc.identifier; QString maker = desc.maker; QString td = desc.name; QString tn = td.section(": ", 0, 0); QString to = td.section(": ", 1); - if (names[tn] > 1) { + if (names[tn] > 1) { maker.replace(QRegExp(tr(" [\\(<].*$")), ""); - tn = QString("%1 [%2]").arg(tn).arg(maker); - } + tn = QString("%1 [%2]").arg(tn).arg(maker); + } if (to != "") { desc.name = QString("%1: %2").arg(tn).arg(to); @@ -394,8 +394,8 @@ desc.name = tn; } - m_transforms[identifier] = desc; - } + m_transforms[identifier] = desc; + } m_transformsPopulated = true; } @@ -416,7 +416,7 @@ for (int i = 0; i < (int)plugs.size(); ++i) { - QString pluginId = plugs[i]; + QString pluginId = plugs[i]; piper_vamp::PluginStaticData psd = factory->getPluginStaticData(pluginId); @@ -434,10 +434,10 @@ QString outputName = QString::fromStdString(o.name); - QString transformId = QString("%1:%2") + QString transformId = QString("%1:%2") .arg(pluginId).arg(QString::fromStdString(o.identifier)); - QString userName; + QString userName; QString friendlyName; //!!! return to this QString units = outputs[j].unit.c_str(); QString description = QString::fromStdString(psd.basic.description); @@ -464,13 +464,13 @@ } } - if (basicOutputs.size() == 1) { - userName = pluginName; + if (basicOutputs.size() == 1) { + userName = pluginName; friendlyName = pluginName; - } else { - userName = QString("%1: %2").arg(pluginName).arg(outputName); + } else { + userName = QString("%1: %2").arg(pluginName).arg(outputName); friendlyName = outputName; - } + } bool configurable = (!psd.programs.empty() || !psd.parameters.empty()); @@ -479,7 +479,7 @@ cerr << "Feature extraction plugin transform: " << transformId << " friendly name: " << friendlyName << endl; #endif - transforms[transformId] = + transforms[transformId] = TransformDescription(TransformDescription::Analysis, category, transformId, @@ -491,7 +491,7 @@ //!!! units, "", configurable); - } + } } } @@ -499,37 +499,37 @@ TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms) { std::vector plugs = - RealTimePluginFactory::getAllPluginIdentifiers(); + RealTimePluginFactory::getAllPluginIdentifiers(); if (m_exiting) return; static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$"); for (int i = 0; i < (int)plugs.size(); ++i) { - QString pluginId = plugs[i]; + QString pluginId = plugs[i]; RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(pluginId); - if (!factory) { - cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId << endl; - continue; - } + if (!factory) { + cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId << endl; + continue; + } const RealTimePluginDescriptor *descriptor = factory->getPluginDescriptor(pluginId); if (!descriptor) { - cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId << endl; - continue; - } - + cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId << endl; + continue; + } + //!!! if (descriptor->controlOutputPortCount == 0 || // descriptor->audioInputPortCount == 0) continue; // cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << endl; - - QString pluginName = descriptor->name.c_str(); + + QString pluginName = descriptor->name.c_str(); QString category = factory->getPluginCategory(pluginId); bool configurable = (descriptor->parameterCount > 0); QString maker = descriptor->maker.c_str(); @@ -856,7 +856,7 @@ TransformFactory::getTransformName(TransformId identifier) { if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].name; + return m_transforms[identifier].name; } else return ""; } @@ -864,7 +864,7 @@ TransformFactory::getTransformFriendlyName(TransformId identifier) { if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].friendlyName; + return m_transforms[identifier].friendlyName; } else return ""; } @@ -872,7 +872,7 @@ TransformFactory::getTransformUnits(TransformId identifier) { if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].units; + return m_transforms[identifier].units; } else return ""; } @@ -880,7 +880,7 @@ TransformFactory::getTransformInfoUrl(TransformId identifier) { if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].infoUrl; + return m_transforms[identifier].infoUrl; } else return ""; } @@ -913,7 +913,7 @@ TransformFactory::isTransformConfigurable(TransformId identifier) { if (m_transforms.find(identifier) != m_transforms.end()) { - return m_transforms[identifier].configurable; + return m_transforms[identifier].configurable; } else return false; }