From 4e824855286d48f228d5dc2f71001e1afd8e9a44 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Fri, 24 Jun 2016 22:06:46 +0200 Subject: [PATCH 01/10] Move formatting of durations from Time into Duration class --- build/depends.py | 1 + src/library/autodj/dlgautodj.cpp | 4 +- src/library/basesqltablemodel.cpp | 4 +- src/library/browse/browsethread.cpp | 4 +- src/library/cratefeature.cpp | 4 +- src/library/playlistfeature.cpp | 4 +- ...{timeutiltest.cpp => durationutiltest.cpp} | 32 ++++++------ src/track/track.cpp | 4 +- src/util/duration.cpp | 52 +++++++++++++++++++ src/util/duration.h | 28 ++++++---- src/util/time.cpp | 39 +------------- src/util/time.h | 18 ------- src/widget/wnumberpos.cpp | 27 +++++----- 13 files changed, 116 insertions(+), 105 deletions(-) rename src/test/{timeutiltest.cpp => durationutiltest.cpp} (56%) create mode 100644 src/util/duration.cpp diff --git a/build/depends.py b/build/depends.py index f38411d76b5..2a9015240f1 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1045,6 +1045,7 @@ def sources(self, build): "util/statsmanager.cpp", "util/stat.cpp", "util/statmodel.cpp", + "util/duration.cpp", "util/time.cpp", "util/timer.cpp", "util/performancetimer.cpp", diff --git a/src/library/autodj/dlgautodj.cpp b/src/library/autodj/dlgautodj.cpp index 5b0569ef144..d00cfb2314e 100644 --- a/src/library/autodj/dlgautodj.cpp +++ b/src/library/autodj/dlgautodj.cpp @@ -5,7 +5,7 @@ #include "library/playlisttablemodel.h" #include "widget/wtracktableview.h" #include "util/assert.h" -#include "util/time.h" +#include "util/duration.h" DlgAutoDJ::DlgAutoDJ(QWidget* parent, UserSettingsPointer pConfig, @@ -214,7 +214,7 @@ void DlgAutoDJ::updateSelectionInfo() { QString label; if (!indices.isEmpty()) { - label.append(Time::formatSeconds(duration)); + label.append(mixxx::Duration::formatSeconds(duration)); label.append(QString(" (%1)").arg(indices.size())); labelSelectionInfo->setText(label); labelSelectionInfo->setEnabled(true); diff --git a/src/library/basesqltablemodel.cpp b/src/library/basesqltablemodel.cpp index 6890264dd44..f1d855064da 100644 --- a/src/library/basesqltablemodel.cpp +++ b/src/library/basesqltablemodel.cpp @@ -16,7 +16,7 @@ #include "mixer/playerinfo.h" #include "track/keyutils.h" #include "track/trackmetadata.h" -#include "util/time.h" +#include "util/duration.h" #include "util/dnd.h" #include "util/assert.h" #include "util/performancetimer.h" @@ -615,7 +615,7 @@ QVariant BaseSqlTableModel::data(const QModelIndex& index, int role) const { if (column == fieldIndex(ColumnCache::COLUMN_LIBRARYTABLE_DURATION)) { int duration = value.toInt(); if (duration > 0) { - value = Time::formatSeconds(duration); + value = mixxx::Duration::formatSeconds(duration); } else { value = QString(); } diff --git a/src/library/browse/browsethread.cpp b/src/library/browse/browsethread.cpp index 7af8c0afd78..227fe16c137 100644 --- a/src/library/browse/browsethread.cpp +++ b/src/library/browse/browsethread.cpp @@ -10,7 +10,7 @@ #include "library/browse/browsetablemodel.h" #include "sources/soundsourceproxy.h" #include "track/trackmetadata.h" -#include "util/time.h" +#include "util/duration.h" #include "util/trace.h" @@ -213,7 +213,7 @@ void BrowseThread::populateModel() { item->setData(item->text(), Qt::UserRole); row_data.insert(COLUMN_COMMENT, item); - QString duration = Time::formatSeconds(pTrack->getDuration()); + QString duration = mixxx::Duration::formatSeconds(pTrack->getDuration()); item = new QStandardItem(duration); item->setToolTip(item->text()); item->setData(item->text(), Qt::UserRole); diff --git a/src/library/cratefeature.cpp b/src/library/cratefeature.cpp index a0f8b118165..0d53d49e321 100644 --- a/src/library/cratefeature.cpp +++ b/src/library/cratefeature.cpp @@ -23,7 +23,7 @@ #include "treeitem.h" #include "sources/soundsourceproxy.h" #include "util/dnd.h" -#include "util/time.h" +#include "util/duration.h" CrateFeature::CrateFeature(Library* pLibrary, TrackCollection* pTrackCollection, @@ -498,7 +498,7 @@ void CrateFeature::buildCrateList() { crateListTableModel.index(row, durationColumn)).toInt(); m_crateList.append(qMakePair(id, QString("%1 (%2) %3") .arg(name, QString::number(count), - Time::formatSeconds(duration)))); + mixxx::Duration::formatSeconds(duration)))); } } diff --git a/src/library/playlistfeature.cpp b/src/library/playlistfeature.cpp index 902709d74b5..fff0ed5bfbb 100644 --- a/src/library/playlistfeature.cpp +++ b/src/library/playlistfeature.cpp @@ -16,7 +16,7 @@ #include "controllers/keyboard/keyboardeventfilter.h" #include "sources/soundsourceproxy.h" #include "util/dnd.h" -#include "util/time.h" +#include "util/duration.h" PlaylistFeature::PlaylistFeature(QObject* parent, TrackCollection* pTrackCollection, @@ -170,7 +170,7 @@ void PlaylistFeature::buildPlaylistList() { playlistTableModel.index(row, durationColumn)).toInt(); m_playlistList.append(qMakePair(id, QString("%1 (%2) %3") .arg(name, QString::number(count), - Time::formatSeconds(duration)))); + mixxx::Duration::formatSeconds(duration)))); } } diff --git a/src/test/timeutiltest.cpp b/src/test/durationutiltest.cpp similarity index 56% rename from src/test/timeutiltest.cpp rename to src/test/durationutiltest.cpp index 248d9ed4536..03310aec51c 100644 --- a/src/test/timeutiltest.cpp +++ b/src/test/durationutiltest.cpp @@ -1,15 +1,15 @@ #include -#include "util/time.h" +#include "util/duration.h" #include namespace { -class TimeUtilTest : public testing::Test { +class DurationUtilTest : public testing::Test { protected: - TimeUtilTest() { + DurationUtilTest() { } virtual void SetUp() { @@ -20,13 +20,13 @@ class TimeUtilTest : public testing::Test { static QString adjustPrecision( QString withMilliseconds, - Time::Precision precision) { + mixxx::Duration::Precision precision) { switch (precision) { - case Time::Precision::SECONDS: + case mixxx::Duration::Precision::SECONDS: { return withMilliseconds.left(withMilliseconds.length() - 4); } - case Time::Precision::CENTISECONDS: + case mixxx::Duration::Precision::CENTISECONDS: { return withMilliseconds.left(withMilliseconds.length() - 1); } @@ -38,28 +38,28 @@ class TimeUtilTest : public testing::Test { void formatSeconds(QString expectedMilliseconds, double dSeconds) { ASSERT_LE(4, expectedMilliseconds.length()); // 3 digits + 1 decimal point const QString actualSeconds = - Time::formatSeconds(dSeconds, Time::Precision::SECONDS); + mixxx::Duration::formatSeconds(dSeconds, mixxx::Duration::Precision::SECONDS); const QString expectedSeconds = - adjustPrecision(expectedMilliseconds, Time::Precision::SECONDS); + adjustPrecision(expectedMilliseconds, mixxx::Duration::Precision::SECONDS); EXPECT_EQ(expectedSeconds, actualSeconds); const QString expectedCentiseconds = - adjustPrecision(expectedMilliseconds, Time::Precision::CENTISECONDS); + adjustPrecision(expectedMilliseconds, mixxx::Duration::Precision::CENTISECONDS); const QString actualCentiseconds = - Time::formatSeconds(dSeconds, Time::Precision::CENTISECONDS); + mixxx::Duration::formatSeconds(dSeconds, mixxx::Duration::Precision::CENTISECONDS); EXPECT_EQ(expectedCentiseconds, actualCentiseconds); const QString actualMilliseconds = - Time::formatSeconds(dSeconds, Time::Precision::MILLISECONDS); + mixxx::Duration::formatSeconds(dSeconds, mixxx::Duration::Precision::MILLISECONDS); EXPECT_EQ(actualMilliseconds, actualMilliseconds); } }; -TEST_F(TimeUtilTest, FormatSecondsNegative) { - EXPECT_EQ("?", Time::formatSeconds(-1, Time::Precision::SECONDS)); - EXPECT_EQ("?", Time::formatSeconds(-1, Time::Precision::CENTISECONDS)); - EXPECT_EQ("?", Time::formatSeconds(-1, Time::Precision::MILLISECONDS)); +TEST_F(DurationUtilTest, FormatSecondsNegative) { + EXPECT_EQ("?", mixxx::Duration::formatSeconds(-1, mixxx::Duration::Precision::SECONDS)); + EXPECT_EQ("?", mixxx::Duration::formatSeconds(-1, mixxx::Duration::Precision::CENTISECONDS)); + EXPECT_EQ("?", mixxx::Duration::formatSeconds(-1, mixxx::Duration::Precision::MILLISECONDS)); } -TEST_F(TimeUtilTest, FormatSeconds) { +TEST_F(DurationUtilTest, FormatSeconds) { formatSeconds("00:00.000", 0); formatSeconds("00:01.000", 1); formatSeconds("00:59.000", 59); diff --git a/src/track/track.cpp b/src/track/track.cpp index 973402982d3..88a423ed380 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -11,7 +11,7 @@ #include "track/trackmetadatataglib.h" #include "util/assert.h" #include "util/compatibility.h" -#include "util/time.h" +#include "util/duration.h" namespace { @@ -387,7 +387,7 @@ int Track::getDuration() const { } QString Track::getDurationText() const { - return Time::formatSeconds(getDuration()); + return mixxx::Duration::formatSeconds(getDuration()); } QString Track::getTitle() const { diff --git a/src/util/duration.cpp b/src/util/duration.cpp new file mode 100644 index 00000000000..96272d20553 --- /dev/null +++ b/src/util/duration.cpp @@ -0,0 +1,52 @@ +#include "util/duration.h" + +#include +#include +#include + +#include "util/assert.h" +#include + +namespace mixxx { + +namespace { + +static const qint64 kSecondsPerMinute = 60; +static const qint64 kSecondsPerHour = 60 * kSecondsPerMinute; +static const qint64 kSecondsPerDay = 24 * kSecondsPerHour; + +} // namespace + +// static +QString DurationBase::formatSeconds(double dSeconds, Precision precision) { + if (dSeconds < 0.0) { + // negative durations are not supported + return "?"; + } + + const qint64 days = static_cast(std::floor(dSeconds)) / kSecondsPerDay; + dSeconds -= days * kSecondsPerDay; + + // NOTE(uklotzde): QTime() constructs a 'null' object, but + // we need 'zero' here. + QTime t = QTime(0, 0).addMSecs(dSeconds * kMillisPerSecond); + + QString formatString = + (days > 0 ? (QString::number(days) % + QLatin1String("'d', ")) : QString()) % + QLatin1String(days > 0 || t.hour() > 0 ? "hh:mm:ss" : "mm:ss") % + QLatin1String(Precision::SECONDS == precision ? "" : ".zzz"); + + QString durationString = t.toString(formatString); + + // The format string gives us milliseconds but we want + // centiseconds. Slice one character off. + if (Precision::CENTISECONDS == precision) { + DEBUG_ASSERT(1 <= durationString.length()); + durationString = durationString.left(durationString.length() - 1); + } + + return durationString; +} + +} // namespace mixxx diff --git a/src/util/duration.h b/src/util/duration.h index 0c72a3612d5..b158be94b33 100644 --- a/src/util/duration.h +++ b/src/util/duration.h @@ -10,17 +10,9 @@ #include "util/assert.h" namespace mixxx { -namespace { - -const qint64 kMillisPerSecond = 1e3; -const qint64 kMicrosPerSecond = 1e6; -const qint64 kNanosPerSecond = 1e9; -const qint64 kNanosPerMilli = 1e6; -const qint64 kNanosPerMicro = 1e3; - -} // namespace class DurationBase { + public: enum Units { HEX, @@ -71,11 +63,29 @@ class DurationBase { return static_cast(m_durationNanos); } + enum class Precision { + SECONDS, + CENTISECONDS, + MILLISECONDS + }; + + // The standard way of formatting a floating-point duration in seconds. + // Used for display of track duration, etc. + static QString formatSeconds( + double dSeconds, + Precision precision = Precision::SECONDS); + protected: DurationBase(qint64 durationNanos) : m_durationNanos(durationNanos) { } + static const qint64 kMillisPerSecond = 1000; + static const qint64 kMicrosPerSecond = kMillisPerSecond * 1000; + static const qint64 kNanosPerSecond = kMicrosPerSecond * 1000; + static const qint64 kNanosPerMilli = kNanosPerSecond / 1000; + static const qint64 kNanosPerMicro = kNanosPerMilli / 1000; + qint64 m_durationNanos; }; diff --git a/src/util/time.cpp b/src/util/time.cpp index 231a96527da..2cd563ab393 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -1,45 +1,10 @@ #include "util/time.h" -#include -#include -#include - -#include "util/assert.h" - // static LLTIMER Time::s_timer; + // static bool Time::s_testMode = false; -// static -mixxx::Duration Time::s_testElapsed = mixxx::Duration::fromNanos(0); // static -QString Time::formatSeconds(double dSeconds, Precision precision) { - if (dSeconds < 0) { - return "?"; - } - - const int days = static_cast(dSeconds) / kSecondsPerDay; - dSeconds -= days * kSecondsPerDay; - - // NOTE(uklotzde): Time() constructs a 'null' object, but - // we need 'zero' here. - QTime t = QTime(0, 0).addMSecs(dSeconds * kMillisPerSecond); - - QString formatString = - (days > 0 ? (QString::number(days) % - QLatin1String("'d', ")) : QString()) % - QLatin1String(days > 0 || t.hour() > 0 ? "hh:mm:ss" : "mm:ss") % - QLatin1String(Precision::SECONDS == precision ? "" : ".zzz"); - - QString timeString = t.toString(formatString); - - // The format string gives us milliseconds but we want - // centiseconds. Slice one character off. - if (Precision::CENTISECONDS == precision) { - DEBUG_ASSERT(1 <= timeString.length()); - timeString = timeString.left(timeString.length() - 1); - } - - return timeString; -} +mixxx::Duration Time::s_testElapsed = mixxx::Duration::fromNanos(0); diff --git a/src/util/time.h b/src/util/time.h index b6d04f74b27..6840c93d2a3 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -1,8 +1,6 @@ #ifndef UTIL_TIME_H #define UTIL_TIME_H -#include - #include "util/performancetimer.h" #include "util/threadcputimer.h" #include "util/timer.h" @@ -13,11 +11,6 @@ class Time { public: - static const int kMillisPerSecond = 1000; - static const int kSecondsPerMinute = 60; - static const int kSecondsPerHour = 60 * kSecondsPerMinute; - static const int kSecondsPerDay = 24 * kSecondsPerHour; - static void start() { s_timer.start(); } @@ -40,17 +33,6 @@ class Time { s_testElapsed = elapsed; } - enum class Precision { - SECONDS, - CENTISECONDS, - MILLISECONDS - }; - - // The standard way of formatting a time in seconds. Used for display - // of track duration, etc. - static QString formatSeconds(double dSeconds, - Precision precision = Time::Precision::SECONDS); - private: static LLTIMER s_timer; diff --git a/src/widget/wnumberpos.cpp b/src/widget/wnumberpos.cpp index d525b6f1cce..9280b6b4759 100644 --- a/src/widget/wnumberpos.cpp +++ b/src/widget/wnumberpos.cpp @@ -6,7 +6,7 @@ #include "control/controlobject.h" #include "control/controlproxy.h" #include "util/math.h" -#include "util/time.h" +#include "util/duration.h" WNumberPos::WNumberPos(const char* group, QWidget* parent) : WNumber(parent), @@ -77,25 +77,26 @@ void WNumberPos::setValue(double dValue) { void WNumberPos::slotSetValue(double dValue) { m_dOldValue = dValue; - double valueMillis = 0.0; + double dPosSeconds = 0.0; if (m_dTrackSamples > 0 && m_dTrackSampleRate > 0) { - double dDuration = m_dTrackSamples / m_dTrackSampleRate / 2.0; - valueMillis = dValue * 500.0 * m_dTrackSamples / m_dTrackSampleRate; - double durationMillis = dDuration * Time::kMillisPerSecond; + double dDurationSeconds = (m_dTrackSamples / 2.0) / m_dTrackSampleRate; + double dDurationMillis = dDurationSeconds * 1000.0; + double dPosMillis = dValue * dDurationMillis; if (m_bRemain) { - valueMillis = math_max(durationMillis - valueMillis, 0.0); + dPosMillis = math_max(dDurationMillis - dPosMillis, 0.0); } + dPosSeconds = dPosMillis / 1000.0; } - QString valueString; - if (valueMillis >= 0) { - valueString = m_skinText % Time::formatSeconds( - valueMillis / Time::kMillisPerSecond, Time::Precision::CENTISECONDS); + QString sPosText; + if (dPosSeconds >= 0.0) { + sPosText = m_skinText % mixxx::Duration::formatSeconds( + dPosSeconds, mixxx::Duration::Precision::CENTISECONDS); } else { - valueString = m_skinText % QLatin1String("-") % Time::formatSeconds( - -valueMillis / Time::kMillisPerSecond, Time::Precision::CENTISECONDS); + sPosText = m_skinText % QLatin1String("-") % mixxx::Duration::formatSeconds( + -dPosSeconds, mixxx::Duration::Precision::CENTISECONDS); } - setText(valueString); + setText(sPosText); } void WNumberPos::slotSetRemain(double remain) { From b0debc788f0c7ae628e67cbba9f29973881b4703 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Fri, 24 Jun 2016 16:12:59 +0200 Subject: [PATCH 02/10] Calculate duration (in seconds) as double value in AudioSource --- src/sources/audiosource.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sources/audiosource.h b/src/sources/audiosource.h index b5d123597fb..21e83fdcb1a 100644 --- a/src/sources/audiosource.h +++ b/src/sources/audiosource.h @@ -47,9 +47,9 @@ class AudioSource: public UrlResource, public AudioSignal { inline bool hasDuration() const { return isValid(); } - inline SINT getDuration() const { + inline double getDuration() const { DEBUG_ASSERT(hasDuration()); // prevents division by zero - return getFrameCount() / getSamplingRate(); + return double(getFrameCount()) / double(getSamplingRate()); } // The bitrate is measured in kbit/s (kbps). From 8ce8f1f5c7776b596c381feff72cf03ac24be402 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 00:28:37 +0200 Subject: [PATCH 03/10] Enhance precision of track duration from int to double --- res/schema.xml | 8 ++++ src/library/autodj/autodjprocessor.cpp | 30 +++++++-------- src/library/autodj/autodjprocessor.h | 8 ++-- src/library/autodj/dlgautodj.cpp | 2 +- src/library/banshee/bansheeplaylistmodel.cpp | 2 +- src/library/browse/browsethread.cpp | 3 +- src/library/dao/trackdao.cpp | 12 +++--- src/library/dlgtrackinfo.cpp | 2 +- src/library/trackcollection.cpp | 2 +- src/musicbrainz/tagfetcher.cpp | 2 +- src/sources/soundsourcemodplug.cpp | 2 +- src/sources/soundsourceopus.cpp | 2 +- src/track/track.cpp | 30 +++++++++++---- src/track/track.h | 40 ++++++++++++++++---- src/track/trackmetadata.cpp | 15 ++++---- src/track/trackmetadata.h | 8 ++-- src/track/trackmetadatataglib.cpp | 4 +- 17 files changed, 112 insertions(+), 60 deletions(-) diff --git a/res/schema.xml b/res/schema.xml index de6d75a8d23..6749fa537b3 100644 --- a/res/schema.xml +++ b/res/schema.xml @@ -416,4 +416,12 @@ METADATA ALTER TABLE library ADD COLUMN tracktotal TEXT DEFAULT '//'; + + + Store track durations with subsecond precision in new column 'duration_real'. + + + ALTER TABLE library ADD COLUMN duration_real REAL DEFAULT duration; + + diff --git a/src/library/autodj/autodjprocessor.cpp b/src/library/autodj/autodjprocessor.cpp index c88a13824be..497e7e4db94 100644 --- a/src/library/autodj/autodjprocessor.cpp +++ b/src/library/autodj/autodjprocessor.cpp @@ -9,7 +9,7 @@ #define kConfigKey "[Auto DJ]" const char* kTransitionPreferenceName = "Transition"; -const int kTransitionPreferenceDefault = 10; +const double kTransitionPreferenceDefault = 10.0; static const bool sDebug = false; @@ -73,7 +73,7 @@ AutoDJProcessor::AutoDJProcessor(QObject* pParent, m_pPlayerManager(pPlayerManager), m_pAutoDJTableModel(NULL), m_eState(ADJ_DISABLED), - m_iTransitionTime(kTransitionPreferenceDefault), + m_transitionTime(kTransitionPreferenceDefault), m_nextTransitionTime(kTransitionPreferenceDefault) { m_pAutoDJTableModel = new PlaylistTableModel(this, pTrackCollection, "mixxx.db.model.autodj"); @@ -123,8 +123,8 @@ AutoDJProcessor::AutoDJProcessor(QObject* pParent, QString str_autoDjTransition = m_pConfig->getValueString( ConfigKey(kConfigKey, kTransitionPreferenceName)); if (!str_autoDjTransition.isEmpty()) { - m_iTransitionTime = str_autoDjTransition.toInt(); - m_nextTransitionTime = m_iTransitionTime; + m_transitionTime = str_autoDjTransition.toDouble(); + m_nextTransitionTime = m_transitionTime; } } @@ -707,37 +707,35 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, if (fromTrack) { // TODO(rryan): Duration is super inaccurate! We should be using // track_samples / track_samplerate instead. - int fromTrackDuration = fromTrack->getDuration(); + double fromTrackDuration = fromTrack->getDuration(); qDebug() << fromTrack->getLocation() << "fromTrackDuration =" << fromTrackDuration; // The track might be shorter than the transition period. Use a // sensible cap. - m_nextTransitionTime = math_min(m_iTransitionTime, - fromTrackDuration / 2); + m_nextTransitionTime = math_min(m_transitionTime, + fromTrackDuration / 2.0); if (pToDeck) { TrackPointer toTrack = pToDeck->getLoadedTrack(); if (toTrack) { // TODO(rryan): Duration is super inaccurate! We should be using // track_samples / track_samplerate instead. - int toTrackDuration = toTrack->getDuration(); + double toTrackDuration = toTrack->getDuration(); qDebug() << toTrack->getLocation() << "toTrackDuration = " << toTrackDuration; m_nextTransitionTime = math_min(m_nextTransitionTime, - toTrackDuration / 2); + toTrackDuration / 2.0); } } - if (fromTrackDuration > 0) { - pFromDeck->fadeDuration = - static_cast(m_nextTransitionTime) / - static_cast(fromTrackDuration); + if (fromTrackDuration > 0.0) { + pFromDeck->fadeDuration = m_nextTransitionTime / fromTrackDuration; } else { - pFromDeck->fadeDuration = 0; + pFromDeck->fadeDuration = 0.0; } - if (m_nextTransitionTime > 0) { + if (m_nextTransitionTime > 0.0) { pFromDeck->posThreshold = 1.0 - pFromDeck->fadeDuration; } else { // in case of pause transition @@ -826,7 +824,7 @@ void AutoDJProcessor::setTransitionTime(int time) { // Update the transition time first. m_pConfig->set(ConfigKey(kConfigKey, kTransitionPreferenceName), ConfigValue(time)); - m_iTransitionTime = time; + m_transitionTime = time; // Then re-calculate fade thresholds for the decks. if (m_eState == ADJ_IDLE) { diff --git a/src/library/autodj/autodjprocessor.h b/src/library/autodj/autodjprocessor.h index f243e6f3134..8a560a50b9b 100644 --- a/src/library/autodj/autodjprocessor.h +++ b/src/library/autodj/autodjprocessor.h @@ -123,8 +123,8 @@ class AutoDJProcessor : public QObject { return m_eState; } - int getTransitionTime() const { - return m_iTransitionTime; + double getTransitionTime() const { + return m_transitionTime; } PlaylistTableModel* getTableModel() const { @@ -197,8 +197,8 @@ class AutoDJProcessor : public QObject { PlaylistTableModel* m_pAutoDJTableModel; AutoDJState m_eState; - int m_iTransitionTime; // the desired value set by the user - int m_nextTransitionTime; // the tweaked value actually used + double m_transitionTime; // the desired value set by the user + double m_nextTransitionTime; // the tweaked value actually used QList m_decks; diff --git a/src/library/autodj/dlgautodj.cpp b/src/library/autodj/dlgautodj.cpp index d00cfb2314e..9b2197e422e 100644 --- a/src/library/autodj/dlgautodj.cpp +++ b/src/library/autodj/dlgautodj.cpp @@ -200,7 +200,7 @@ void DlgAutoDJ::setTrackTableRowHeight(int rowHeight) { } void DlgAutoDJ::updateSelectionInfo() { - int duration = 0; + double duration = 0.0; QModelIndexList indices = m_pTrackTableView->selectionModel()->selectedRows(); diff --git a/src/library/banshee/bansheeplaylistmodel.cpp b/src/library/banshee/bansheeplaylistmodel.cpp index 49e222ecbe2..ec162d478ca 100644 --- a/src/library/banshee/bansheeplaylistmodel.cpp +++ b/src/library/banshee/bansheeplaylistmodel.cpp @@ -305,7 +305,7 @@ TrackPointer BansheePlaylistModel::getTrack(const QModelIndex& index) const { if (pTrack && !track_already_in_library) { pTrack->setArtist(getFieldString(index, CLM_ARTIST)); pTrack->setTitle(getFieldString(index, CLM_TITLE)); - pTrack->setDuration(getFieldString(index, CLM_DURATION).toInt()); + pTrack->setDuration(getFieldString(index, CLM_DURATION).toDouble()); pTrack->setAlbum(getFieldString(index, CLM_ALBUM)); pTrack->setAlbumArtist(getFieldString(index, CLM_ALBUM_ARTIST)); pTrack->setYear(getFieldString(index, CLM_YEAR)); diff --git a/src/library/browse/browsethread.cpp b/src/library/browse/browsethread.cpp index 227fe16c137..dddac5399b7 100644 --- a/src/library/browse/browsethread.cpp +++ b/src/library/browse/browsethread.cpp @@ -10,7 +10,6 @@ #include "library/browse/browsetablemodel.h" #include "sources/soundsourceproxy.h" #include "track/trackmetadata.h" -#include "util/duration.h" #include "util/trace.h" @@ -213,7 +212,7 @@ void BrowseThread::populateModel() { item->setData(item->text(), Qt::UserRole); row_data.insert(COLUMN_COMMENT, item); - QString duration = mixxx::Duration::formatSeconds(pTrack->getDuration()); + QString duration = pTrack->getDurationText(mixxx::Duration::Precision::SECONDS); item = new QStandardItem(duration); item->setToolTip(item->text()); item->setData(item->text(), Qt::UserRole); diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index 4f448027712..a8457608283 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -390,7 +390,7 @@ void TrackDAO::addTracksPrepare() { m_pQueryLibraryInsert->prepare("INSERT INTO library " "(" "artist,title,album,album_artist,year,genre,tracknumber,tracktotal,composer," - "grouping,filetype,location,comment,url,duration,rating,key,key_id," + "grouping,filetype,location,comment,url,duration,duration_real,rating,key,key_id," "bitrate,samplerate,cuepoint,bpm,replaygain,replaygain_peak,wavesummaryhex," "timesplayed,channels,mixxx_deleted,header_parsed," "beats_version,beats_sub_version,beats,bpm_lock," @@ -398,7 +398,7 @@ void TrackDAO::addTracksPrepare() { "coverart_source,coverart_type,coverart_location,coverart_hash" ") VALUES (" ":artist,:title,:album,:album_artist,:year,:genre,:tracknumber,:tracktotal,:composer," - ":grouping,:filetype,:location,:comment,:url,:duration,:rating,:key,:key_id," + ":grouping,:filetype,:location,:comment,:url,:duration,:duration_real,:rating,:key,:key_id," ":bitrate,:samplerate,:cuepoint,:bpm,:replaygain,:replaygain_peak,:wavesummaryhex," ":timesplayed,:channels,:mixxx_deleted,:header_parsed," ":beats_version,:beats_sub_version,:beats,:bpm_lock," @@ -465,7 +465,8 @@ namespace { pTrackLibraryQuery->bindValue(":filetype", track.getType()); pTrackLibraryQuery->bindValue(":comment", track.getComment()); pTrackLibraryQuery->bindValue(":url", track.getURL()); - pTrackLibraryQuery->bindValue(":duration", track.getDuration()); + pTrackLibraryQuery->bindValue(":duration", track.getDurationInt()); + pTrackLibraryQuery->bindValue(":duration_real", track.getDuration()); pTrackLibraryQuery->bindValue(":rating", track.getRating()); pTrackLibraryQuery->bindValue(":bitrate", track.getBitrate()); pTrackLibraryQuery->bindValue(":samplerate", track.getSampleRate()); @@ -1088,7 +1089,7 @@ bool setTrackUrl(const QSqlRecord& record, const int column, bool setTrackDuration(const QSqlRecord& record, const int column, TrackPointer pTrack) { - pTrack->setDuration(record.value(column).toInt()); + pTrack->setDuration(record.value(column).toDouble()); return false; } @@ -1279,7 +1280,7 @@ TrackPointer TrackDAO::getTrackFromDB(TrackId trackId) const { { "rating", setTrackRating }, { "comment", setTrackComment }, { "url", setTrackUrl }, - { "duration", setTrackDuration }, + { "duration_real", setTrackDuration }, { "bitrate", setTrackBitrate }, { "samplerate", setTrackSampleRate }, { "cuepoint", setTrackCuePoint }, @@ -1542,6 +1543,7 @@ bool TrackDAO::updateTrack(Track* pTrack) { "comment=:comment," "url=:url," "duration=:duration," + "duration_real=:duration_real," "rating=:rating," "key=:key," "key_id=:key_id," diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index d3998a9648c..a83c9321b6b 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -165,7 +165,7 @@ void DlgTrackInfo::populateFields(const Track& track) { txtComment->setPlainText(track.getComment()); // Non-editable fields - txtDuration->setText(track.getDurationText()); + txtDuration->setText(track.getDurationText(mixxx::Duration::Precision::SECONDS)); txtLocation->setPlainText(track.getLocation()); txtType->setText(track.getType()); txtBitrate->setText(QString(track.getBitrateText()) + (" ") + tr("kbps")); diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp index 5406bfbf8f8..1af6d52ebb2 100644 --- a/src/library/trackcollection.cpp +++ b/src/library/trackcollection.cpp @@ -14,7 +14,7 @@ #include "util/assert.h" // static -const int TrackCollection::kRequiredSchemaVersion = 26; +const int TrackCollection::kRequiredSchemaVersion = 27; TrackCollection::TrackCollection(UserSettingsPointer pConfig) : m_pConfig(pConfig), diff --git a/src/musicbrainz/tagfetcher.cpp b/src/musicbrainz/tagfetcher.cpp index 516f641571e..9a7320f1583 100644 --- a/src/musicbrainz/tagfetcher.cpp +++ b/src/musicbrainz/tagfetcher.cpp @@ -85,7 +85,7 @@ void TagFetcher::fingerprintFound(int index) { emit(fetchProgress(tr("Identifying track"))); // qDebug() << "start to look up the MBID"; - m_AcoustidClient.start(index, fingerprint, ptrack->getDuration()); + m_AcoustidClient.start(index, fingerprint, ptrack->getDurationInt()); } void TagFetcher::mbidFound(int index, const QString& mbid) { diff --git a/src/sources/soundsourcemodplug.cpp b/src/sources/soundsourcemodplug.cpp index 627e2300405..bfeaf9cb007 100644 --- a/src/sources/soundsourcemodplug.cpp +++ b/src/sources/soundsourcemodplug.cpp @@ -77,7 +77,7 @@ Result SoundSourceModPlug::parseTrackMetadataAndCoverArt( if (nullptr != pModFile) { pTrackMetadata->setComment(QString(ModPlug::ModPlug_GetMessage(pModFile))); pTrackMetadata->setTitle(QString(ModPlug::ModPlug_GetName(pModFile))); - pTrackMetadata->setDuration(ModPlug::ModPlug_GetLength(pModFile) / 1000); + pTrackMetadata->setDuration(ModPlug::ModPlug_GetLength(pModFile) / 1000.0); pTrackMetadata->setBitrate(8); // not really, but fill in something... ModPlug::ModPlug_Unload(pModFile); } diff --git a/src/sources/soundsourceopus.cpp b/src/sources/soundsourceopus.cpp index 4a87feea229..9747eb99fac 100644 --- a/src/sources/soundsourceopus.cpp +++ b/src/sources/soundsourceopus.cpp @@ -84,7 +84,7 @@ Result SoundSourceOpus::parseTrackMetadataAndCoverArt( pTrackMetadata->setSampleRate(kSamplingRate); pTrackMetadata->setBitrate(op_bitrate(l_ptrOpusFile, -1) / 1000); pTrackMetadata->setDuration( - op_pcm_total(l_ptrOpusFile, -1) / pTrackMetadata->getSampleRate()); + double(op_pcm_total(l_ptrOpusFile, -1)) / double(pTrackMetadata->getSampleRate())); bool hasDate = false; for (i = 0; i < l_ptrOpusTags->comments; ++i) { diff --git a/src/track/track.cpp b/src/track/track.cpp index 88a423ed380..1ed5b111073 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -11,7 +11,6 @@ #include "track/trackmetadatataglib.h" #include "util/assert.h" #include "util/compatibility.h" -#include "util/duration.h" namespace { @@ -373,21 +372,38 @@ void Track::setDateAdded(const QDateTime& dateAdded) { m_dateAdded = dateAdded; } -void Track::setDuration(int iDuration) { +void Track::setDuration(double duration) { QMutexLocker lock(&m_qMutex); - if (m_metadata.getDuration() != iDuration) { - m_metadata.setDuration(iDuration); + if (m_metadata.getDuration() != duration) { + m_metadata.setDuration(duration); markDirtyAndUnlock(&lock); } } -int Track::getDuration() const { +double Track::getDuration(DurationRounding rounding) const { QMutexLocker lock(&m_qMutex); + switch (rounding) { + case DurationRounding::SECONDS: + return std::round(m_metadata.getDuration()); + case DurationRounding::NONE: + return m_metadata.getDuration(); + } + // unreachable code / avoid compiler warnings + DEBUG_ASSERT(!"unhandled enum value"); return m_metadata.getDuration(); } -QString Track::getDurationText() const { - return mixxx::Duration::formatSeconds(getDuration()); +QString Track::getDurationText(mixxx::Duration::Precision precision) const { + double duration; + if (precision == mixxx::Duration::Precision::SECONDS) { + // Round to full seconds before formatting for consistency: + // getDurationText() should always display the same number + // as getDuration(DurationRounding::SECONDS) = getDurationInt() + duration = getDuration(DurationRounding::SECONDS); + } else { + duration = getDuration(DurationRounding::NONE); + } + return mixxx::Duration::formatSeconds(duration, precision); } QString Track::getTitle() const { diff --git a/src/track/track.h b/src/track/track.h index 351a80bfb05..3eda94af8ce 100644 --- a/src/track/track.h +++ b/src/track/track.h @@ -17,6 +17,7 @@ #include "track/playcounter.h" #include "track/trackmetadata.h" #include "util/sandbox.h" +#include "util/duration.h" #include "waveform/waveform.h" class Track; @@ -59,8 +60,10 @@ class Track : public QObject { Q_PROPERTY(double bpm READ getBpm WRITE setBpm) Q_PROPERTY(QString bpmFormatted READ getBpmText STORED false) Q_PROPERTY(QString key READ getKeyText WRITE setKeyText) - Q_PROPERTY(int duration READ getDuration WRITE setDuration) - Q_PROPERTY(QString durationFormatted READ getDurationText STORED false) + Q_PROPERTY(double duration READ getDuration WRITE setDuration) + Q_PROPERTY(QString durationFormatted READ getDurationTextSeconds STORED false) + Q_PROPERTY(QString durationFormattedCentiseconds READ getDurationTextCentiseconds STORED false) + Q_PROPERTY(QString durationFormattedMilliseconds READ getDurationTextMilliseconds STORED false) QFileInfo getFileInfo() const { // Copying a QFileInfo is thread-safe (implicit sharing), no locking needed. @@ -114,12 +117,27 @@ class Track : public QObject { // Returns the bitrate as a string QString getBitrateText() const; - // Set duration in seconds - void setDuration(int); - // Returns the duration in seconds - int getDuration() const; - // Returns the duration as a string: H:MM:SS - QString getDurationText() const; + void setDuration(double duration); + double getDuration() const { + return getDuration(DurationRounding::NONE); + } + // Returns the duration rounded to seconds + int getDurationInt() const { + return static_cast(getDuration(DurationRounding::SECONDS)); + } + // Returns the duration formatted as a string (H:MM:SS or H:MM:SS.cc or H:MM:SS.mmm) + QString getDurationText(mixxx::Duration::Precision precision) const; + + // Helper functions for Q_PROPERTYs + QString getDurationTextSeconds() const { + return getDurationText(mixxx::Duration::Precision::SECONDS); + } + QString getDurationTextCentiseconds() const { + return getDurationText(mixxx::Duration::Precision::CENTISECONDS); + } + QString getDurationTextMilliseconds() const { + return getDurationText(mixxx::Duration::Precision::MILLISECONDS); + } // Set BPM double setBpm(double); @@ -324,6 +342,12 @@ class Track : public QObject { // Only used by TrackDAO! void setId(TrackId id); + enum class DurationRounding { + SECONDS, // rounded to full seconds + NONE // unmodified + }; + double getDuration(DurationRounding rounding) const; + // The file const QFileInfo m_fileInfo; diff --git a/src/track/trackmetadata.cpp b/src/track/trackmetadata.cpp index 77d0f777079..5affb01425c 100644 --- a/src/track/trackmetadata.cpp +++ b/src/track/trackmetadata.cpp @@ -67,14 +67,19 @@ QString TrackMetadata::reformatYear(QString year) { } TrackMetadata::TrackMetadata() - : m_bitrate(0), + : m_duration(0.0), + m_bitrate(0), m_channels(0), - m_duration(0), m_sampleRate(0) { } bool operator==(const TrackMetadata& lhs, const TrackMetadata& rhs) { - return (lhs.getArtist() == rhs.getArtist()) && + // Compare the integer and double fields 1st for maximum efficiency + return (lhs.getBitrate() == rhs.getBitrate()) && + (lhs.getChannels() == rhs.getChannels()) && + (lhs.getSampleRate() == rhs.getSampleRate()) && + (lhs.getDuration() == rhs.getDuration()) && + (lhs.getArtist() == rhs.getArtist()) && (lhs.getTitle() == rhs.getTitle()) && (lhs.getAlbum() == rhs.getAlbum()) && (lhs.getAlbumArtist() == rhs.getAlbumArtist()) && @@ -86,10 +91,6 @@ bool operator==(const TrackMetadata& lhs, const TrackMetadata& rhs) { (lhs.getComposer() == rhs.getComposer()) && (lhs.getGrouping() == rhs.getGrouping()) && (lhs.getKey() == rhs.getKey()) && - (lhs.getChannels() == rhs.getChannels()) && - (lhs.getSampleRate() == rhs.getSampleRate()) && - (lhs.getBitrate() == rhs.getBitrate()) && - (lhs.getDuration() == rhs.getDuration()) && (lhs.getBpm() == rhs.getBpm()) && (lhs.getReplayGain() == rhs.getReplayGain()); } diff --git a/src/track/trackmetadata.h b/src/track/trackmetadata.h index d80cb0a6b1c..58cef6bb859 100644 --- a/src/track/trackmetadata.h +++ b/src/track/trackmetadata.h @@ -122,10 +122,10 @@ class TrackMetadata { } // #seconds - int getDuration() const { + double getDuration() const { return m_duration; } - void setDuration(int duration) { + void setDuration(double duration) { m_duration = duration; } @@ -189,10 +189,12 @@ class TrackMetadata { Bpm m_bpm; ReplayGain m_replayGain; + // Floating-point fields (in alphabetical order) + double m_duration; // seconds + // Integer fields (in alphabetical order) int m_bitrate; // kbit/s int m_channels; - int m_duration; // seconds int m_sampleRate; // Hz }; diff --git a/src/track/trackmetadatataglib.cpp b/src/track/trackmetadatataglib.cpp index 021190727a7..6818e1f6c3f 100644 --- a/src/track/trackmetadatataglib.cpp +++ b/src/track/trackmetadatataglib.cpp @@ -252,8 +252,10 @@ void readAudioProperties(TrackMetadata* pTrackMetadata, pTrackMetadata->setChannels(audioProperties.channels()); pTrackMetadata->setSampleRate(audioProperties.sampleRate()); - pTrackMetadata->setDuration(audioProperties.length()); pTrackMetadata->setBitrate(audioProperties.bitrate()); + // Duration will be replaced by the actual length with + // sub-second precision later + pTrackMetadata->setDuration(audioProperties.length()); } bool readAudioProperties(TrackMetadata* pTrackMetadata, From 1d8d9331ccddbc4d52412f64efc465cc79b64098 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 00:56:32 +0200 Subject: [PATCH 04/10] Read track duration with millisecond precision from TagLib Requires TagLib v1.10 or newer. --- src/track/trackmetadatataglib.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/track/trackmetadatataglib.cpp b/src/track/trackmetadatataglib.cpp index 6818e1f6c3f..b28b6112bbb 100644 --- a/src/track/trackmetadatataglib.cpp +++ b/src/track/trackmetadatataglib.cpp @@ -253,9 +253,10 @@ void readAudioProperties(TrackMetadata* pTrackMetadata, pTrackMetadata->setChannels(audioProperties.channels()); pTrackMetadata->setSampleRate(audioProperties.sampleRate()); pTrackMetadata->setBitrate(audioProperties.bitrate()); - // Duration will be replaced by the actual length with - // sub-second precision later - pTrackMetadata->setDuration(audioProperties.length()); + // Duration will be replaced by the actual length with a more + // precise value when reading the audio data. + pTrackMetadata->setDuration( + audioProperties.lengthInMilliseconds() / 1000.0); } bool readAudioProperties(TrackMetadata* pTrackMetadata, From db2e002d74108f352fbad727a972e179065c3b58 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 12:13:59 +0200 Subject: [PATCH 05/10] Fix build for older versions of TagLib before 1.10 Ubuntu 14.04 only provides TagLib 1.9.x --- src/track/trackmetadatataglib.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/track/trackmetadatataglib.cpp b/src/track/trackmetadatataglib.cpp index b28b6112bbb..e5f93cd20f3 100644 --- a/src/track/trackmetadatataglib.cpp +++ b/src/track/trackmetadatataglib.cpp @@ -5,6 +5,10 @@ #include "util/assert.h" #include "util/memory.h" +// TagLib has full support for MP4 atom types since version 1.8 +#define TAGLIB_HAS_MP4_ATOM_TYPES \ + (TAGLIB_MAJOR_VERSION > 1) || ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 8)) + // TagLib has support for the Ogg Opus file format since version 1.9 #define TAGLIB_HAS_OPUSFILE \ ((TAGLIB_MAJOR_VERSION > 1) || ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 9))) @@ -13,14 +17,14 @@ #define TAGLIB_HAS_WAV_ID3V2TAG \ (TAGLIB_MAJOR_VERSION > 1) || ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 9)) -// TagLib has full support for MP4 atom types since version 1.8 -#define TAGLIB_HAS_MP4_ATOM_TYPES \ - (TAGLIB_MAJOR_VERSION > 1) || ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 8)) - // TagLib has support for has() style functions since version 1.9 #define TAGLIB_HAS_TAG_CHECK \ (TAGLIB_MAJOR_VERSION > 1) || ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 9)) +// TagLib has support for length in milliseconds since version 1.10 +#define TAGLIB_HAS_LENGTH_IN_MILLISECONDS \ + (TAGLIB_MAJOR_VERSION > 1) || ((TAGLIB_MAJOR_VERSION == 1) && (TAGLIB_MINOR_VERSION >= 10)) + #ifdef _WIN32 static_assert(sizeof(wchar_t) == sizeof(QChar), "wchar_t is not the same size than QChar"); #define TAGLIB_FILENAME_FROM_QSTRING(fileName) (const wchar_t*)fileName.utf16() @@ -250,13 +254,20 @@ void readAudioProperties(TrackMetadata* pTrackMetadata, const TagLib::AudioProperties& audioProperties) { DEBUG_ASSERT(pTrackMetadata); + // NOTE(uklotzde): All audio properties will be updated + // with the actual (and more precise) values when reading + // the audio data for this track. Often those properties + // stored in tags don't match with the corresponding + // audio data in the file. pTrackMetadata->setChannels(audioProperties.channels()); pTrackMetadata->setSampleRate(audioProperties.sampleRate()); pTrackMetadata->setBitrate(audioProperties.bitrate()); - // Duration will be replaced by the actual length with a more - // precise value when reading the audio data. - pTrackMetadata->setDuration( - audioProperties.lengthInMilliseconds() / 1000.0); +#if TAGLIB_HAS_LENGTH_IN_MILLISECONDS + double duration = audioProperties.lengthInMilliseconds() / 1000.0; +#else + double duration = audioProperties.length(); +#endif + pTrackMetadata->setDuration(duration); } bool readAudioProperties(TrackMetadata* pTrackMetadata, From 99f990af0021562a11e3add1337369be1fda6eb9 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 18:11:39 +0200 Subject: [PATCH 06/10] Use integer divisor --- src/library/autodj/autodjprocessor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/library/autodj/autodjprocessor.cpp b/src/library/autodj/autodjprocessor.cpp index 497e7e4db94..080785df209 100644 --- a/src/library/autodj/autodjprocessor.cpp +++ b/src/library/autodj/autodjprocessor.cpp @@ -714,7 +714,7 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, // The track might be shorter than the transition period. Use a // sensible cap. m_nextTransitionTime = math_min(m_transitionTime, - fromTrackDuration / 2.0); + fromTrackDuration / 2); if (pToDeck) { TrackPointer toTrack = pToDeck->getLoadedTrack(); @@ -725,7 +725,7 @@ void AutoDJProcessor::calculateTransition(DeckAttributes* pFromDeck, qDebug() << toTrack->getLocation() << "toTrackDuration = " << toTrackDuration; m_nextTransitionTime = math_min(m_nextTransitionTime, - toTrackDuration / 2.0); + toTrackDuration / 2); } } From c160ca93bec6e7ac0436d6b759e4a6a3223c4b17 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 18:15:51 +0200 Subject: [PATCH 07/10] Remove obsolete cast to double --- src/sources/soundsourceopus.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sources/soundsourceopus.cpp b/src/sources/soundsourceopus.cpp index 9747eb99fac..932ab5882da 100644 --- a/src/sources/soundsourceopus.cpp +++ b/src/sources/soundsourceopus.cpp @@ -83,8 +83,9 @@ Result SoundSourceOpus::parseTrackMetadataAndCoverArt( pTrackMetadata->setChannels(op_channel_count(l_ptrOpusFile, -1)); pTrackMetadata->setSampleRate(kSamplingRate); pTrackMetadata->setBitrate(op_bitrate(l_ptrOpusFile, -1) / 1000); - pTrackMetadata->setDuration( - double(op_pcm_total(l_ptrOpusFile, -1)) / double(pTrackMetadata->getSampleRate())); + // Cast to double is required for duration with sub-second precision + const double dTotalFrames = op_pcm_total(l_ptrOpusFile, -1); + pTrackMetadata->setDuration(dTotalFrames / pTrackMetadata->getSampleRate()); bool hasDate = false; for (i = 0; i < l_ptrOpusTags->comments; ++i) { From 86c9a63702789fba8d410cd918e552ae4b0a91a8 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 18:41:09 +0200 Subject: [PATCH 08/10] Replace magic number with constant --- src/track/trackmetadatataglib.cpp | 7 +++++-- src/util/duration.h | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/track/trackmetadatataglib.cpp b/src/track/trackmetadatataglib.cpp index e5f93cd20f3..26a4eba9061 100644 --- a/src/track/trackmetadatataglib.cpp +++ b/src/track/trackmetadatataglib.cpp @@ -3,6 +3,7 @@ #include "track/tracknumbers.h" #include "util/assert.h" +#include "util/duration.h" #include "util/memory.h" // TagLib has full support for MP4 atom types since version 1.8 @@ -263,9 +264,11 @@ void readAudioProperties(TrackMetadata* pTrackMetadata, pTrackMetadata->setSampleRate(audioProperties.sampleRate()); pTrackMetadata->setBitrate(audioProperties.bitrate()); #if TAGLIB_HAS_LENGTH_IN_MILLISECONDS - double duration = audioProperties.lengthInMilliseconds() / 1000.0; + // Cast to double is required for duration with sub-second precision + const double dLengthInMilliseconds = audioProperties.lengthInMilliseconds(); + const double duration = dLengthInMilliseconds / mixxx::Duration::kMillisPerSecond; #else - double duration = audioProperties.length(); + const double duration = audioProperties.length(); #endif pTrackMetadata->setDuration(duration); } diff --git a/src/util/duration.h b/src/util/duration.h index b158be94b33..4c375d60a4d 100644 --- a/src/util/duration.h +++ b/src/util/duration.h @@ -75,17 +75,17 @@ class DurationBase { double dSeconds, Precision precision = Precision::SECONDS); - protected: - DurationBase(qint64 durationNanos) - : m_durationNanos(durationNanos) { - } - static const qint64 kMillisPerSecond = 1000; static const qint64 kMicrosPerSecond = kMillisPerSecond * 1000; static const qint64 kNanosPerSecond = kMicrosPerSecond * 1000; static const qint64 kNanosPerMilli = kNanosPerSecond / 1000; static const qint64 kNanosPerMicro = kNanosPerMilli / 1000; + protected: + DurationBase(qint64 durationNanos) + : m_durationNanos(durationNanos) { + } + qint64 m_durationNanos; }; From b7626330b2a02608f2833da98e75b7a48642e487 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sat, 25 Jun 2016 21:33:16 +0200 Subject: [PATCH 09/10] Revert DB schema update and reuse existing duration column For newly created DB files use the correct type for the schema. Existing DB files are backwards compatible according to the documentation: https://www.sqlite.org/datatype3.html "Any column in an SQLite version 3 database, except an INTEGER PRIMARY KEY column, may be used to store a value of any storage class." --- res/schema.xml | 12 ++---------- src/library/dao/trackdao.cpp | 8 +++----- src/library/trackcollection.cpp | 2 +- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/res/schema.xml b/res/schema.xml index 6749fa537b3..e85570fb316 100644 --- a/res/schema.xml +++ b/res/schema.xml @@ -40,7 +40,7 @@ METADATA genre varchar(32), tracknumber varchar(3), location varchar(512) REFERENCES track_locations(location), comment varchar(20), url varchar(256), - duration integer, + duration float, bitrate integer, samplerate integer, cuepoint integer, bpm float, wavesummaryhex blob, @@ -113,7 +113,7 @@ METADATA location integer REFERENCES track_locations(location), comment varchar(256), url varchar(256), - duration integer, + duration float, bitrate integer, samplerate integer, cuepoint integer, @@ -416,12 +416,4 @@ METADATA ALTER TABLE library ADD COLUMN tracktotal TEXT DEFAULT '//'; - - - Store track durations with subsecond precision in new column 'duration_real'. - - - ALTER TABLE library ADD COLUMN duration_real REAL DEFAULT duration; - - diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index a8457608283..f76fd28985a 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -390,7 +390,7 @@ void TrackDAO::addTracksPrepare() { m_pQueryLibraryInsert->prepare("INSERT INTO library " "(" "artist,title,album,album_artist,year,genre,tracknumber,tracktotal,composer," - "grouping,filetype,location,comment,url,duration,duration_real,rating,key,key_id," + "grouping,filetype,location,comment,url,duration,rating,key,key_id," "bitrate,samplerate,cuepoint,bpm,replaygain,replaygain_peak,wavesummaryhex," "timesplayed,channels,mixxx_deleted,header_parsed," "beats_version,beats_sub_version,beats,bpm_lock," @@ -398,7 +398,7 @@ void TrackDAO::addTracksPrepare() { "coverart_source,coverart_type,coverart_location,coverart_hash" ") VALUES (" ":artist,:title,:album,:album_artist,:year,:genre,:tracknumber,:tracktotal,:composer," - ":grouping,:filetype,:location,:comment,:url,:duration,:duration_real,:rating,:key,:key_id," + ":grouping,:filetype,:location,:comment,:url,:duration,:rating,:key,:key_id," ":bitrate,:samplerate,:cuepoint,:bpm,:replaygain,:replaygain_peak,:wavesummaryhex," ":timesplayed,:channels,:mixxx_deleted,:header_parsed," ":beats_version,:beats_sub_version,:beats,:bpm_lock," @@ -465,8 +465,7 @@ namespace { pTrackLibraryQuery->bindValue(":filetype", track.getType()); pTrackLibraryQuery->bindValue(":comment", track.getComment()); pTrackLibraryQuery->bindValue(":url", track.getURL()); - pTrackLibraryQuery->bindValue(":duration", track.getDurationInt()); - pTrackLibraryQuery->bindValue(":duration_real", track.getDuration()); + pTrackLibraryQuery->bindValue(":duration", track.getDuration()); pTrackLibraryQuery->bindValue(":rating", track.getRating()); pTrackLibraryQuery->bindValue(":bitrate", track.getBitrate()); pTrackLibraryQuery->bindValue(":samplerate", track.getSampleRate()); @@ -1543,7 +1542,6 @@ bool TrackDAO::updateTrack(Track* pTrack) { "comment=:comment," "url=:url," "duration=:duration," - "duration_real=:duration_real," "rating=:rating," "key=:key," "key_id=:key_id," diff --git a/src/library/trackcollection.cpp b/src/library/trackcollection.cpp index 1af6d52ebb2..5406bfbf8f8 100644 --- a/src/library/trackcollection.cpp +++ b/src/library/trackcollection.cpp @@ -14,7 +14,7 @@ #include "util/assert.h" // static -const int TrackCollection::kRequiredSchemaVersion = 27; +const int TrackCollection::kRequiredSchemaVersion = 26; TrackCollection::TrackCollection(UserSettingsPointer pConfig) : m_pConfig(pConfig), From 1070a48caa577f8246a828434800f1b8d678dfcb Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Sun, 26 Jun 2016 01:40:53 +0200 Subject: [PATCH 10/10] Fix a leftover from the previous revert commit --- src/library/dao/trackdao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index f76fd28985a..724860d2ada 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -1279,7 +1279,7 @@ TrackPointer TrackDAO::getTrackFromDB(TrackId trackId) const { { "rating", setTrackRating }, { "comment", setTrackComment }, { "url", setTrackUrl }, - { "duration_real", setTrackDuration }, + { "duration", setTrackDuration }, { "bitrate", setTrackBitrate }, { "samplerate", setTrackSampleRate }, { "cuepoint", setTrackCuePoint },