Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance precision of track duration #970

Merged
merged 10 commits into from Jun 25, 2016
1 change: 1 addition & 0 deletions build/depends.py
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions res/schema.xml
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
26 changes: 12 additions & 14 deletions src/library/autodj/autodjprocessor.cpp
Expand Up @@ -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;

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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,
m_nextTransitionTime = math_min(m_transitionTime,
fromTrackDuration / 2);

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);
}
}

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
Expand Down Expand Up @@ -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) {
Expand Down
8 changes: 4 additions & 4 deletions src/library/autodj/autodjprocessor.h
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions src/library/autodj/dlgautodj.cpp
Expand Up @@ -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,
Expand Down Expand Up @@ -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();

Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/library/banshee/bansheeplaylistmodel.cpp
Expand Up @@ -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));
Expand Down
4 changes: 2 additions & 2 deletions src/library/basesqltablemodel.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
}
Expand Down
3 changes: 1 addition & 2 deletions src/library/browse/browsethread.cpp
Expand Up @@ -10,7 +10,6 @@
#include "library/browse/browsetablemodel.h"
#include "sources/soundsourceproxy.h"
#include "track/trackmetadata.h"
#include "util/time.h"
#include "util/trace.h"


Expand Down Expand Up @@ -213,7 +212,7 @@ void BrowseThread::populateModel() {
item->setData(item->text(), Qt::UserRole);
row_data.insert(COLUMN_COMMENT, item);

QString duration = Time::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);
Expand Down
4 changes: 2 additions & 2 deletions src/library/cratefeature.cpp
Expand Up @@ -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,
Expand Down Expand Up @@ -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))));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/library/dao/trackdao.cpp
Expand Up @@ -1088,7 +1088,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;
}

Expand Down
2 changes: 1 addition & 1 deletion src/library/dlgtrackinfo.cpp
Expand Up @@ -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"));
Expand Down
4 changes: 2 additions & 2 deletions src/library/playlistfeature.cpp
Expand Up @@ -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,
Expand Down Expand Up @@ -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))));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/musicbrainz/tagfetcher.cpp
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/sources/audiosource.h
Expand Up @@ -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).
Expand Down
2 changes: 1 addition & 1 deletion src/sources/soundsourcemodplug.cpp
Expand Up @@ -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);
}
Expand Down
5 changes: 3 additions & 2 deletions src/sources/soundsourceopus.cpp
Expand Up @@ -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(
op_pcm_total(l_ptrOpusFile, -1) / 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) {
Expand Down
32 changes: 16 additions & 16 deletions src/test/timeutiltest.cpp → src/test/durationutiltest.cpp
@@ -1,15 +1,15 @@
#include <gtest/gtest.h>

#include "util/time.h"
#include "util/duration.h"

#include <QtDebug>

namespace {

class TimeUtilTest : public testing::Test {
class DurationUtilTest : public testing::Test {
protected:

TimeUtilTest() {
DurationUtilTest() {
}

virtual void SetUp() {
Expand All @@ -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);
}
Expand All @@ -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);
Expand Down