void CueControl::hotcueSet(HotcueControl* pControl, double v) { //qDebug() << "CueControl::hotcueSet" << v; if (!v) return; QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; int hotcue = pControl->getHotcueNumber(); detachCue(hotcue); Cue* pCue = m_pLoadedTrack->addCue(); double cuePosition = (m_pQuantizeEnabled->get() > 0.0 && m_pClosestBeat->get() != -1) ? floorf(m_pClosestBeat->get()) : floorf(getCurrentSample()); if (!even(cuePosition)) cuePosition--; pCue->setPosition(cuePosition); pCue->setHotCue(hotcue); pCue->setLabel(""); pCue->setType(Cue::CUE); // TODO(XXX) deal with spurious signals attachCue(pCue, hotcue); // If quantize is enabled and we are not playing, jump to the cue point // since it's not necessarily where we currently are. TODO(XXX) is this // potentially invalid for vinyl control? bool playing = m_pPlayButton->get() > 0; if (!playing && m_pQuantizeEnabled->get() > 0.0) { lock.unlock(); // prevent deadlock. // Enginebuffer will quantize more exactly than we can. seekAbs(cuePosition); } }
void CueControl::trackUnloaded(TrackPointer pTrack) { QMutexLocker lock(&m_mutex); disconnect(pTrack.data(), 0, this, 0); for (int i = 0; i < m_iNumHotCues; ++i) { detachCue(i); } // Store the cue point in a load cue. double cuePoint = m_pCuePoint->get(); if (cuePoint != -1 && cuePoint != 0.0) { Cue* loadCue = NULL; const QList<Cue*>& cuePoints = pTrack->getCuePoints(); QListIterator<Cue*> it(cuePoints); while (it.hasNext()) { Cue* pCue = it.next(); if (pCue->getType() == Cue::LOAD) { loadCue = pCue; break; } } if (!loadCue) { loadCue = pTrack->addCue(); loadCue->setType(Cue::LOAD); loadCue->setLength(0); } loadCue->setPosition(cuePoint); } m_pCueIndicator->setBlinkValue(ControlIndicator::OFF); m_pCuePoint->set(-1.0); m_pLoadedTrack.clear(); }
void BaseTrackPlayer::slotLoadTrack(TrackPointer track, bool bPlay) { //Disconnect the old track's signals. if (m_pLoadedTrack) { // Save the loops that are currently set in a loop cue. If no loop cue is // currently on the track, then create a new one. int loopStart = m_pLoopInPoint->get(); int loopEnd = m_pLoopOutPoint->get(); if (loopStart != -1 && loopEnd != -1 && even(loopStart) && even(loopEnd) && loopStart <= loopEnd) { Cue* pLoopCue = NULL; QList<Cue*> cuePoints = m_pLoadedTrack->getCuePoints(); QListIterator<Cue*> it(cuePoints); while (it.hasNext()) { Cue* pCue = it.next(); if (pCue->getType() == Cue::LOOP) { pLoopCue = pCue; } } if (!pLoopCue) { pLoopCue = m_pLoadedTrack->addCue(); pLoopCue->setType(Cue::LOOP); } pLoopCue->setPosition(loopStart); pLoopCue->setLength(loopEnd - loopStart); } // WARNING: Never. Ever. call bare disconnect() on an object. Mixxx // relies on signals and slots to get tons of things done. Don't // randomly disconnect things. // m_pLoadedTrack->disconnect(); disconnect(m_pLoadedTrack.data(), 0, m_pBPM, 0); disconnect(m_pLoadedTrack.data(), 0, this, 0); m_pReplayGain->slotSet(0); // Causes the track's data to be saved back to the library database. emit(unloadingTrack(m_pLoadedTrack)); } m_pLoadedTrack = track; // Listen for updates to the file's BPM connect(m_pLoadedTrack.data(), SIGNAL(bpmUpdated(double)), m_pBPM, SLOT(slotSet(double))); // Listen for updates to the file's Replay Gain connect(m_pLoadedTrack.data(), SIGNAL(ReplayGainUpdated(double)), this, SLOT(slotSetReplayGain(double))); //Request a new track from the reader emit(loadTrack(track, bPlay)); }
void CueControl::hotcuePositionChanged(HotcueControl* pControl, double newPosition) { QMutexLocker lock(&m_mutex); if (!m_pLoadedTrack) return; Cue* pCue = pControl->getCue(); if (pCue) { // Setting the position to -1 is the same as calling hotcue_x_clear if (newPosition == -1) { pCue->setHotCue(-1); detachCue(pControl->getHotcueNumber()); } else if (newPosition > 0 && newPosition < m_pTrackSamples->get()) { int position = newPosition; // People writing from MIDI land, elsewhere might be careless. if (position % 2 != 0) { position--; } pCue->setPosition(position); } } }
/** Upgrade from <= 1.7 library to 1.8 DB format */ void LegacyLibraryImporter::import() { // TODO(XXX) SETTINGS_PATH may change in new Mixxx Versions. Here we need // the SETTINGS_PATH from Mixxx V <= 1.7 QString settingPath17 = QDir::homePath().append("/").append(SETTINGS_PATH); QString trackXML = settingPath17.append("mixxxtrack.xml"); QFile file(trackXML); QDomDocument doc("TrackList"); if(!file.open(QIODevice::ReadOnly)) { //qDebug() << "Could not import legacy 1.7 XML library: " << trackXML; return; } QString* errorMsg = NULL; int* errorLine = NULL; int* errorColumn = NULL; qDebug() << "Starting upgrade from 1.7 library..."; QHash<int, QString> playlistHashTable; //Maps track indices onto track locations QList<LegacyPlaylist> legacyPlaylists; // <= 1.7 playlists if (doc.setContent(&file, false, errorMsg, errorLine, errorColumn)) { QDomNodeList playlistList = doc.elementsByTagName("Playlist"); QDomNode playlist; for (int i = 0; i < playlistList.size(); i++) { LegacyPlaylist legPlaylist; playlist = playlistList.at(i); QString name = playlist.firstChildElement("Name").text(); legPlaylist.name = name; //Store the IDs in the hash table so we can map them to track locations later, //and also store them in-order in a temporary playlist struct. QDomElement listNode = playlist.firstChildElement("List").toElement(); QDomNodeList trackIDs = listNode.elementsByTagName("Id"); for (int j = 0; j < trackIDs.size(); j++) { int id = trackIDs.at(j).toElement().text().toInt(); if (!playlistHashTable.contains(id)) playlistHashTable.insert(id, ""); legPlaylist.indexes.push_back(id); //Save this track id. } //Save this playlist in our list. legacyPlaylists.push_back(legPlaylist); } QDomNodeList trackList = doc.elementsByTagName("Track"); QDomNode track; for (int i = 0; i < trackList.size(); i++) { //blah, can't figure out how to use an iterator with QDomNodeList track = trackList.at(i); TrackInfoObject trackInfo17(track); //Only add the track to the DB if the file exists on disk, //because Mixxx <= 1.7 had no logic to deal with detecting deleted //files. if (trackInfo17.exists()) { //Create a TrackInfoObject by directly parsing //the actual MP3/OGG/whatever because 1.7 didn't parse //genre and album tags (so the imported TIO doesn't have //those fields). emit(progress("Upgrading Mixxx 1.7 Library: " + trackInfo17.getTitle())); // Read the metadata we couldn't support in <1.8 from file. QFileInfo fileInfo(trackInfo17.getLocation()); //Ensure we have the absolute file path stored trackInfo17.setLocation(fileInfo.absoluteFilePath()); TrackInfoObject trackInfoNew(trackInfo17.getLocation()); trackInfo17.setGenre(trackInfoNew.getGenre()); trackInfo17.setAlbum(trackInfoNew.getAlbum()); trackInfo17.setYear(trackInfoNew.getYear()); trackInfo17.setType(trackInfoNew.getType()); trackInfo17.setTrackNumber(trackInfoNew.getTrackNumber()); trackInfo17.setKey(trackInfoNew.getKey()); trackInfo17.setHeaderParsed(true); // Import the track's saved cue point if it is non-zero. float fCuePoint = trackInfo17.getCuePoint(); if (fCuePoint != 0.0f) { Cue* pCue = trackInfo17.addCue(); pCue->setType(Cue::CUE); pCue->setPosition(fCuePoint); } // Provide a no-op deleter b/c this Track is on the stack. TrackPointer pTrack(&trackInfo17, &doNothing); m_trackDao.saveTrack(pTrack); //Check if this track is used in a playlist anywhere. If it is, save the //track location. (The "id" of a track in 1.8 is a database index, so it's totally //different. Using the track location is the best way for us to identify the song.) int id = trackInfo17.getId(); if (playlistHashTable.contains(id)) playlistHashTable[id] = trackInfo17.getLocation(); } } //Create the imported playlists QListIterator<LegacyPlaylist> it(legacyPlaylists); LegacyPlaylist current; while (it.hasNext()) { current = it.next(); emit(progress("Upgrading Mixxx 1.7 Playlists: " + current.name)); //Create the playlist with the imported name. //qDebug() << "Importing playlist:" << current.name; int playlistId = m_playlistDao.createPlaylist(current.name); //For each track ID in the XML... QList<int> trackIDs = current.indexes; for (int i = 0; i < trackIDs.size(); i++) { QString trackLocation; int id = trackIDs[i]; //qDebug() << "track ID:" << id; //Try to resolve the (XML's) track ID to a track location. if (playlistHashTable.contains(id)) { trackLocation = playlistHashTable[id]; //qDebug() << "Resolved to:" << trackLocation; } //Get the database's track ID (NOT the XML's track ID!) int dbTrackId = m_trackDao.getTrackId(trackLocation); if (dbTrackId >= 0) { // Add it to the database's playlist. // TODO(XXX): Care if the append succeeded. m_playlistDao.appendTrackToPlaylist(dbTrackId, playlistId); } } } QString upgrade_filename = settingPath17.append("DBUPGRADED"); //now create stub so that the library is not readded next time program loads QFile upgradefile(upgrade_filename); if (!upgradefile.open(QIODevice::WriteOnly | QIODevice::Text)) qDebug() << "Couldn't open" << upgrade_filename << "for writing"; else { file.write("",0); file.close(); } } else { qDebug() << errorMsg << " line: " << errorLine << " column: " << errorColumn; } file.close(); }
TEST(CueSettings,EmptyCue) { // Avoid using references to const values in EXPECT_EQ statements const Cue::Align defaultAlign = Cue::defaultAlign; const int defaultLine = Cue::defaultLine; const int defaultPosition = Cue::defaultPosition; const int defaultSize = Cue::defaultSize; const bool defaultSnapToLines = Cue::defaultSnapToLines; const Cue::Vertical defaultVertical = Cue::defaultVertical; // Trying to set empty cue settings should always fail Cue cue; // Id cue.setId("Phnglui mglw nafh Cthulhu R'lyeh wgah nagl fhtagn"); EXPECT_STREQ("", cue.id()); // Text cue.setText("Phnglui mglw nafh Cthulhu R'lyeh wgah nagl fhtagn"); EXPECT_STREQ("", cue.text()); // StartTime cue.setStartTime(68.067); EXPECT_EQ(MalformedTimestamp, cue.startTime()); // EndTime cue.setEndTime(98.678); EXPECT_EQ(MalformedTimestamp, cue.endTime()); // Align EXPECT_FALSE(cue.setAlign(Cue::Start)); EXPECT_FALSE(cue.setAlign(Cue::Middle)); EXPECT_FALSE(cue.setAlign(Cue::End)); EXPECT_FALSE(cue.setAlign(Cue::Left)); EXPECT_FALSE(cue.setAlign(Cue::Right)); EXPECT_FALSE(cue.setAlign("start")); EXPECT_FALSE(cue.setAlign("middle")); EXPECT_FALSE(cue.setAlign("end")); EXPECT_FALSE(cue.setAlign("left")); EXPECT_FALSE(cue.setAlign("right")); EXPECT_EQ(defaultAlign,cue.align()); // Line EXPECT_FALSE(cue.setLine(0, false)); EXPECT_FALSE(cue.setLine(0, false)); EXPECT_FALSE(cue.setLine(-101, true)); EXPECT_FALSE(cue.setLine(101, true)); EXPECT_FALSE(cue.setLine("0%")); EXPECT_FALSE(cue.setLine("100%")); EXPECT_FALSE(cue.setLine("-101")); EXPECT_FALSE(cue.setLine("1001")); EXPECT_EQ(defaultLine, cue.line()); EXPECT_EQ(defaultSnapToLines, cue.snapToLines()); // Position EXPECT_FALSE(cue.setPosition(0)); EXPECT_FALSE(cue.setPosition(100)); EXPECT_FALSE(cue.setPosition("0%")); EXPECT_FALSE(cue.setPosition("100%")); EXPECT_EQ(defaultPosition, cue.position()); // Size EXPECT_FALSE(cue.setSize(0)); EXPECT_FALSE(cue.setSize(100)); EXPECT_FALSE(cue.setSize("0%")); EXPECT_FALSE(cue.setSize("100%")); EXPECT_EQ(defaultSize, cue.size()); // Vertical EXPECT_FALSE(cue.setVertical(Cue::Horizontal)); EXPECT_FALSE(cue.setVertical(Cue::VerticalLeftToRight)); EXPECT_FALSE(cue.setVertical(Cue::VerticalRightToLeft)); EXPECT_FALSE(cue.setVertical("")); EXPECT_FALSE(cue.setVertical("lr")); EXPECT_FALSE(cue.setVertical("rl")); EXPECT_EQ(defaultVertical, cue.vertical()); }