diff options
Diffstat (limited to 'src/track')
-rw-r--r-- | src/track/albuminfo.cpp | 2 | ||||
-rw-r--r-- | src/track/albuminfo.h | 2 | ||||
-rw-r--r-- | src/track/cue.cpp | 12 | ||||
-rw-r--r-- | src/track/cue.h | 4 | ||||
-rw-r--r-- | src/track/track.cpp | 100 | ||||
-rw-r--r-- | src/track/track.h | 25 | ||||
-rw-r--r-- | src/track/trackinfo.cpp | 2 | ||||
-rw-r--r-- | src/track/trackinfo.h | 2 | ||||
-rw-r--r-- | src/track/trackmetadata.cpp | 88 | ||||
-rw-r--r-- | src/track/trackmetadata.h | 30 | ||||
-rw-r--r-- | src/track/trackmetadatataglib.cpp | 11 |
11 files changed, 220 insertions, 58 deletions
diff --git a/src/track/albuminfo.cpp b/src/track/albuminfo.cpp index 232de95ea4..fdd72f1fc2 100644 --- a/src/track/albuminfo.cpp +++ b/src/track/albuminfo.cpp @@ -18,7 +18,7 @@ bool operator==(const AlbumInfo& lhs, const AlbumInfo& rhs) { } QDebug operator<<(QDebug dbg, const AlbumInfo& arg) { - dbg << '{'; + dbg << "AlbumInfo{"; arg.dbgArtist(dbg); #if defined(__EXTRA_METADATA__) arg.dbgCopyright(dbg); diff --git a/src/track/albuminfo.h b/src/track/albuminfo.h index f4b45125aa..4c9da32339 100644 --- a/src/track/albuminfo.h +++ b/src/track/albuminfo.h @@ -11,7 +11,7 @@ namespace mixxx { class AlbumInfo final { - // Album and release properties (in alphabetical order) + // Properties in alphabetical order PROPERTY_SET_BYVAL_GET_BYREF(QString, artist, Artist) #if defined(__EXTRA_METADATA__) PROPERTY_SET_BYVAL_GET_BYREF(QString, copyright, Copyright) diff --git a/src/track/cue.cpp b/src/track/cue.cpp index 7ba0ad9925..5cdb1fee6c 100644 --- a/src/track/cue.cpp +++ b/src/track/cue.cpp @@ -15,8 +15,8 @@ namespace { inline std::optional<double> positionSamplesToMillis( double positionSamples, - mixxx::AudioSignal::SampleRate sampleRate) { - VERIFY_OR_DEBUG_ASSERT(sampleRate.valid()) { + mixxx::audio::SampleRate sampleRate) { + VERIFY_OR_DEBUG_ASSERT(sampleRate.isValid()) { return Cue::kNoPosition; } if (positionSamples == Cue::kNoPosition) { @@ -28,8 +28,8 @@ inline std::optional<double> positionSamplesToMillis( inline double positionMillisToSamples( std::optional<double> positionMillis, - mixxx::AudioSignal::SampleRate sampleRate) { - VERIFY_OR_DEBUG_ASSERT(sampleRate.valid()) { + mixxx::audio::SampleRate sampleRate) { + VERIFY_OR_DEBUG_ASSERT(sampleRate.isValid()) { return Cue::kNoPosition; } if (!positionMillis) { @@ -87,7 +87,7 @@ Cue::Cue( Cue::Cue( const mixxx::CueInfo& cueInfo, - mixxx::AudioSignal::SampleRate sampleRate) + mixxx::audio::SampleRate sampleRate) : m_bDirty(false), m_iId(-1), m_type(cueInfo.getType()), @@ -105,7 +105,7 @@ Cue::Cue( } mixxx::CueInfo Cue::getCueInfo( - mixxx::AudioSignal::SampleRate sampleRate) const { + mixxx::audio::SampleRate sampleRate) const { QMutexLocker lock(&m_mutex); return mixxx::CueInfo( m_type, diff --git a/src/track/cue.h b/src/track/cue.h index 14f605c28f..fdda9fc063 100644 --- a/src/track/cue.h +++ b/src/track/cue.h @@ -24,7 +24,7 @@ class Cue : public QObject { Cue(); Cue( const mixxx::CueInfo& cueInfo, - mixxx::AudioSignal::SampleRate sampleRate); + mixxx::audio::SampleRate sampleRate); ~Cue() override = default; bool isDirty() const; @@ -56,7 +56,7 @@ class Cue : public QObject { double getEndPosition() const; mixxx::CueInfo getCueInfo( - mixxx::AudioSignal::SampleRate sampleRate) const; + mixxx::audio::SampleRate sampleRate) const; signals: void updated(); diff --git a/src/track/track.cpp b/src/track/track.cpp index 27e04c8282..00c907f0bb 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -357,7 +357,19 @@ void Track::setDateAdded(const QDateTime& dateAdded) { void Track::setDuration(mixxx::Duration duration) { QMutexLocker lock(&m_qMutex); - if (compareAndSet(&m_record.refMetadata().refDuration(), duration)) { + VERIFY_OR_DEBUG_ASSERT(!m_streamInfo || + m_streamInfo->getDuration() <= mixxx::Duration::empty() || + m_streamInfo->getDuration() == duration) { + kLogger.warning() + << "Cannot override stream duration:" + << m_streamInfo->getDuration() + << "->" + << duration; + return; + } + if (compareAndSet( + &m_record.refMetadata().refDuration(), + duration)) { markDirtyAndUnlock(&lock); } } @@ -576,28 +588,14 @@ void Track::setType(const QString& sType) { } } -void Track::setSampleRate(int iSampleRate) { - QMutexLocker lock(&m_qMutex); - if (compareAndSet(&m_record.refMetadata().refSampleRate(), mixxx::AudioSignal::SampleRate(iSampleRate))) { - markDirtyAndUnlock(&lock); - } -} - int Track::getSampleRate() const { QMutexLocker lock(&m_qMutex); return m_record.getMetadata().getSampleRate(); } -void Track::setChannels(int iChannels) { - QMutexLocker lock(&m_qMutex); - if (compareAndSet(&m_record.refMetadata().refChannels(), mixxx::AudioSignal::ChannelCount(iChannels))) { - markDirtyAndUnlock(&lock); - } -} - int Track::getChannels() const { QMutexLocker lock(&m_qMutex); - return m_record.getMetadata().getChannels(); + return m_record.getMetadata().getChannelCount(); } int Track::getBitrate() const { @@ -611,7 +609,20 @@ QString Track::getBitrateText() const { void Track::setBitrate(int iBitrate) { QMutexLocker lock(&m_qMutex); - if (compareAndSet(&m_record.refMetadata().refBitrate(), mixxx::AudioSource::Bitrate(iBitrate))) { + const mixxx::audio::Bitrate bitrate(iBitrate); + VERIFY_OR_DEBUG_ASSERT(!m_streamInfo || + !m_streamInfo->getBitrate().isValid() || + m_streamInfo->getBitrate() == bitrate) { + kLogger.warning() + << "Cannot override stream bitrate:" + << m_streamInfo->getBitrate() + << "->" + << bitrate; + return; + } + if (compareAndSet( + &m_record.refMetadata().refBitrate(), + bitrate)) { markDirtyAndUnlock(&lock); } } @@ -804,7 +815,7 @@ void Track::setCuePoints(const QList<CuePointer>& cuePoints) { void Track::importCuePoints(const QList<mixxx::CueInfo>& cueInfos) { TrackId trackId; - mixxx::AudioSignal::SampleRate sampleRate; + mixxx::audio::SampleRate sampleRate; { QMutexLocker lock(&m_qMutex); trackId = m_record.getId(); @@ -1122,3 +1133,56 @@ ExportTrackMetadataResult Track::exportMetadata( return ExportTrackMetadataResult::Failed; } } + +void Track::setAudioProperties( + mixxx::audio::ChannelCount channelCount, + mixxx::audio::SampleRate sampleRate, + mixxx::audio::Bitrate bitrate, + mixxx::Duration duration) { + QMutexLocker lock(&m_qMutex); + DEBUG_ASSERT(!m_streamInfo); + bool dirty = false; + if (compareAndSet( + &m_record.refMetadata().refChannelCount(), + channelCount)) { + dirty = true; + } + if (compareAndSet( + &m_record.refMetadata().refSampleRate(), + sampleRate)) { + dirty = true; + } + if (compareAndSet( + &m_record.refMetadata().refBitrate(), + bitrate)) { + dirty = true; + } + if (compareAndSet( + &m_record.refMetadata().refDuration(), + duration)) { + dirty = true; + } + if (dirty) { + markDirtyAndUnlock(&lock); + } +} + +void Track::updateAudioPropertiesFromStream( + mixxx::audio::StreamInfo&& streamInfo) { + QMutexLocker lock(&m_qMutex); + VERIFY_OR_DEBUG_ASSERT(!m_streamInfo || + *m_streamInfo == streamInfo) { + kLogger.warning() + << "Varying stream properties:" + << *m_streamInfo + << "->" + << streamInfo; + } + bool updated = m_record.refMetadata().updateAudioPropertiesFromStream( + streamInfo); + m_streamInfo = std::make_optional(std::move(streamInfo)); + // TODO: Continue deferred import of pending CueInfo objects + if (updated) { + markDirtyAndUnlock(&lock); + } +} diff --git a/src/track/track.h b/src/track/track.h index 78965cf65d..530a1d3e2a 100644 --- a/src/track/track.h +++ b/src/track/track.h @@ -5,6 +5,7 @@ #include <QObject> #include <QUrl> +#include "audio/streaminfo.h" #include "track/beats.h" #include "track/cue.h" #include "track/trackfile.h" @@ -68,7 +69,7 @@ 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(double duration READ getDuration WRITE setDuration) + Q_PROPERTY(double duration READ getDuration) Q_PROPERTY(QString durationFormatted READ getDurationTextSeconds STORED false) Q_PROPERTY(QString durationFormattedCentiseconds READ getDurationTextCentiseconds STORED false) Q_PROPERTY(QString durationFormattedMilliseconds READ getDurationTextMilliseconds STORED false) @@ -99,13 +100,9 @@ class Track : public QObject { void setType(const QString&); QString getType() const; - // Set number of channels - void setChannels(int iChannels); // Get number of channels int getChannels() const; - // Set sample rate - void setSampleRate(int iSampleRate); // Get sample rate int getSampleRate() const; @@ -315,6 +312,12 @@ class Track : public QObject { void markForMetadataExport(); bool isMarkedForMetadataExport() const; + void setAudioProperties( + mixxx::audio::ChannelCount channelCount, + mixxx::audio::SampleRate sampleRate, + mixxx::audio::Bitrate bitrate, + mixxx::Duration duration); + signals: void waveformUpdated(); void waveformSummaryUpdated(); @@ -366,6 +369,13 @@ class Track : public QObject { ExportTrackMetadataResult exportMetadata( mixxx::MetadataSourcePointer pMetadataSource); + // Information about the actual properties of the + // audio stream is only available after opening it. + // On this occasion the audio properties of the track + // need to be updated to reflect these values. + void updateAudioPropertiesFromStream( + mixxx::audio::StreamInfo&& streamInfo); + // Mutex protecting access to object mutable QMutex m_qMutex; @@ -384,6 +394,11 @@ class Track : public QObject { // the metadata. bool m_bMarkedForMetadataExport; + // Reliable information about the PCM audio stream + // that only becomes available when opening the + // corresponding file. + std::optional<mixxx::audio::StreamInfo> m_streamInfo; + // The list of cue points for the track QList<CuePointer> m_cuePoints; diff --git a/src/track/trackinfo.cpp b/src/track/trackinfo.cpp index 4adcdee652..203bb1ce28 100644 --- a/src/track/trackinfo.cpp +++ b/src/track/trackinfo.cpp @@ -88,7 +88,7 @@ bool TrackInfo::compareEq( } QDebug operator<<(QDebug dbg, const TrackInfo& arg) { - dbg << '{'; + dbg << "TrackInfo{"; arg.dbgArtist(dbg); arg.dbgBpm(dbg); arg.dbgComment(dbg); diff --git a/src/track/trackinfo.h b/src/track/trackinfo.h index 367a4eb53d..9f784a181b 100644 --- a/src/track/trackinfo.h +++ b/src/track/trackinfo.h @@ -13,7 +13,7 @@ namespace mixxx { class TrackInfo final { - // Track properties (in alphabetical order) + // Properties in alphabetical order PROPERTY_SET_BYVAL_GET_BYREF(QString, artist, Artist) PROPERTY_SET_BYVAL_GET_BYREF(Bpm, bpm, Bpm) PROPERTY_SET_BYVAL_GET_BYREF(QString, comment, Comment) diff --git a/src/track/trackmetadata.cpp b/src/track/trackmetadata.cpp index b372430a18..8085e260b5 100644 --- a/src/track/trackmetadata.cpp +++ b/src/track/trackmetadata.cpp @@ -1,9 +1,81 @@ #include "track/trackmetadata.h" +#include "audio/streaminfo.h" +#include "util/logger.h" + namespace mixxx { +namespace { + +const Logger kLogger("TrackMetadata"); + +} // anonymous namespace + /*static*/ constexpr int TrackMetadata::kCalendarYearInvalid; +bool TrackMetadata::updateAudioPropertiesFromStream( + const audio::StreamInfo& streamInfo) { + bool changed = false; + const auto streamChannelCount = + streamInfo.getSignalInfo().getChannelCount(); + if (streamChannelCount.isValid() && + streamChannelCount != getChannelCount()) { + if (getChannelCount().isValid()) { + kLogger.debug() + << "Modifying channel count:" + << getChannelCount() + << "->" + << streamChannelCount; + } + setChannelCount(streamChannelCount); + changed = true; + } + const auto streamSampleRate = + streamInfo.getSignalInfo().getSampleRate(); + if (streamSampleRate.isValid() && + streamSampleRate != getSampleRate()) { + if (getSampleRate().isValid()) { + kLogger.debug() + << "Modifying sample rate:" + << getSampleRate() + << "->" + << streamSampleRate; + } + setSampleRate(streamSampleRate); + changed = true; + } + const auto streamBitrate = + streamInfo.getBitrate(); + if (streamBitrate.isValid() && + streamBitrate != getBitrate()) { + if (getBitrate().isValid()) { + kLogger.debug() + << "Modifying bitrate:" + << getBitrate() + << "->" + << streamBitrate; + } + setBitrate(streamBitrate); + changed = true; + } + const auto streamDuration = + streamInfo.getDuration(); + if (streamDuration > Duration::empty() && + streamDuration != getDuration()) { + if (getDuration() > Duration::empty()) { + kLogger.debug() + << "Modifying duration:" + << getDuration() + << "->" + << streamDuration; + } + setDuration(streamDuration); + changed = true; + } + return changed; +} + + int TrackMetadata::parseCalendarYear(QString year, bool* pValid) { const QDateTime dateTime(parseDateTime(year)); if (0 < dateTime.date().year()) { @@ -83,20 +155,20 @@ bool TrackMetadata::anyFileTagsModified( } bool operator==(const TrackMetadata& lhs, const TrackMetadata& rhs) { - return (lhs.getAlbumInfo() == rhs.getAlbumInfo()) && - (lhs.getTrackInfo() == rhs.getTrackInfo()) && - (lhs.getBitrate() == rhs.getBitrate()) && - (lhs.getChannels() == rhs.getChannels()) && - (lhs.getDuration() == rhs.getDuration()) && - (lhs.getSampleRate() == rhs.getSampleRate()); + return lhs.getAlbumInfo() == rhs.getAlbumInfo() && + lhs.getTrackInfo() == rhs.getTrackInfo() && + lhs.getChannelCount() == rhs.getChannelCount() && + lhs.getSampleRate() == rhs.getSampleRate() && + lhs.getBitrate() == rhs.getBitrate() && + lhs.getDuration() == rhs.getDuration(); } QDebug operator<<(QDebug dbg, const TrackMetadata& arg) { - dbg << '{'; + dbg << "TrackMetadata{"; arg.dbgTrackInfo(dbg); arg.dbgAlbumInfo(dbg); arg.dbgBitrate(dbg); - arg.dbgChannels(dbg); + arg.dbgChannelCount(dbg); arg.dbgDuration(dbg); arg.dbgSampleRate(dbg); dbg << '}'; diff --git a/src/track/trackmetadata.h b/src/track/trackmetadata.h index 9ca165c4be..07bdc30b22 100644 --- a/src/track/trackmetadata.h +++ b/src/track/trackmetadata.h @@ -2,21 +2,27 @@ #include <QDateTime> +#include "audio/types.h" #include "track/albuminfo.h" #include "track/trackinfo.h" - namespace mixxx { +namespace audio { + +class StreamInfo; + +} // namespace audio + class TrackMetadata final { // Audio properties - // - read-only - // - stored file tags - // - adjusted by audio decoder AFTER import from file tags - PROPERTY_SET_BYVAL_GET_BYREF(AudioSource::Bitrate, bitrate, Bitrate) - PROPERTY_SET_BYVAL_GET_BYREF(AudioSignal::ChannelCount, channels, Channels) - PROPERTY_SET_BYVAL_GET_BYREF(Duration, duration, Duration) - PROPERTY_SET_BYVAL_GET_BYREF(AudioSignal::SampleRate, sampleRate, SampleRate) + // - read-only + // - stored in file tags + // - adjusted when opening the audio stream (if available) + PROPERTY_SET_BYVAL_GET_BYREF(audio::ChannelCount, channels, ChannelCount) + PROPERTY_SET_BYVAL_GET_BYREF(audio::SampleRate, sampleRate, SampleRate) + PROPERTY_SET_BYVAL_GET_BYREF(audio::Bitrate, bitrate, Bitrate) + PROPERTY_SET_BYVAL_GET_BYREF(Duration, duration, Duration) // Track properties // - read-write @@ -24,7 +30,7 @@ class TrackMetadata final { PROPERTY_SET_BYVAL_GET_BYREF(AlbumInfo, albumInfo, AlbumInfo) PROPERTY_SET_BYVAL_GET_BYREF(TrackInfo, trackInfo, TrackInfo) -public: + public: TrackMetadata() = default; TrackMetadata(TrackMetadata&&) = default; TrackMetadata(const TrackMetadata&) = default; @@ -33,6 +39,9 @@ public: TrackMetadata& operator=(TrackMetadata&&) = default; TrackMetadata& operator=(const TrackMetadata&) = default; + bool updateAudioPropertiesFromStream( + const audio::StreamInfo& streamInfo); + // Adjusts floating-point values to match their string representation // in file tags to account for rounding errors. void normalizeBeforeExport(); @@ -72,8 +81,7 @@ public: bool operator==(const TrackMetadata& lhs, const TrackMetadata& rhs); -inline -bool operator!=(const TrackMetadata& lhs, const TrackMetadata& rhs) { +inline bool operator!=(const TrackMetadata& lhs, const TrackMetadata& rhs) { return !(lhs == rhs); } diff --git a/src/track/trackmetadatataglib.cpp b/src/track/trackmetadatataglib.cpp index d70673fee2..d0c217cf47 100644 --- a/src/track/trackmetadatataglib.cpp +++ b/src/track/trackmetadatataglib.cpp @@ -2,8 +2,8 @@ #include <taglib/tpropertymap.h> +#include "audio/streaminfo.h" #include "track/tracknumbers.h" - #include "util/assert.h" #include "util/compatibility.h" #include "util/duration.h" @@ -484,9 +484,12 @@ void readAudioProperties( // 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(AudioSignal::ChannelCount(audioProperties.channels())); - pTrackMetadata->setSampleRate(AudioSignal::SampleRate(audioProperties.sampleRate())); - pTrackMetadata->setBitrate(AudioSource::Bitrate(audioProperties.bitrate())); + pTrackMetadata->setChannelCount( + audio::ChannelCount(audioProperties.channels())); + pTrackMetadata->setSampleRate( + audio::SampleRate(audioProperties.sampleRate())); + pTrackMetadata->setBitrate( + audio::Bitrate(audioProperties.bitrate())); #if (TAGLIB_HAS_LENGTH_IN_MILLISECONDS) const auto duration = Duration::fromMillis(audioProperties.lengthInMilliseconds()); #else |