Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/basetrackplayer.cpp

Go to the documentation of this file.
00001 #include <QtCore>
00002 #include <QMessageBox>
00003 
00004 #include "basetrackplayer.h"
00005 #include "playerinfo.h"
00006 
00007 #include "controlobjectthreadmain.h"
00008 #include "controlobject.h"
00009 #include "trackinfoobject.h"
00010 #include "engine/enginebuffer.h"
00011 #include "engine/enginedeck.h"
00012 #include "engine/enginemaster.h"
00013 #include "soundsourceproxy.h"
00014 #include "engine/cuecontrol.h"
00015 #include "engine/clockcontrol.h"
00016 #include "mathstuff.h"
00017 #include "waveform/waveformrenderer.h"
00018 #include "track/beatgrid.h"
00019 #include "track/beatfactory.h"
00020 
00021 BaseTrackPlayer::BaseTrackPlayer(QObject* pParent,
00022                                  ConfigObject<ConfigValue> *pConfig,
00023                                  EngineMaster* pMixingEngine,
00024                                  EngineChannel::ChannelOrientation defaultOrientation,
00025                                  AnalyserQueue* pAnalyserQueue,
00026                                  QString group)
00027         : BasePlayer(pParent, group),
00028           m_pAnalyserQueue(pAnalyserQueue),
00029           m_pConfig(pConfig),
00030           m_pLoadedTrack() {
00031 
00032     // Need to strdup the string because EngineChannel will save the pointer,
00033     // but we might get deleted before the EngineChannel. TODO(XXX)
00034     // pSafeGroupName is leaked. It's like 5 bytes so whatever.
00035     const char* pSafeGroupName = strdup(getGroup().toAscii().constData());
00036 
00037     EngineDeck* pChannel = new EngineDeck(pSafeGroupName,
00038                                           pConfig, defaultOrientation);
00039     EngineBuffer* pEngineBuffer = pChannel->getEngineBuffer();
00040     pMixingEngine->addChannel(pChannel);
00041 
00042     ClockControl* pClockControl = new ClockControl(pSafeGroupName, pConfig);
00043     pEngineBuffer->addControl(pClockControl);
00044 
00045     CueControl* pCueControl = new CueControl(pSafeGroupName, pConfig);
00046     connect(this, SIGNAL(newTrackLoaded(TrackPointer)),
00047             pCueControl, SLOT(loadTrack(TrackPointer)));
00048     connect(this, SIGNAL(unloadingTrack(TrackPointer)),
00049             pCueControl, SLOT(unloadTrack(TrackPointer)));
00050     pEngineBuffer->addControl(pCueControl);
00051 
00052     // Connect our signals and slots with the EngineBuffer's signals and
00053     // slots. This will let us know when the reader is done loading a track, and
00054     // let us request that the reader load a track.
00055     connect(this, SIGNAL(loadTrack(TrackPointer)),
00056             pEngineBuffer, SLOT(slotLoadTrack(TrackPointer)));
00057     connect(pEngineBuffer, SIGNAL(trackLoaded(TrackPointer)),
00058             this, SLOT(slotFinishLoading(TrackPointer)));
00059     connect(pEngineBuffer, SIGNAL(trackLoadFailed(TrackPointer, QString)),
00060             this, SLOT(slotLoadFailed(TrackPointer, QString)));
00061     connect(pEngineBuffer, SIGNAL(trackUnloaded(TrackPointer)),
00062             this, SLOT(slotUnloadTrack(TrackPointer)));
00063 
00064     //Get cue point control object
00065     m_pCuePoint = new ControlObjectThreadMain(
00066         ControlObject::getControl(ConfigKey(getGroup(),"cue_point")));
00067     // Get loop point control objects
00068     m_pLoopInPoint = new ControlObjectThreadMain(
00069         ControlObject::getControl(ConfigKey(getGroup(),"loop_start_position")));
00070     m_pLoopOutPoint = new ControlObjectThreadMain(
00071         ControlObject::getControl(ConfigKey(getGroup(),"loop_end_position")));
00072     //Playback position within the currently loaded track (in this player).
00073     m_pPlayPosition = new ControlObjectThreadMain(
00074         ControlObject::getControl(ConfigKey(getGroup(), "playposition")));
00075 
00076     // Duration of the current song, we create this one because nothing else does.
00077     m_pDuration = new ControlObject(ConfigKey(getGroup(), "duration"));
00078 
00079     //BPM of the current song
00080     m_pBPM = new ControlObjectThreadMain(
00081         ControlObject::getControl(ConfigKey(getGroup(), "file_bpm")));
00082 
00083     m_pReplayGain = new ControlObjectThreadMain(
00084         ControlObject::getControl(ConfigKey(getGroup(), "replaygain")));
00085 
00086     // Create WaveformRenderer last, because it relies on controls created above
00087     // (e.g. EngineBuffer)
00088 
00089     m_pWaveformRenderer = new WaveformRenderer(pSafeGroupName);
00090     connect(this, SIGNAL(newTrackLoaded(TrackPointer)),
00091             m_pWaveformRenderer, SLOT(slotNewTrack(TrackPointer)));
00092     connect(this, SIGNAL(unloadingTrack(TrackPointer)),
00093             m_pWaveformRenderer, SLOT(slotUnloadTrack(TrackPointer)));
00094 }
00095 
00096 BaseTrackPlayer::~BaseTrackPlayer()
00097 {
00098     if (m_pLoadedTrack) {
00099         emit(unloadingTrack(m_pLoadedTrack));
00100         m_pLoadedTrack.clear();
00101     }
00102 
00103     delete m_pCuePoint;
00104     delete m_pLoopInPoint;
00105     delete m_pLoopOutPoint;
00106     delete m_pPlayPosition;
00107     delete m_pBPM;
00108     delete m_pReplayGain;
00109     delete m_pWaveformRenderer;
00110     delete m_pDuration;
00111 }
00112 
00113 void BaseTrackPlayer::slotLoadTrack(TrackPointer track, bool bStartFromEndPos)
00114 {
00115     //Disconnect the old track's signals.
00116     if (m_pLoadedTrack) {
00117         // Save the loops that are currently set in a loop cue. If no loop cue is
00118         // currently on the track, then create a new one.
00119         int loopStart = m_pLoopInPoint->get();
00120         int loopEnd = m_pLoopOutPoint->get();
00121         if (loopStart != -1 && loopEnd != -1 &&
00122             even(loopStart) && even(loopEnd) && loopStart <= loopEnd) {
00123             Cue* pLoopCue = NULL;
00124             QList<Cue*> cuePoints = m_pLoadedTrack->getCuePoints();
00125             QListIterator<Cue*> it(cuePoints);
00126             while (it.hasNext()) {
00127                 Cue* pCue = it.next();
00128                 if (pCue->getType() == Cue::LOOP) {
00129                     pLoopCue = pCue;
00130                 }
00131             }
00132             if (!pLoopCue) {
00133                 pLoopCue = m_pLoadedTrack->addCue();
00134                 pLoopCue->setType(Cue::LOOP);
00135             }
00136             pLoopCue->setPosition(loopStart);
00137             pLoopCue->setLength(loopEnd - loopStart);
00138         }
00139 
00140         // WARNING: Never. Ever. call bare disconnect() on an object. Mixxx
00141         // relies on signals and slots to get tons of things done. Don't
00142         // randomly disconnect things.
00143         // m_pLoadedTrack->disconnect();
00144         disconnect(m_pLoadedTrack.data(), 0, m_pBPM, 0);
00145         disconnect(m_pLoadedTrack.data(), 0, m_pReplayGain, 0);
00146 
00147         // Causes the track's data to be saved back to the library database.
00148         emit(unloadingTrack(m_pLoadedTrack));
00149     }
00150 
00151     m_pLoadedTrack = track;
00152 
00153     // Listen for updates to the file's BPM
00154     connect(m_pLoadedTrack.data(), SIGNAL(bpmUpdated(double)),
00155             m_pBPM, SLOT(slotSet(double)));
00156 
00157     // Listen for updates to the file's Replay Gain
00158     connect(m_pLoadedTrack.data(), SIGNAL(ReplayGainUpdated(double)),
00159             m_pReplayGain, SLOT(slotSet(double)));
00160 
00161     //Request a new track from the reader
00162     emit(loadTrack(track));
00163 }
00164 
00165 void BaseTrackPlayer::slotLoadFailed(TrackPointer track, QString reason) {
00166     qDebug() << "Failed to load track" << track->getLocation() << reason;
00167     // Alert user.
00168     QMessageBox::warning(NULL, tr("Couldn't load track."), reason);
00169 }
00170 
00171 void BaseTrackPlayer::slotUnloadTrack(TrackPointer) {
00172     if (m_pLoadedTrack) {
00173         // WARNING: Never. Ever. call bare disconnect() on an object. Mixxx
00174         // relies on signals and slots to get tons of things done. Don't
00175         // randomly disconnect things.
00176         // m_pLoadedTrack->disconnect();
00177         disconnect(m_pLoadedTrack.data(), 0, m_pBPM, 0);
00178         disconnect(m_pLoadedTrack.data(), 0, m_pReplayGain, 0);
00179 
00180         // Causes the track's data to be saved back to the library database and
00181         // for all the widgets to unload the track and blank themselves.
00182         emit(unloadingTrack(m_pLoadedTrack));
00183     }
00184     m_pDuration->set(0);
00185     m_pBPM->slotSet(0);
00186     m_pReplayGain->slotSet(0);
00187     m_pLoopInPoint->slotSet(-1);
00188     m_pLoopOutPoint->slotSet(-1);
00189     m_pLoadedTrack.clear();
00190 
00191     // Update the PlayerInfo class that is used in EngineShoutcast to replace
00192     // the metadata of a stream
00193     PlayerInfo::Instance().setTrackInfo(getGroup(), m_pLoadedTrack);
00194 }
00195 
00196 void BaseTrackPlayer::slotFinishLoading(TrackPointer pTrackInfoObject)
00197 {
00198     // Read the tags if required
00199     if(!m_pLoadedTrack->getHeaderParsed())
00200         SoundSourceProxy::ParseHeader(m_pLoadedTrack.data());
00201 
00202     m_pLoadedTrack->setPlayed(true);
00203 
00204     // Generate waveform summary
00205     //TODO: Consider reworking this visual resample stuff... need to ask rryan about this -- Albert.
00206     // TODO(rryan) : fix this crap -- the waveform renderers should be owned by
00207     // Player so they can just set this directly or something.
00208     ControlObjectThreadMain* pVisualResampleCO = new ControlObjectThreadMain(ControlObject::getControl(ConfigKey(getGroup(),"VisualResample")));
00209     m_pLoadedTrack->setVisualResampleRate(pVisualResampleCO->get());
00210     delete pVisualResampleCO;
00211 
00212     //Update the BPM and duration values that are stored in ControlObjects
00213     m_pDuration->set(m_pLoadedTrack->getDuration());
00214 
00215     float track_bpm = m_pLoadedTrack->getBpm();
00216     m_pBPM->slotSet(m_pLoadedTrack->getBpm());
00217 
00218     if (!m_pLoadedTrack->getBeats() && track_bpm > 0) {
00219         // This track has no beats object but has a non-zero BPM. Create a
00220         // fixed-size beatgrid for it.
00221         BeatsPointer pBeats = BeatFactory::makeBeatGrid(m_pLoadedTrack,
00222                                                         track_bpm, 0.0f);
00223         m_pLoadedTrack->setBeats(pBeats);
00224     }
00225 
00226     m_pReplayGain->slotSet(m_pLoadedTrack->getReplayGain());
00227 
00228     // Update the PlayerInfo class that is used in EngineShoutcast to replace
00229     // the metadata of a stream
00230     PlayerInfo::Instance().setTrackInfo(getGroup(), m_pLoadedTrack);
00231 
00232     // Reset the loop points.
00233     m_pLoopInPoint->slotSet(-1);
00234     m_pLoopOutPoint->slotSet(-1);
00235 
00236     const QList<Cue*> trackCues = pTrackInfoObject->getCuePoints();
00237     QListIterator<Cue*> it(trackCues);
00238     while (it.hasNext()) {
00239         Cue* pCue = it.next();
00240         if (pCue->getType() == Cue::LOOP) {
00241             int loopStart = pCue->getPosition();
00242             int loopEnd = loopStart + pCue->getLength();
00243             if (loopStart != -1 && loopEnd != -1 && even(loopStart) && even(loopEnd)) {
00244                 m_pLoopInPoint->slotSet(loopStart);
00245                 m_pLoopOutPoint->slotSet(loopEnd);
00246                 break;
00247             }
00248         }
00249     }
00250 
00251     emit(newTrackLoaded(m_pLoadedTrack));
00252 }
00253 
00254 AnalyserQueue* BaseTrackPlayer::getAnalyserQueue() const {
00255     return m_pAnalyserQueue;
00256 }
00257 
00258 WaveformRenderer* BaseTrackPlayer::getWaveformRenderer() const {
00259     return m_pWaveformRenderer;
00260 }
00261 
00262 TrackPointer BaseTrackPlayer::getLoadedTrack() const {
00263     return m_pLoadedTrack;
00264 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines