#include <QDirIterator>
#include <QMutexLocker>
#include <atomic>
#include "track/track.h"
#include "track/trackref.h"
#include "track/beatfactory.h"
#include "util/assert.h"
#include "util/logger.h"
namespace {
const mixxx::Logger kLogger("Track");
constexpr bool kLogStats = false;
// Count the number of currently existing instances for detecting
// memory leaks.
std::atomic<int> s_numberOfInstances;
SecurityTokenPointer openSecurityToken(
const TrackFile& trackFile,
SecurityTokenPointer pSecurityToken = SecurityTokenPointer()) {
if (pSecurityToken.isNull()) {
return Sandbox::openSecurityToken(trackFile.asFileInfo(), true);
} else {
return pSecurityToken;
}
}
template<typename T>
inline
bool compareAndSet(T* pField, const T& value) {
if (*pField != value) {
*pField = value;
return true;
} else {
return false;
}
}
inline
mixxx::Bpm getActualBpm(
mixxx::Bpm bpm,
BeatsPointer pBeats = BeatsPointer()) {
// Only use the imported BPM if the beat grid is not valid!
// Reason: The BPM value in the metadata might be normalized
// or rounded, e.g. ID3v2 only supports integer values.
if (pBeats) {
return mixxx::Bpm(pBeats->getBpm());
} else {
return bpm;
}
}
} // anonymous namespace
Track::Track(
TrackFile fileInfo,
SecurityTokenPointer pSecurityToken,
TrackId trackId)
: m_qMutex(QMutex::Recursive),
m_fileInfo(std::move(fileInfo)),
m_pSecurityToken(openSecurityToken(m_fileInfo, std::move(pSecurityToken))),
m_record(trackId),
m_bDirty(false),
m_bMarkedForMetadataExport(false) {
if (kLogStats && kLogger.debugEnabled()) {
long numberOfInstancesBefore = s_numberOfInstances.fetch_add(1);
kLogger.debug()
<< "Creating instance:"
<< this
<< numberOfInstancesBefore
<< "->"
<< numberOfInstancesBefore + 1;
}
}
Track::~Track() {
if (kLogStats && kLogger.debugEnabled()) {
long numberOfInstancesBefore = s_numberOfInstances.fetch_sub(1);
kLogger.debug()
<< "Destroying instance:"
<< this
<< numberOfInstancesBefore
<< "->"
<< numberOfInstancesBefore - 1;
}
}
//static
TrackPointer Track::newTemporary(
TrackFile fileInfo,
SecurityTokenPointer pSecurityToken) {
return std::make_shared<Track>(
std::move(fileInfo),
std::move(pSecurityToken));
}
//static
TrackPointer Track::newDummy(
TrackFile fileInfo,
TrackId trackId) {
return std::make_shared<Track>(
std::move(fileInfo),
SecurityTokenPointer(),
trackId);
}
void Track::relocate(
TrackFile fileInfo,
SecurityTokenPointer pSecurityToken) {
QMutexLocker lock(&m_qMutex);
m_pSecurityToken = openSecurityToken(fileInfo, std::move(pSecurityToken));
m_fileInfo = std::move(fileInfo);
// The track does not need to be marked as dirty,
// because this function will always be called with
// the updated location from the database.
}
void Track::setTrackMetadata(
mixxx::TrackMetadata trackMetadata,
QDateTime metadataSynchronized) {
// Safe some values that are needed after move assignment and unlocking(see below)
const auto newBpm = trackMetadata.getTrackInfo().getBpm();
const auto newKey = trackMetadata.getTrackInfo().getKey();
const auto newReplayGain = trackMetadata.getTrackInfo().getReplayGain();
{
// enter locking scope
QMutexLocker lock(&m_qMutex);
bool modified = compareAndSet(
&m_record.refMetadataSynchronized(),
!metadataSynchronized.isNull());
bool modifiedReplayGain = false;
if (m_record.getMetadata()