File indexing completed on 2024-05-05 04:48:30

0001 /****************************************************************************************
0002  * Copyright (c) 2008 Daniel Caleb Jones <danielcjones@gmail.com>                       *
0003  * Copyright (c) 2010, 2013 Ralf Engels <ralf-engels@gmx.de>                            *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) version 3 or        *
0008  * any later version accepted by the membership of KDE e.V. (or its successor approved  *
0009  * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of  *
0010  * version 3 of the license.                                                            *
0011  *                                                                                      *
0012  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0013  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0014  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0015  *                                                                                      *
0016  * You should have received a copy of the GNU General Public License along with         *
0017  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0018  ****************************************************************************************/
0019 
0020 #ifndef AMAROK_BIASSOLVER_H
0021 #define AMAROK_BIASSOLVER_H
0022 
0023 #include "Bias.h"
0024 #include "core/meta/forward_declarations.h"
0025 #include "dynamic/TrackSet.h"
0026 
0027 #include <QDateTime>
0028 #include <QMutex>
0029 #include <QWaitCondition>
0030 
0031 #include <ThreadWeaver/Job>
0032 
0033 namespace Dynamic
0034 {
0035 
0036     /** A playlist helper class
0037     */
0038     class SolverList;
0039 
0040     /**
0041      * A class to implement the optimization algorithm used to generate
0042      * playlists from a set of biases. The method used here is slightly
0043      * complicated. It uses a a combination of heuristics, genetic algorithms,
0044      * and simulated annealing. The steps involved are documented in the source
0045      * code of BiasSolver::run.
0046 
0047        The whole operation is a little bit tricky since the bias solver runs as
0048        separate job without it's own event queue.
0049 
0050        The signals and slots are all handled via the UI threads event queue.
0051        Since the BiasSolver and all biases are created from the UI thread this
0052        is also the event queue of all objects.
0053        Bottom line: don't create objects in the bias solver that use signals.
0054 
0055 
0056      * Playlist generation is now done by adding tracks until we run out of time
0057      * or out of candidates.
0058      * We are back stepping a couple of times in such a case.
0059      *
0060      * The old bias solver that tried different optimization solutions is
0061      * not longer used. If you want to see the old code and/or re-use parts
0062      * of it see Amarok 2.7
0063      *
0064      * To use the solver:
0065      * Create an instance
0066      * enqueue the job. When the job is finished, call solution to get the
0067      * playlist produced.
0068      */
0069     class BiasSolver : public QObject, public ThreadWeaver::Job
0070     {
0071         Q_OBJECT
0072 
0073         public:
0074 
0075             /**
0076              * Create and prepare the solver. The constructor returns
0077              *
0078              * @param n The size of the playlist to generate.
0079              * @param bias The system of biases being applied.
0080              * @param context The tracks (if any) that precede the playlist
0081              * being generated.
0082              */
0083             BiasSolver( int n, const BiasPtr &bias, const Meta::TrackList &context );
0084 
0085             ~BiasSolver() override;
0086 
0087             /// Returns the playlist generated after the job has finished.
0088             Meta::TrackList solution();
0089 
0090             /// Politely asks the thread to give up and finish ASAP.
0091             void requestAbort() override;
0092 
0093             /**
0094              * Returns true if the solver was successful, false if it was
0095              * aborted or encountered some other error.
0096              */
0097             bool success() const  override;
0098 
0099             /**
0100              * Choose whether the BiasSolver instance should delete itself after the query.
0101              * By passing true the instance will delete itself after emitting done, failed.
0102              * Otherwise it is the responsibility of the owner to delete the instance
0103              * when it is not needed anymore.
0104              *
0105              * Defaults to false, i.e. the BiasSolver instance will not delete itself.
0106              */
0107             void setAutoDelete( bool autoDelete );
0108 
0109             /**
0110              * Return the universe set (a list of the uid of every track being
0111              * considered) being used.
0112              */
0113             static const QList<QByteArray>& universe();
0114 
0115             /**
0116              * Mark the universe set as out of date (i.e. it needs to be
0117              * updated).
0118              */
0119             static void outdateUniverse();
0120 
0121         protected:
0122             void run(ThreadWeaver::JobPointer self = QSharedPointer<ThreadWeaver::Job>(), ThreadWeaver::Thread *thread = nullptr)  override;
0123             void defaultBegin(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread)  override;
0124             void defaultEnd(const ThreadWeaver::JobPointer& job, ThreadWeaver::Thread *thread)  override;
0125 
0126         Q_SIGNALS:
0127             /** a job must implement the following signals for the progress bar
0128                 BiasedPlaylist set's us as progress sender in startSolver.
0129             */
0130 
0131             /** Sets the total steps for the progress bar (which is always 100 for the bias solver).
0132                 This signal is never emitted as the BiasedPlaylist already registers us with
0133                 100 steps. */
0134             void totalSteps( int );
0135             void incrementProgress();
0136             void endProgressOperation( QObject * );
0137 
0138             /** This signal is emitted when this job is being processed by a thread. */
0139             void started(ThreadWeaver::JobPointer);
0140             /** This signal is emitted when the job has been finished (no matter if it succeeded or not). */
0141             void done(ThreadWeaver::JobPointer);
0142             /** This job has failed.
0143              * This signal is emitted when success() returns false after the job is executed. */
0144             void failed(ThreadWeaver::JobPointer);
0145 
0146         private Q_SLOTS:
0147             void biasResultReady( const Dynamic::TrackSet &set );
0148 
0149             void trackCollectionResultsReady( const QStringList &);
0150             void trackCollectionDone();
0151 
0152 
0153         private:
0154             /** Returns the TrackSet that would match the end of the given playlist
0155                 The function blocks until the result is received */
0156             TrackSet matchingTracks( const Meta::TrackList& playlist ) const;
0157 
0158             /** Query for the universe set (the set of all tracks in the collection being considered.
0159                 This function needs to be called from a thread with an event loop.
0160             */
0161             void getTrackCollection();
0162 
0163             /** Try to recursive add tracks to the current solver list up to m_n tracks.
0164              *  The function will return with partly filled lists.
0165              */
0166             void addTracks( SolverList *list );
0167 
0168             /**
0169              * Get the track referenced by the uid stored in the given
0170              * QByteArray.
0171              * @param uid The uid
0172              */
0173             Meta::TrackPtr trackForUid( const QString& uid ) const;
0174 
0175             /**
0176              * Return a random track from the domain.
0177              */
0178             Meta::TrackPtr getMutation();
0179 
0180             /**
0181              * Return a random track from the given subset.
0182              * @param subset A list (representing a set) of uids stored in
0183              * QByteArrays.
0184              */
0185             Meta::TrackPtr getRandomTrack( const TrackSet& subset ) const;
0186 
0187             /** Returns a TrackSet with all duplicates removed (except the one at "position")
0188                 This helper function can be used in special cases if needed and
0189                 AmarokConfig::dynamicDuplicates() == false
0190                 Normally the BiasSolver will call it at for the top most bias.
0191             */
0192             static TrackSet withoutDuplicate( int position,
0193                                               const Meta::TrackList& playlist,
0194                                               const Dynamic::TrackSet& oldSet );
0195 
0196             /** Emits the required progress signals */
0197             void updateProgress( const SolverList* list );
0198 
0199             int m_n;                    //!< size of playlist to generate
0200             Dynamic::BiasPtr m_bias; // bias used to determine tracks. not owned by solver
0201             Meta::TrackList m_context;  //!< tracks that precede the playlist
0202 
0203             Meta::TrackList m_solution;
0204 
0205             bool m_abortRequested; //!< flag set when the thread is aborted
0206 
0207             QDateTime m_startTime;
0208 
0209             mutable QMutex m_biasResultsMutex;
0210             mutable QWaitCondition m_biasResultsReady;
0211             mutable Dynamic::TrackSet m_tracks; // tracks just received from the bias.
0212 
0213             mutable QMutex m_collectionResultsMutex;
0214             mutable QWaitCondition m_collectionResultsReady;
0215 
0216             /** All uids of all the tracks in the collection */
0217             QStringList m_collectionUids;
0218             TrackCollectionPtr m_trackCollection;
0219 
0220             bool m_allowDuplicates;
0221 
0222             int m_currentProgress;
0223 
0224             /** The maximum time we should try to spend generating the playlist */
0225             static const int MAX_TIME_MS = 5000;
0226     };
0227 }
0228 
0229 #endif