Skip to content

Commit

Permalink
Expose cached waveform information to the user.
Browse files Browse the repository at this point in the history
* Allow the user to disable waveform caching.
* Allow the user to clear cached waveforms on disk.
* Display cached waveform size on disk.
* Explain why cached waveforms are useful.

Partial fix for Bug #1023787.
  • Loading branch information
rryan committed May 29, 2016
1 parent f942b60 commit 59a78fa
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 146 deletions.
59 changes: 54 additions & 5 deletions src/library/dao/analysisdao.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
#include <QSqlError>
#include <QtDebug>

#include "waveform/waveform.h"
#include "library/dao/analysisdao.h"
#include "library/queryutil.h"
#include "preferences/waveformsettings.h"
#include "util/performancetimer.h"
#include "waveform/waveform.h"

const QString AnalysisDao::s_analysisTableName = "track_analysis";

Expand Down Expand Up @@ -83,6 +84,7 @@ QList<AnalysisDao::AnalysisInfo> AnalysisDao::loadAnalysesFromQuery(TrackId trac
const int versionColumn = queryRecord.indexOf("version");
const int dataChecksumColumn = queryRecord.indexOf("data_checksum");

QDir analysisPath(getAnalysisStoragePath());
while (query->next()) {
AnalysisDao::AnalysisInfo info;
info.analysisId = query->value(idColumn).toInt();
Expand All @@ -91,7 +93,7 @@ QList<AnalysisDao::AnalysisInfo> AnalysisDao::loadAnalysesFromQuery(TrackId trac
info.description = query->value(descriptionColumn).toString();
info.version = query->value(versionColumn).toString();
int checksum = query->value(dataChecksumColumn).toInt();
QString dataPath = getAnalysisStoragePath().absoluteFilePath(
QString dataPath = analysisPath.absoluteFilePath(
QString::number(info.analysisId));
QByteArray compressedData = loadDataFromFile(dataPath);
int file_checksum = qChecksum(compressedData.constData(),
Expand Down Expand Up @@ -216,11 +218,10 @@ void AnalysisDao::deleteAnalyses(const QList<TrackId>& trackIds) {
LOG_FAILED_QUERY(query) << "couldn't delete analysis";
}
const int idColumn = query.record().indexOf("id");
QDir analysisPath(getAnalysisStoragePath());
while (query.next()) {
int id = query.value(idColumn).toInt();
QString dataPath = getAnalysisStoragePath().absoluteFilePath(
QString::number(id));
qDebug() << dataPath;
QString dataPath = analysisPath.absoluteFilePath(QString::number(id));
deleteFile(dataPath);
}
query.prepare(QString("DELETE FROM track_analysis "
Expand Down Expand Up @@ -320,6 +321,13 @@ void AnalysisDao::saveTrackAnalyses(Track* pTrack) {
return;
}

// The only analyses we have at the moment are waveform analyses so we have
// nothing to do if it is disabled.
WaveformSettings waveformSettings(m_pConfig);
if (!waveformSettings.waveformCachingEnabled()) {
return;
}

ConstWaveformPointer pWaveform = pTrack->getWaveform();
ConstWaveformPointer pWaveSummary = pTrack->getWaveformSummary();

Expand Down Expand Up @@ -364,3 +372,44 @@ void AnalysisDao::saveTrackAnalyses(Track* pTrack) {
<< "waveform summary analysis for trackId" << trackId
<< "analysisId" << analysis.analysisId;
}

size_t AnalysisDao::getDiskUsageInBytes(AnalysisType type) {
QDir analysisPath(getAnalysisStoragePath());

QSqlQuery query(m_db);
query.prepare(QString("SELECT id FROM %1 WHERE type=:type").arg(s_analysisTableName));
query.bindValue(":type", type);

if (!query.exec()) {
LOG_FAILED_QUERY(query) << "couldn't get analyses of type" << type;
return 0;
}

const int idColumn = query.record().indexOf("id");
size_t total = 0;
while (query.next()) {
total += QFileInfo(analysisPath.absoluteFilePath(
query.value(idColumn).toString())).size();
}
return total;
}

bool AnalysisDao::deleteAnalysesByType(AnalysisType type) {
QDir analysisPath(getAnalysisStoragePath());

QSqlQuery query(m_db);
query.prepare(QString("SELECT id FROM %1 WHERE type=:type").arg(s_analysisTableName));
query.bindValue(":type", type);

if (!query.exec()) {
LOG_FAILED_QUERY(query) << "couldn't get analyses of type" << type;
return false;
}

const int idColumn = query.record().indexOf("id");
while (query.next()) {
QString dataPath = analysisPath.absoluteFilePath(query.value(idColumn).toString());
deleteFile(dataPath);
}
return true;
}
3 changes: 3 additions & 0 deletions src/library/dao/analysisdao.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ class AnalysisDao : public DAO {
bool deleteAnalysis(const int analysisId);
void deleteAnalyses(const QList<TrackId>& trackIds);
bool deleteAnalysesForTrack(TrackId trackId);
bool deleteAnalysesByType(AnalysisType type);

void saveTrackAnalyses(Track* pTrack);

size_t getDiskUsageInBytes(AnalysisType type);

private:
bool saveWaveform(const Track& tio,
const Waveform& waveform,
Expand Down
3 changes: 3 additions & 0 deletions src/library/trackcollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class TrackCollection : public QObject {
TrackDAO& getTrackDAO();
PlaylistDAO& getPlaylistDAO();
DirectoryDAO& getDirectoryDAO();
AnalysisDao& getAnalysisDAO() {
return m_analysisDao;
}
QSharedPointer<BaseTrackCache> getTrackSource();
void setTrackSource(QSharedPointer<BaseTrackCache> trackSource);
void cancelLibraryScan();
Expand Down
2 changes: 1 addition & 1 deletion src/preferences/dialog/dlgpreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ DlgPreferences::DlgPreferences(MixxxMainWindow * mixxx, SkinLoader* pSkinLoader,
pLibrary, SLOT(scan()));
m_wcontrols = new DlgPrefControls(this, mixxx, pSkinLoader, pPlayerManager, m_pConfig);
addPageWidget(m_wcontrols);
m_wwaveform = new DlgPrefWaveform(this, mixxx, m_pConfig);
m_wwaveform = new DlgPrefWaveform(this, mixxx, m_pConfig, pLibrary);
addPageWidget(m_wwaveform);
m_wautodj = new DlgPrefAutoDJ(this, m_pConfig);
addPageWidget(m_wautodj);
Expand Down
67 changes: 54 additions & 13 deletions src/preferences/dialog/dlgprefwaveform.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
#include "preferences/dialog/dlgprefwaveform.h"

#include "mixxx.h"
#include "preferences/waveformsettings.h"
#include "waveform/waveformwidgetfactory.h"
#include "waveform/renderers/waveformwidgetrenderer.h"

DlgPrefWaveform::DlgPrefWaveform(QWidget* pParent, MixxxMainWindow* pMixxx,
UserSettingsPointer pConfig)
UserSettingsPointer pConfig, Library* pLibrary)
: DlgPreferencePage(pParent),
m_pConfig(pConfig),
m_pLibrary(pLibrary),
m_pMixxx(pMixxx) {
setupUi(this);

Expand Down Expand Up @@ -53,20 +55,22 @@ DlgPrefWaveform::DlgPrefWaveform(QWidget* pParent, MixxxMainWindow* pMixxx,
this, SLOT(slotSetDefaultZoom(int)));
connect(synchronizeZoomCheckBox, SIGNAL(clicked(bool)),
this, SLOT(slotSetZoomSynchronization(bool)));
connect(allVisualGain,SIGNAL(valueChanged(double)),
this,SLOT(slotSetVisualGainAll(double)));
connect(lowVisualGain,SIGNAL(valueChanged(double)),
this,SLOT(slotSetVisualGainLow(double)));
connect(midVisualGain,SIGNAL(valueChanged(double)),
this,SLOT(slotSetVisualGainMid(double)));
connect(highVisualGain,SIGNAL(valueChanged(double)),
this,SLOT(slotSetVisualGainHigh(double)));
connect(normalizeOverviewCheckBox,SIGNAL(toggled(bool)),
this,SLOT(slotSetNormalizeOverview(bool)));
connect(allVisualGain, SIGNAL(valueChanged(double)),
this, SLOT(slotSetVisualGainAll(double)));
connect(lowVisualGain, SIGNAL(valueChanged(double)),
this, SLOT(slotSetVisualGainLow(double)));
connect(midVisualGain, SIGNAL(valueChanged(double)),
this, SLOT(slotSetVisualGainMid(double)));
connect(highVisualGain, SIGNAL(valueChanged(double)),
this, SLOT(slotSetVisualGainHigh(double)));
connect(normalizeOverviewCheckBox, SIGNAL(toggled(bool)),
this, SLOT(slotSetNormalizeOverview(bool)));
connect(factory, SIGNAL(waveformMeasured(float,int)),
this, SLOT(slotWaveformMeasured(float,int)));
connect(waveformOverviewComboBox,SIGNAL(currentIndexChanged(int)),
this,SLOT(slotSetWaveformOverviewType(int)));
connect(waveformOverviewComboBox, SIGNAL(currentIndexChanged(int)),
this, SLOT(slotSetWaveformOverviewType(int)));
connect(clearCachedWaveforms, SIGNAL(clicked()),
this, SLOT(slotClearCachedWaveforms()));
}

DlgPrefWaveform::~DlgPrefWaveform() {
Expand Down Expand Up @@ -105,9 +109,15 @@ void DlgPrefWaveform::slotUpdate() {
if (overviewType != waveformOverviewComboBox->currentIndex()) {
waveformOverviewComboBox->setCurrentIndex(overviewType);
}

WaveformSettings waveformSettings(m_pConfig);
enableWaveformCaching->setChecked(waveformSettings.waveformCachingEnabled());
calculateCachedWaveformDiskUsage();
}

void DlgPrefWaveform::slotApply() {
WaveformSettings waveformSettings(m_pConfig);
waveformSettings.setWaveformCachingEnabled(enableWaveformCaching->isChecked());
}

void DlgPrefWaveform::slotResetToDefaults() {
Expand Down Expand Up @@ -141,14 +151,19 @@ void DlgPrefWaveform::slotResetToDefaults() {
// 30FPS is the default
frameRateSlider->setValue(30);
endOfTrackWarningTimeSlider->setValue(30);

// Waveform caching enabled.
enableWaveformCaching->setChecked(true);
}

void DlgPrefWaveform::slotSetFrameRate(int frameRate) {
WaveformWidgetFactory::instance()->setFrameRate(frameRate);
}

void DlgPrefWaveform::slotSetWaveformEndRender(int endTime) {
WaveformWidgetFactory::instance()->setEndOfTrackWarningTime(endTime);
}

void DlgPrefWaveform::slotSetWaveformType(int index) {
// Ignore sets for -1 since this happens when we clear the combobox.
if (index < 0) {
Expand Down Expand Up @@ -195,3 +210,29 @@ void DlgPrefWaveform::slotWaveformMeasured(float frameRate, int droppedFrames) {
QString::number((double)frameRate, 'f', 2) + " : " +
tr("dropped frames") + " " + QString::number(droppedFrames));
}

void DlgPrefWaveform::slotClearCachedWaveforms() {
TrackCollection* pTrackCollection = m_pLibrary->getTrackCollection();
if (pTrackCollection != nullptr) {
AnalysisDao& analysisDao = pTrackCollection->getAnalysisDAO();
analysisDao.deleteAnalysesByType(AnalysisDao::TYPE_WAVEFORM);
analysisDao.deleteAnalysesByType(AnalysisDao::TYPE_WAVESUMMARY);
calculateCachedWaveformDiskUsage();
}
}

void DlgPrefWaveform::calculateCachedWaveformDiskUsage() {
TrackCollection* pTrackCollection = m_pLibrary->getTrackCollection();
if (pTrackCollection != nullptr) {
AnalysisDao& analysisDao = pTrackCollection->getAnalysisDAO();
size_t waveformBytes = analysisDao.getDiskUsageInBytes(AnalysisDao::TYPE_WAVEFORM);
size_t wavesummaryBytes = analysisDao.getDiskUsageInBytes(AnalysisDao::TYPE_WAVESUMMARY);

// Display total cached waveform size in mebibytes with 2 decimals.
QString sizeMebibytes = QString::number(
(waveformBytes + wavesummaryBytes) / (1024.0 * 1024.0), 'f', 2);

waveformDiskUsage->setText(
tr("Cached waveforms occupy %1 MiB on disk.").arg(sizeMebibytes));
}
}
6 changes: 5 additions & 1 deletion src/preferences/dialog/dlgprefwaveform.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <QWidget>

#include "library/library.h"
#include "preferences/dialog/ui_dlgprefwaveformdlg.h"
#include "preferences/usersettings.h"
#include "preferences/dlgpreferencepage.h"
Expand All @@ -13,7 +14,7 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg
Q_OBJECT
public:
DlgPrefWaveform(QWidget* pParent, MixxxMainWindow* pMixxx,
UserSettingsPointer pConfig);
UserSettingsPointer pConfig, Library* pLibrary);
virtual ~DlgPrefWaveform();

public slots:
Expand All @@ -34,11 +35,14 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg
void slotSetVisualGainHigh(double gain);
void slotSetNormalizeOverview(bool normalize);
void slotWaveformMeasured(float frameRate, int droppedFrames);
void slotClearCachedWaveforms();

private:
void initWaveformControl();
void calculateCachedWaveformDiskUsage();

UserSettingsPointer m_pConfig;
Library* m_pLibrary;
MixxxMainWindow* m_pMixxx;
};

Expand Down
Loading

0 comments on commit 59a78fa

Please sign in to comment.