![]() |
Mixxx
|
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 }