diff options
author | Uwe Klotz <uwe_klotz@web.de> | 2016-06-25 00:28:37 +0200 |
---|---|---|
committer | Uwe Klotz <uwe_klotz@web.de> | 2016-06-25 10:12:31 +0200 |
commit | 8ce8f1f5c7776b596c381feff72cf03ac24be402 (patch) | |
tree | f67cb1e4f907f1d8142cb12856a671e086d28e3a | |
parent | b0debc788f0c7ae628e67cbba9f29973881b4703 (diff) |
Enhance precision of track duration from int to double
-rw-r--r-- | res/schema.xml | 8 | ||||
-rw-r--r-- | src/library/autodj/autodjprocessor.cpp | 30 | ||||
-rw-r--r-- | src/library/autodj/autodjprocessor.h | 8 | ||||
-rw-r--r-- | src/library/autodj/dlgautodj.cpp | 2 | ||||
-rw-r--r-- | src/library/banshee/bansheeplaylistmodel.cpp | 2 | ||||
-rw-r--r-- | src/library/browse/browsethread.cpp | 3 | ||||
-rw-r--r-- | src/library/dao/trackdao.cpp | 12 | ||||
-rw-r--r-- | src/library/dlgtrackinfo.cpp | 2 | ||||
-rw-r--r-- | src/library/trackcollection.cpp | 2 | ||||
-rw-r--r-- | src/musicbrainz/tagfetcher.cpp | 2 | ||||
-rw-r--r-- | src/sources/soundsourcemodplug.cpp | 2 | ||||
-rw-r--r-- | src/sources/soundsourceopus.cpp | 2 | ||||
-rw-r--r-- | src/track/track.cpp | 30 | ||||
-rw-r--r-- | src/track/track.h | 40 | ||||
-rw-r--r-- | src/track/trackmetadata.cpp | 15 | ||||
-rw-r--r-- | src/track/trackmetadata.h | 8 | ||||
-rw-r--r-- | src/track/trackmetadatataglib.cpp | 4 |
17 files changed, 112 insertions, 60 deletions
diff --git a/res/schema.xml b/res/schema.xml index de6d75a8d2..6749fa537b 100644 --- a/res/schema.xml +++ b/res/schema.xml @@ -416,4 +416,12 @@ METADATA ALTER TABLE library ADD COLUMN tracktotal TEXT DEFAULT '//'; </sql> </revision> + <revision version="27" min_compatible="3"> + <description> + Store track durations with subsecond precision in new column 'duration_real'. + </description> + <sql> + ALTER TABLE library ADD COLUMN duration_real REAL DEFAULT duration; + </sql> + </revision> </schema> diff --git a/src/library/autodj/autodjprocessor.cpp b/src/library/autodj/autodjprocessor.cpp index c88a13824b..497e7e4db9 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<double>(m_nextTransitionTime) / - static_cast<double>(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 f243e6f313..8a560a50b9 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<DeckAttributes*> m_decks; diff --git a/src/library/autodj/dlgautodj.cpp b/src/library/autodj/dlgautodj.cpp index d00cfb2314..9b2197e422 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 49e222ecbe..ec162d478c 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 227fe16c13..dddac5399b 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 4f44802771..a845760828 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 d3998a9648..a83c9321b6 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 5406bfbf8f..1af6d52ebb 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 516f641571..9a7320f158 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 627e230040..bfeaf9cb00 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 4a87feea22..9747eb99fa 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 88a423ed38..1ed5b11107 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 351a80bfb0..3eda94af8c 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<int>(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 77d0f77707..5affb01425 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 d80cb0a6b1..58cef6bb85 100644 --- a/src/track/trackmetadata.h +++ b/src/track/trackmetadata.h @@ -122,10 +122,10 @@ public: } // #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 @@ private: 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 021190727a..6818e1f6c3 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, |