diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d81e2ff824d..9a2a3a912847 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1388,6 +1388,9 @@ add_library( src/library/autodj/autodjprocessor.cpp src/library/autodj/dlgautodj.cpp src/library/autodj/dlgautodj.ui + src/library/autodj/track/deckattributes.cpp + src/library/autodj/track/trackattributes.cpp + src/library/autodj/track/trackordeckattributes.cpp src/library/banshee/bansheedbconnection.cpp src/library/banshee/bansheefeature.cpp src/library/banshee/bansheeplaylistmodel.cpp diff --git a/src/library/autodj/autodjprocessor.cpp b/src/library/autodj/autodjprocessor.cpp index 466e75ab1f4d..c0b8f6b44ef9 100644 --- a/src/library/autodj/autodjprocessor.cpp +++ b/src/library/autodj/autodjprocessor.cpp @@ -13,7 +13,7 @@ const QString kControlGroup = QStringLiteral("[AutoDJ]"); const char* kTransitionPreferenceName = "Transition"; const char* kTransitionModePreferenceName = "TransitionMode"; constexpr double kTransitionPreferenceDefault = 10.0; -constexpr double kKeepPosition = -1.0; +constexpr double kKeepPosition = TrackOrDeckAttributes::kKeepPosition; // A track needs to be longer than two callbacks to not stop AutoDJ constexpr double kMinimumTrackDurationSec = 0.2; @@ -21,97 +21,6 @@ constexpr double kMinimumTrackDurationSec = 0.2; constexpr bool sDebug = false; } // anonymous namespace -DeckAttributes::DeckAttributes(int index, - BaseTrackPlayer* pPlayer) - : index(index), - group(pPlayer->getGroup()), - startPos(kKeepPosition), - fadeBeginPos(1.0), - fadeEndPos(1.0), - isFromDeck(false), - loading(false), - m_orientation(group, "orientation"), - m_playPos(group, "playposition"), - m_play(group, "play"), - m_repeat(group, "repeat"), - m_introStartPos(group, "intro_start_position"), - m_introEndPos(group, "intro_end_position"), - m_outroStartPos(group, "outro_start_position"), - m_outroEndPos(group, "outro_end_position"), - m_trackSamples(group, "track_samples"), - m_sampleRate(group, "track_samplerate"), - m_rateRatio(group, "rate_ratio"), - m_pPlayer(pPlayer) { - connect(m_pPlayer, &BaseTrackPlayer::newTrackLoaded, - this, &DeckAttributes::slotTrackLoaded); - connect(m_pPlayer, &BaseTrackPlayer::loadingTrack, - this, &DeckAttributes::slotLoadingTrack); - connect(m_pPlayer, &BaseTrackPlayer::playerEmpty, - this, &DeckAttributes::slotPlayerEmpty); - m_playPos.connectValueChanged(this, &DeckAttributes::slotPlayPosChanged); - m_play.connectValueChanged(this, &DeckAttributes::slotPlayChanged); - m_introStartPos.connectValueChanged(this, &DeckAttributes::slotIntroStartPositionChanged); - m_introEndPos.connectValueChanged(this, &DeckAttributes::slotIntroEndPositionChanged); - m_outroStartPos.connectValueChanged(this, &DeckAttributes::slotOutroStartPositionChanged); - m_outroEndPos.connectValueChanged(this, &DeckAttributes::slotOutroEndPositionChanged); - m_rateRatio.connectValueChanged(this, &DeckAttributes::slotRateChanged); - m_orientation.connectValueChanged(this, &DeckAttributes::slotOrientationChanged); -} - -DeckAttributes::~DeckAttributes() { -} - -void DeckAttributes::slotPlayChanged(double v) { - emit playChanged(this, v > 0.0); -} - -void DeckAttributes::slotPlayPosChanged(double v) { - emit playPositionChanged(this, v); -} - -void DeckAttributes::slotIntroStartPositionChanged(double v) { - emit introStartPositionChanged(this, v); -} - -void DeckAttributes::slotIntroEndPositionChanged(double v) { - emit introEndPositionChanged(this, v); -} - -void DeckAttributes::slotOutroStartPositionChanged(double v) { - emit outroStartPositionChanged(this, v); -} - -void DeckAttributes::slotOutroEndPositionChanged(double v) { - emit outroEndPositionChanged(this, v); -} - -void DeckAttributes::slotTrackLoaded(TrackPointer pTrack) { - emit trackLoaded(this, pTrack); -} - -void DeckAttributes::slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack) { - //qDebug() << "DeckAttributes::slotLoadingTrack"; - emit loadingTrack(this, pNewTrack, pOldTrack); -} - -void DeckAttributes::slotPlayerEmpty() { - emit playerEmpty(this); -} - -void DeckAttributes::slotRateChanged(double v) { - Q_UNUSED(v); - emit rateChanged(this); -} - -void DeckAttributes::slotOrientationChanged(double v) { - Q_UNUSED(v); - emit orientationChanged(this); -} - -TrackPointer DeckAttributes::getLoadedTrack() const { - return m_pPlayer != nullptr ? m_pPlayer->getLoadedTrack() : TrackPointer(); -} - AutoDJProcessor::AutoDJProcessor( QObject* pParent, UserSettingsPointer pConfig, diff --git a/src/library/autodj/autodjprocessor.h b/src/library/autodj/autodjprocessor.h index 61e7dc9acc50..a1cd35865460 100644 --- a/src/library/autodj/autodjprocessor.h +++ b/src/library/autodj/autodjprocessor.h @@ -9,7 +9,9 @@ #include "control/controlproxy.h" #include "control/controlpushbutton.h" #include "control/pollingcontrolproxy.h" -#include "engine/channels/enginechannel.h" +#include "library/autodj/track/deckattributes.h" +#include "library/autodj/track/trackattributes.h" +#include "library/autodj/track/trackordeckattributes.h" #include "library/playlisttablemodel.h" #include "preferences/usersettings.h" #include "track/track_decl.h" @@ -18,132 +20,8 @@ class TrackCollectionManager; class PlayerManagerInterface; -class BaseTrackPlayer; typedef QList QModelIndexList; -class DeckAttributes : public QObject { - Q_OBJECT - public: - DeckAttributes(int index, - BaseTrackPlayer* pPlayer); - virtual ~DeckAttributes(); - - bool isLeft() const { - return m_orientation.get() == static_cast(EngineChannel::LEFT); - } - - bool isRight() const { - return m_orientation.get() == static_cast(EngineChannel::RIGHT); - } - - bool isPlaying() const { - return m_play.toBool(); - } - - void stop() { - m_play.set(0.0); - } - - void play() { - m_play.set(1.0); - } - - double playPosition() const { - return m_playPos.get(); - } - - void setPlayPosition(double playpos) { - m_playPos.set(playpos); - } - - bool isRepeat() const { - return m_repeat.toBool(); - } - - void setRepeat(bool enabled) { - m_repeat.set(enabled ? 1.0 : 0.0); - } - - mixxx::audio::FramePos introStartPosition() const { - return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_introStartPos.get()); - } - - mixxx::audio::FramePos introEndPosition() const { - return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_introEndPos.get()); - } - - mixxx::audio::FramePos outroStartPosition() const { - return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_outroStartPos.get()); - } - - mixxx::audio::FramePos outroEndPosition() const { - return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_outroEndPos.get()); - } - - mixxx::audio::SampleRate sampleRate() const { - return mixxx::audio::SampleRate::fromDouble(m_sampleRate.get()); - } - - mixxx::audio::FramePos trackEndPosition() const { - return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_trackSamples.get()); - } - - double rateRatio() const { - return m_rateRatio.get(); - } - - TrackPointer getLoadedTrack() const; - - signals: - void playChanged(DeckAttributes* pDeck, bool playing); - void playPositionChanged(DeckAttributes* pDeck, double playPosition); - void introStartPositionChanged(DeckAttributes* pDeck, double introStartPosition); - void introEndPositionChanged(DeckAttributes* pDeck, double introEndPosition); - void outroStartPositionChanged(DeckAttributes* pDeck, double outtroStartPosition); - void outroEndPositionChanged(DeckAttributes* pDeck, double outroEndPosition); - void trackLoaded(DeckAttributes* pDeck, TrackPointer pTrack); - void loadingTrack(DeckAttributes* pDeck, TrackPointer pNewTrack, TrackPointer pOldTrack); - void playerEmpty(DeckAttributes* pDeck); - void rateChanged(DeckAttributes* pDeck); - void orientationChanged(DeckAttributes* pDeck); - - private slots: - void slotPlayPosChanged(double v); - void slotPlayChanged(double v); - void slotIntroStartPositionChanged(double v); - void slotIntroEndPositionChanged(double v); - void slotOutroStartPositionChanged(double v); - void slotOutroEndPositionChanged(double v); - void slotTrackLoaded(TrackPointer pTrack); - void slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack); - void slotPlayerEmpty(); - void slotRateChanged(double v); - void slotOrientationChanged(double v); - - public: - int index; - QString group; - double startPos; // Set in toDeck nature - double fadeBeginPos; // set in fromDeck nature - double fadeEndPos; // set in fromDeck nature - bool isFromDeck; - bool loading; // The data is inconsistent during loading a deck - - private: - ControlProxy m_orientation; - ControlProxy m_playPos; - ControlProxy m_play; - ControlProxy m_repeat; - ControlProxy m_introStartPos; - ControlProxy m_introEndPos; - ControlProxy m_outroStartPos; - ControlProxy m_outroEndPos; - ControlProxy m_trackSamples; - ControlProxy m_sampleRate; - ControlProxy m_rateRatio; - BaseTrackPlayer* m_pPlayer; -}; - class AutoDJProcessor : public QObject { Q_OBJECT public: diff --git a/src/library/autodj/track/deckattributes.cpp b/src/library/autodj/track/deckattributes.cpp new file mode 100644 index 000000000000..c9819182c862 --- /dev/null +++ b/src/library/autodj/track/deckattributes.cpp @@ -0,0 +1,88 @@ +#include "library/autodj/track/deckattributes.h" + +#include "mixer/basetrackplayer.h" +#include "moc_deckattributes.cpp" + +DeckAttributes::DeckAttributes(int index, + BaseTrackPlayer* pPlayer) + : index(index), + group(pPlayer->getGroup()), + loading(false), + m_orientation(group, "orientation"), + m_playPos(group, "playposition"), + m_play(group, "play"), + m_repeat(group, "repeat"), + m_introStartPos(group, "intro_start_position"), + m_introEndPos(group, "intro_end_position"), + m_outroStartPos(group, "outro_start_position"), + m_outroEndPos(group, "outro_end_position"), + m_trackSamples(group, "track_samples"), + m_sampleRate(group, "track_samplerate"), + m_rateRatio(group, "rate_ratio"), + m_pPlayer(pPlayer) { + connect(m_pPlayer, &BaseTrackPlayer::newTrackLoaded, this, &DeckAttributes::slotTrackLoaded); + connect(m_pPlayer, &BaseTrackPlayer::loadingTrack, this, &DeckAttributes::slotLoadingTrack); + connect(m_pPlayer, &BaseTrackPlayer::playerEmpty, this, &DeckAttributes::slotPlayerEmpty); + m_playPos.connectValueChanged(this, &DeckAttributes::slotPlayPosChanged); + m_play.connectValueChanged(this, &DeckAttributes::slotPlayChanged); + m_introStartPos.connectValueChanged(this, &DeckAttributes::slotIntroStartPositionChanged); + m_introEndPos.connectValueChanged(this, &DeckAttributes::slotIntroEndPositionChanged); + m_outroStartPos.connectValueChanged(this, &DeckAttributes::slotOutroStartPositionChanged); + m_outroEndPos.connectValueChanged(this, &DeckAttributes::slotOutroEndPositionChanged); + m_rateRatio.connectValueChanged(this, &DeckAttributes::slotRateChanged); + m_orientation.connectValueChanged(this, &DeckAttributes::slotOrientationChanged); +} + +DeckAttributes::~DeckAttributes() { +} + +void DeckAttributes::slotPlayChanged(double v) { + emit playChanged(this, v > 0.0); +} + +void DeckAttributes::slotPlayPosChanged(double v) { + emit playPositionChanged(this, v); +} + +void DeckAttributes::slotIntroStartPositionChanged(double v) { + emit introStartPositionChanged(this, v); +} + +void DeckAttributes::slotIntroEndPositionChanged(double v) { + emit introEndPositionChanged(this, v); +} + +void DeckAttributes::slotOutroStartPositionChanged(double v) { + emit outroStartPositionChanged(this, v); +} + +void DeckAttributes::slotOutroEndPositionChanged(double v) { + emit outroEndPositionChanged(this, v); +} + +void DeckAttributes::slotTrackLoaded(TrackPointer pTrack) { + emit trackLoaded(this, pTrack); +} + +void DeckAttributes::slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack) { + // qDebug() << "DeckAttributes::slotLoadingTrack"; + emit loadingTrack(this, pNewTrack, pOldTrack); +} + +void DeckAttributes::slotPlayerEmpty() { + emit playerEmpty(this); +} + +void DeckAttributes::slotRateChanged(double v) { + Q_UNUSED(v); + emit rateChanged(this); +} + +void DeckAttributes::slotOrientationChanged(double v) { + Q_UNUSED(v); + emit orientationChanged(this); +} + +TrackPointer DeckAttributes::getLoadedTrack() const { + return m_pPlayer != nullptr ? m_pPlayer->getLoadedTrack() : TrackPointer(); +} diff --git a/src/library/autodj/track/deckattributes.h b/src/library/autodj/track/deckattributes.h new file mode 100644 index 000000000000..5dbd7be1f2dd --- /dev/null +++ b/src/library/autodj/track/deckattributes.h @@ -0,0 +1,126 @@ +#pragma once + +#include "control/controlproxy.h" +#include "engine/channels/enginechannel.h" +#include "library/autodj/track/trackordeckattributes.h" + +class BaseTrackPlayer; + +/// Exposes the attributes of the track loaded in a certain player deck. +class DeckAttributes : public TrackOrDeckAttributes { + Q_OBJECT + public: + DeckAttributes(int index, BaseTrackPlayer* pPlayer); + virtual ~DeckAttributes(); + + bool isLeft() const { + return m_orientation.get() == static_cast(EngineChannel::LEFT); + } + + bool isRight() const { + return m_orientation.get() == static_cast(EngineChannel::RIGHT); + } + + bool isPlaying() const { + return m_play.toBool(); + } + + void stop() { + m_play.set(0.0); + } + + void play() { + m_play.set(1.0); + } + + double playPosition() const override { + return m_playPos.get(); + } + + void setPlayPosition(double playpos) { + m_playPos.set(playpos); + } + + bool isRepeat() const { + return m_repeat.toBool(); + } + + void setRepeat(bool enabled) { + m_repeat.set(enabled ? 1.0 : 0.0); + } + + mixxx::audio::FramePos introStartPosition() const override { + return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_introStartPos.get()); + } + + mixxx::audio::FramePos introEndPosition() const override { + return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_introEndPos.get()); + } + + mixxx::audio::FramePos outroStartPosition() const override { + return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_outroStartPos.get()); + } + + mixxx::audio::FramePos outroEndPosition() const override { + return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_outroEndPos.get()); + } + + mixxx::audio::SampleRate sampleRate() const override { + return mixxx::audio::SampleRate::fromDouble(m_sampleRate.get()); + } + + mixxx::audio::FramePos trackEndPosition() const override { + return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_trackSamples.get()); + } + + double rateRatio() const override { + return m_rateRatio.get(); + } + + TrackPointer getLoadedTrack() const override; + + signals: + void playChanged(DeckAttributes* pDeck, bool playing); + void playPositionChanged(DeckAttributes* pDeck, double playPosition); + void introStartPositionChanged(DeckAttributes* pDeck, double introStartPosition); + void introEndPositionChanged(DeckAttributes* pDeck, double introEndPosition); + void outroStartPositionChanged(DeckAttributes* pDeck, double outtroStartPosition); + void outroEndPositionChanged(DeckAttributes* pDeck, double outroEndPosition); + void trackLoaded(DeckAttributes* pDeck, TrackPointer pTrack); + void loadingTrack(DeckAttributes* pDeck, TrackPointer pNewTrack, TrackPointer pOldTrack); + void playerEmpty(DeckAttributes* pDeck); + void rateChanged(DeckAttributes* pDeck); + void orientationChanged(DeckAttributes* pDeck); + + private slots: + void slotPlayPosChanged(double v); + void slotPlayChanged(double v); + void slotIntroStartPositionChanged(double v); + void slotIntroEndPositionChanged(double v); + void slotOutroStartPositionChanged(double v); + void slotOutroEndPositionChanged(double v); + void slotTrackLoaded(TrackPointer pTrack); + void slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack); + void slotPlayerEmpty(); + void slotRateChanged(double v); + void slotOrientationChanged(double v); + + public: + int index; + QString group; + bool loading; // The data is inconsistent during loading a deck + + private: + ControlProxy m_orientation; + ControlProxy m_playPos; + ControlProxy m_play; + ControlProxy m_repeat; + ControlProxy m_introStartPos; + ControlProxy m_introEndPos; + ControlProxy m_outroStartPos; + ControlProxy m_outroEndPos; + ControlProxy m_trackSamples; + ControlProxy m_sampleRate; + ControlProxy m_rateRatio; + BaseTrackPlayer* m_pPlayer; +}; diff --git a/src/library/autodj/track/trackattributes.cpp b/src/library/autodj/track/trackattributes.cpp new file mode 100644 index 000000000000..bd579eb3af33 --- /dev/null +++ b/src/library/autodj/track/trackattributes.cpp @@ -0,0 +1,64 @@ +#include "library/autodj/track/trackattributes.h" + +#include "moc_trackattributes.cpp" +#include "track/track.h" + +TrackAttributes::TrackAttributes(TrackPointer pTrack) + : m_pTrack(pTrack) { +} + +mixxx::audio::FramePos TrackAttributes::introStartPosition() const { + auto pIntro = m_pTrack->findCueByType(mixxx::CueType::Intro); + if (pIntro) { + return pIntro->getPosition(); + } + return mixxx::audio::FramePos(); +} + +mixxx::audio::FramePos TrackAttributes::introEndPosition() const { + auto pIntro = m_pTrack->findCueByType(mixxx::CueType::Intro); + if (pIntro) { + return pIntro->getEndPosition(); + } + return mixxx::audio::FramePos(); +} + +mixxx::audio::FramePos TrackAttributes::outroStartPosition() const { + auto pOutro = m_pTrack->findCueByType(mixxx::CueType::Outro); + if (pOutro) { + return pOutro->getPosition(); + } + return mixxx::audio::FramePos(); +} + +mixxx::audio::FramePos TrackAttributes::outroEndPosition() const { + auto pOutro = m_pTrack->findCueByType(mixxx::CueType::Outro); + if (pOutro) { + return pOutro->getEndPosition(); + } + return mixxx::audio::FramePos(); +} + +mixxx::audio::SampleRate TrackAttributes::sampleRate() const { + return m_pTrack->getSampleRate(); +} + +double TrackAttributes::playPosition() const { + return 0.0; +} + +double TrackAttributes::rateRatio() const { + return 1.0; +} + +mixxx::audio::FramePos TrackAttributes::trackEndPosition() const { + // Instead of actually loading the file, we simply infer + // the number of frames from duration and sample rate stored + // in the database. This isn't entirely accurate due to + // rounding errors, but more than accurate enough for + // estimating the remaining play time of the AutoDJ queue. + double approxNumSamples = + m_pTrack->getSampleRate() * m_pTrack->getDuration(); + + return mixxx::audio::FramePos(approxNumSamples); +} diff --git a/src/library/autodj/track/trackattributes.h b/src/library/autodj/track/trackattributes.h new file mode 100644 index 000000000000..f320818d363f --- /dev/null +++ b/src/library/autodj/track/trackattributes.h @@ -0,0 +1,26 @@ +#pragma once + +#include "library/autodj/track/trackordeckattributes.h" + +/// Exposes the attributes of a track from the Auto DJ queue. +class TrackAttributes : public TrackOrDeckAttributes { + Q_OBJECT + public: + TrackAttributes(TrackPointer pTrack); + + virtual mixxx::audio::FramePos introStartPosition() const override; + virtual mixxx::audio::FramePos introEndPosition() const override; + virtual mixxx::audio::FramePos outroStartPosition() const override; + virtual mixxx::audio::FramePos outroEndPosition() const override; + virtual mixxx::audio::SampleRate sampleRate() const override; + virtual mixxx::audio::FramePos trackEndPosition() const override; + virtual double playPosition() const override; + virtual double rateRatio() const override; + + TrackPointer getLoadedTrack() const override { + return m_pTrack; + } + + private: + TrackPointer m_pTrack; +}; diff --git a/src/library/autodj/track/trackordeckattributes.cpp b/src/library/autodj/track/trackordeckattributes.cpp new file mode 100644 index 000000000000..d3c797890fed --- /dev/null +++ b/src/library/autodj/track/trackordeckattributes.cpp @@ -0,0 +1,13 @@ +#include "library/autodj/track/trackordeckattributes.h" + +#include "moc_trackordeckattributes.cpp" + +TrackOrDeckAttributes::TrackOrDeckAttributes() + : startPos(TrackOrDeckAttributes::kKeepPosition), + fadeBeginPos(1.0), + fadeEndPos(1.0), + isFromDeck(false) { +} + +TrackOrDeckAttributes::~TrackOrDeckAttributes() { +} diff --git a/src/library/autodj/track/trackordeckattributes.h b/src/library/autodj/track/trackordeckattributes.h new file mode 100644 index 000000000000..2c8b3823835a --- /dev/null +++ b/src/library/autodj/track/trackordeckattributes.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "audio/frame.h" +#include "audio/types.h" +#include "track/track_decl.h" + +/// Represents either a track in the Auto DJ queue, +/// or a player deck controlled by the Auto DJ processor. +class TrackOrDeckAttributes : public QObject { + Q_OBJECT + public: + /// Special value for startPos that indicates the deck + /// should start playing from its current position. + static constexpr double kKeepPosition = -1.0; + + /// Special value for startPos that indicates a track + /// should be skipped because it is too short. + static constexpr double kSkipToNextTrack = -2.0; + + TrackOrDeckAttributes(); + virtual ~TrackOrDeckAttributes(); + + virtual mixxx::audio::FramePos introStartPosition() const = 0; + virtual mixxx::audio::FramePos introEndPosition() const = 0; + virtual mixxx::audio::FramePos outroStartPosition() const = 0; + virtual mixxx::audio::FramePos outroEndPosition() const = 0; + virtual mixxx::audio::SampleRate sampleRate() const = 0; + virtual mixxx::audio::FramePos trackEndPosition() const = 0; + virtual double playPosition() const = 0; + virtual double rateRatio() const = 0; + + virtual TrackPointer getLoadedTrack() const = 0; + + inline bool isEmpty() const { + return !getLoadedTrack(); + } + + double startPos; // Set for toDeck + double fadeBeginPos; // Set for fromDeck + double fadeEndPos; // Set for fromDeck + bool isFromDeck; +};