File indexing completed on 2024-05-05 05:40:23

0001 /***************************************************************************
0002  *  Copyright (C) 2021 by Renaud Guezennec                               *
0003  *   http://www.rolisteam.org/contact                                      *
0004  *                                                                         *
0005  *   This software is free software; you can redistribute it and/or modify *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  *   This program is distributed in the hope that it will be useful,       *
0011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0013  *   GNU General Public License for more details.                          *
0014  *                                                                         *
0015  *   You should have received a copy of the GNU General Public License     *
0016  *   along with this program; if not, write to the                         *
0017  *   Free Software Foundation, Inc.,                                       *
0018  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
0019  ***************************************************************************/
0020 #include "controller/audioplayercontroller.h"
0021 
0022 #include "preferences/preferencesmanager.h"
0023 #include "worker/iohelper.h"
0024 #include "worker/modelhelper.h"
0025 
0026 #include <chrono>
0027 #include <random>
0028 
0029 int randomSong(int current, int count)
0030 {
0031     static auto seed= std::chrono::high_resolution_clock::now().time_since_epoch().count();
0032     static std::mt19937 rng= std::mt19937(static_cast<unsigned long long>(seed));
0033     std::uniform_int_distribution<qint64> dist(0, count - 1);
0034 
0035     if(count < 0)
0036         return -1;
0037     if(count < 1)
0038         return 0;
0039     if(count < 2)
0040         return current == 1 ? 0 : 1;
0041 
0042     int res= current;
0043     while(res == current)
0044     {
0045         res= dist(rng);
0046     }
0047     return res;
0048 }
0049 
0050 AudioPlayerController::AudioPlayerController(int id, const QString& key, PreferencesManager* pref, QObject* parent)
0051     : QObject(parent), m_id(id), m_model(new MusicModel), m_pref(pref), m_prefkey(key)
0052 {
0053     m_player.setAudioOutput(&m_audioOutput);
0054 
0055     // m_player.setAudioRole(QAudio::MusicRole);
0056     connect(&m_player, &QMediaPlayer::errorOccurred, this,
0057             [this](QMediaPlayer::Error, const QString& errorStr) { emit errorChanged(errorStr); });
0058 
0059     connect(&m_player, &QMediaPlayer::playbackStateChanged, this, [this]() { emit stateChanged(state()); });
0060     connect(&m_audioOutput, &QAudioOutput::volumeChanged, this, [this]() { emit volumeChanged(volume()); });
0061     connect(&m_audioOutput, &QAudioOutput::mutedChanged, this, &AudioPlayerController::mutedChanged);
0062     connect(&m_player, &QMediaPlayer::durationChanged, this, &AudioPlayerController::durationChanged);
0063     //connect(&m_player, &QMediaPlayer::positionChanged, this, &AudioPlayerController::timeChanged);
0064 
0065     connect(&m_player, &QMediaPlayer::mediaStatusChanged, this,
0066             [this](QMediaPlayer::MediaStatus status)
0067             {
0068                 switch(status)
0069                 {
0070                 case QMediaPlayer::EndOfMedia:
0071                     next();
0072                     break;
0073                 default:
0074                     break;
0075                 }
0076             });
0077 }
0078 
0079 AudioPlayerController::~AudioPlayerController()= default;
0080 
0081 int AudioPlayerController::id() const
0082 {
0083     return m_id;
0084 }
0085 
0086 AudioPlayerController::PlayingMode AudioPlayerController::mode() const
0087 {
0088     return m_mode;
0089 }
0090 
0091 MusicModel* AudioPlayerController::model() const
0092 {
0093     return m_model.get();
0094 }
0095 
0096 QString AudioPlayerController::error() const
0097 {
0098     return m_player.errorString();
0099 }
0100 
0101 bool AudioPlayerController::visible() const
0102 {
0103     return m_visible;
0104 }
0105 
0106 bool AudioPlayerController::muted() const
0107 {
0108     return m_audioOutput.isMuted();
0109 }
0110 
0111 quint64 AudioPlayerController::time() const
0112 {
0113     return m_player.position();
0114 }
0115 
0116 AudioPlayerController::State AudioPlayerController::state() const
0117 {
0118     AudioPlayerController::State state= AudioPlayerController::StoppedState;
0119     switch(m_player.playbackState())
0120     {
0121     case QMediaPlayer::StoppedState:
0122         state= AudioPlayerController::StoppedState;
0123         break;
0124     case QMediaPlayer::PausedState:
0125         state= AudioPlayerController::PausedState;
0126         break;
0127     case QMediaPlayer::PlayingState:
0128         state= AudioPlayerController::PlayingState;
0129         break;
0130     }
0131 
0132     if(m_player.error() != QMediaPlayer::NoError)
0133         state= AudioPlayerController::ErrorState;
0134 
0135     return state;
0136 }
0137 
0138 QString AudioPlayerController::text() const
0139 {
0140     return m_text;
0141 }
0142 
0143 uint AudioPlayerController::volume() const
0144 {
0145     qreal linearVolume
0146         = QAudio::convertVolume(m_audioOutput.volume(), QAudio::LinearVolumeScale, QAudio::LogarithmicVolumeScale);
0147 
0148     return qRound(linearVolume * 100);
0149 }
0150 
0151 bool AudioPlayerController::localIsGm() const
0152 {
0153     return m_localIsGM;
0154 }
0155 
0156 void AudioPlayerController::setMedia(const QModelIndex& index)
0157 {
0158     m_text= m_model->data(index, MusicModel::TITLE).toString();
0159     m_player.setSource(m_model->data(index, MusicModel::URL).toUrl());
0160     m_model->setCurrentSong(index);
0161     emit textChanged();
0162 }
0163 
0164 void AudioPlayerController::play()
0165 {
0166     m_player.play();
0167     auto url= m_player.source();
0168     emit startPlayingSong(url.isLocalFile() ? url.fileName() : url.toString(), m_player.position());
0169 }
0170 
0171 void AudioPlayerController::stop()
0172 {
0173     m_player.stop();
0174 }
0175 
0176 void AudioPlayerController::pause()
0177 {
0178     m_player.pause();
0179 }
0180 
0181 void AudioPlayerController::next()
0182 {
0183     auto p= m_model->indexOfCurrent();
0184     auto start= true;
0185     switch(m_mode)
0186     {
0187     case LOOP:
0188         play();
0189         break;
0190     case UNIQUE:
0191         start= false;
0192         stop();
0193         break;
0194     case NEXT:
0195         setMedia(m_model->index(p + 1, 0));
0196         play();
0197         break;
0198     case SHUFFLE:
0199         setMedia(m_model->index(randomSong(p, m_model->rowCount()), 0));
0200         play();
0201         break;
0202     }
0203     if(start)
0204     {
0205         auto url= m_player.source();
0206         emit startPlayingSong(url.isLocalFile() ? url.fileName() : url.toString(), m_player.position());
0207     }
0208 }
0209 
0210 void AudioPlayerController::setPlayingMode(PlayingMode mode)
0211 {
0212     if(m_mode == mode)
0213         return;
0214     m_mode= mode;
0215     emit modeChanged();
0216 }
0217 
0218 void AudioPlayerController::loadPlayList(const QString& path)
0219 {
0220     auto urls= IOHelper::readM3uPlayList(path);
0221     m_model->addSong(urls);
0222 }
0223 
0224 void AudioPlayerController::setVolume(uint volume)
0225 {
0226     qreal linearVolume
0227         = QAudio::convertVolume(volume / qreal(100.0), QAudio::LogarithmicVolumeScale, QAudio::LinearVolumeScale);
0228 
0229     m_audioOutput.setVolume(linearVolume);
0230 }
0231 
0232 void AudioPlayerController::exportList(const QString& path)
0233 {
0234     IOHelper::writePlaylist(path, m_model->urls());
0235 }
0236 
0237 void AudioPlayerController::mute()
0238 {
0239     m_audioOutput.setMuted(!m_audioOutput.isMuted());
0240 }
0241 
0242 void AudioPlayerController::clear()
0243 {
0244     m_model->removeAll();
0245 }
0246 
0247 void AudioPlayerController::removeSong(const QModelIndexList& index)
0248 {
0249     m_model->removeSong(index);
0250 }
0251 
0252 void AudioPlayerController::addSong(const QList<QUrl>& path)
0253 {
0254     m_model->addSong(path);
0255 }
0256 
0257 void AudioPlayerController::setLocalIsGm(bool b)
0258 {
0259     if(b == m_localIsGM)
0260         return;
0261     m_localIsGM= b;
0262     emit localIsGmChanged();
0263 }
0264 
0265 void AudioPlayerController::setVisible(bool b)
0266 {
0267     if(m_visible == b)
0268         return;
0269     m_visible= b;
0270     emit visibleChanged(b);
0271 }
0272 
0273 void AudioPlayerController::setTime(quint64 time)
0274 {
0275     m_player.setPosition(static_cast<qint64>(time));
0276     emit timeChanged(time);
0277 }
0278 
0279 void AudioPlayerController::addMusicModel()
0280 {
0281     ModelHelper::fetchMusicModelWithTableTop(m_model.get());
0282 }
0283 
0284 QStringList AudioPlayerController::directoriesList() const
0285 {
0286     if(!m_pref)
0287         return {};
0288     return m_pref->value(m_prefkey, {}).toStringList();
0289 }
0290 
0291 void AudioPlayerController::nwNewSong(const QString& name, qint64 time)
0292 {
0293     if(localIsGm())
0294         return;
0295     QStringList list= directoriesList();
0296 
0297     auto url= IOHelper::findSong(name, list);
0298 
0299     m_text= url.fileName();
0300     m_player.setSource(url);
0301     m_player.setPosition(time);
0302     m_player.play();
0303     emit textChanged();
0304 }