bool KNMusicCueListParser::writeDetail(const KNMusicAnalysisItem &analysisItem) { //Get the track index. const KNMusicDetailInfo &detailInfo=analysisItem.detailInfo; //Prepare the orignial file data and the temporary file data. QTemporaryFile updatedListFile; QFile listFile(detailInfo.trackFilePath); //Open the list file. if(!listFile.open(QIODevice::ReadOnly)) { return false; } //Open the list file. if(!updatedListFile.open()) { //Close the opened music file. listFile.close(); return false; } //Read the list file. //Initial the list file and temporary file stream as a text file. QTextStream trackStream(&listFile), temporaryStream(&updatedListFile); //Read until the track index comes to the track index. QString rawLine=trackStream.readLine(); //Check the raw line. while(!rawLine.isNull()) { //Simplified the raw line. QString commandRawLine=rawLine.simplified(); //Generate command part and data part cache. QString rawCommand, rawData; //Parse the command. parseCommand(commandRawLine, rawCommand, rawData); //Now we are only taken care about the TRACK. if(rawCommand=="TRACK") { //Use the trackIndex variable find the space temporarily. int trackIndex=rawData.indexOf(' '); //Get the track index. trackIndex= (trackIndex==-1?rawData:rawData.left(trackIndex)).toInt(); //Check the track index. if(trackIndex==detailInfo.trackIndex) { //Hit the track. //First we have to output the raw line data. temporaryStream << rawLine << '\n'; //Then check the detail info data. //We have to write out these informations: // PERFORMER Artist // TITLE Name QString trackData; trackData.append(generateLine( " TITLE ", detailInfo.textLists[Name].toString())); trackData.append(generateLine( " PERFORMER ", detailInfo.textLists[Artist].toString())); //Write the track data to temporary file. temporaryStream << trackData; //Read the original file again until to INDEX, skip all the //other data. rawLine=trackStream.readLine(); //Simplified the raw line. commandRawLine=rawLine.simplified(); //Parse the raw line. parseCommand(commandRawLine, rawCommand, rawData); //Read until we get the INDEX. while(!rawLine.isNull() && rawCommand!="INDEX") { //Continue skip the file. rawLine=trackStream.readLine(); //Simplified the raw line. commandRawLine=rawLine.simplified(); //Parse the raw line. parseCommand(commandRawLine, rawCommand, rawData); } //So now the data should be the first INDEX. //Output the data and continue copy the data. temporaryStream << rawLine << '\n'; } else { //Simply output the data. temporaryStream << rawLine << '\n'; } } else { //Simply output the data. temporaryStream << rawLine << '\n'; } //Read the next line. rawLine=trackStream.readLine(); } //Flush the data. temporaryStream << flush; //Now the data should be all done. //Close the list file. listFile.close(); //Reset the temporary file. updatedListFile.reset(); //Reopen the list file, open as write only mode. if(!listFile.open(QIODevice::WriteOnly)) { return false; } //Write all the data to list file. //Generate the music data cache. char *turboCache=new char[DataCacheSize]; //Now copy all the content from the original file to temporary file. int bytesRead=updatedListFile.read(turboCache, DataCacheSize); while(bytesRead>0) { //Write the cache to the list file. listFile.write(turboCache, bytesRead); //Read new data from the original file to cache. bytesRead=updatedListFile.read(turboCache, DataCacheSize); } //Close the list file and temporary file. listFile.close(); updatedListFile.close(); //Clear up the turbo cache. delete[] turboCache; //Written finished. return true; }
bool KNMusicCueListParser::parseList(QFile &listFile, KNMusicListDetailInfo &listDetailInfo) { //Get the file info of the list file. QFileInfo listFileInfo(listFile); //Initial the file as a text file. QTextStream trackStream(&listFile); //Read the first text line. QString rawLine=trackStream.readLine(); //Generate the parse cache data. QString currentFilePath; //Current track music file. bool inTrack=false; //Inside a track or global settings. KNMusicListTrackDetailInfo track; //Track detail info. //Check the raw line while(!rawLine.isNull()) { //Simplified the line. rawLine=rawLine.simplified(); //Generate command part and data part cache. QString rawCommand, rawData; //Parse the command. parseCommand(rawLine, rawCommand, rawData); //Check if command is FILE. //In the FILE command, the structure will be like this: /* * FILE "C:\MYAUDIO.WAV" WAVE * TRACK 01 AUDIO * INDEX 01 00:00:00 * CATALOG 3898347789120 * FILE "C:\TRACK2.WAV" WAVE * TRACK 02 AUDIO * INDEX 00 05:49:65 ; 1 second pregap * INDEX 01 05:50:65 * TRACK 03 AUDIO * INDEX 00 09:45:50 ; 2 second pregap * INDEX 01 09:47:50 * TRACK 04 AUDIO * ISRC ABCDE1234567 * INDEX 01 15:12:53 * TRACK 05 AUDIO * INDEX 01 25:02:40 ; the track pregap * TRACK 06 AUDIO * TITLE "Stay away" * INDEX 01 27:34:05 * FILE "TRACK3.WAV" WAVE * TRACK 07 AUDIO * INDEX 01 31:58:53 * TRACK 08 AUDIO * INDEX 01 35:08:65 */ if(rawCommand=="FILE") { //Get the file name from the raw data. QString filePath=rawLine.left(rawLine.lastIndexOf('\"')); filePath.remove(0, filePath.indexOf('\"')+1); //Check whether this file is using an absolute path. if(QFileInfo::exists(filePath)) { currentFilePath=filePath; } else { //It should use a relatively path. //Because the cue file is in the same directory of the music //file, so we need to combine the music file path. filePath=listFileInfo.absoluteDir().filePath(filePath); //Check existance. if(QFileInfo::exists(filePath)) { currentFilePath=filePath; } else { //If there's no music file, try to find the music file using //the same name as the cue sheet. filePath=listFileInfo.absoluteDir().filePath( listFileInfo.completeBaseName()+"."+ QFileInfo(filePath).suffix()); //Check existance. if(!QFileInfo::exists(filePath)) { //If we still cannot find the file, then failed to parse //this file. return false; } //Save the file path. currentFilePath=filePath; } } //Update the in file state. inTrack=true; } else if(rawCommand=="TRACK") { //Check if the previous index is valid. if(track.index>-1) { //Add the previous track to the list. listDetailInfo.trackList.append(track); } //When we get here, means we will have new track. //Reset the track detail info. track=KNMusicListTrackDetailInfo(); //Configure the track detail info. track.musicFilePath=currentFilePath; //The rawData should be like // 01 AUDIO //The 01 is the track index, get the track index. //Use the trackIndex variable find the space temporarily. int trackIndex=rawData.indexOf(' '); //Get the track index. trackIndex= (trackIndex==-1?rawData:rawData.left(trackIndex)).toInt(); //Check the track index, track index must be start at least 01. if(trackIndex>0) { //Save the track index. track.index=trackIndex; } } else if(rawCommand=="INDEX") { //Check whether we are inside a track, an INDEX command will only //available when there's a valid track. if(inTrack) { //Here's the important part, INDEX command record the start //time(INDEX 01) and stop time for last track (INDEX 00). //Get the current index. int indexID=rawData.indexOf(' '); QString indexRecord; if(indexID!=-1) { //Get the index time text. indexRecord=rawData.mid(indexID+1); //Update the index ID. indexID=(indexID==-1?rawData:rawData.left(indexID)).toInt(); } //Check the index ID. switch(indexID) { case -1: //Invalid. break; case 0: //Stop time for last track. { //Check whether there's tracks in the list. if(!listDetailInfo.trackList.isEmpty()) { //Get the last track from the list. KNMusicListTrackDetailInfo &lastTrack= listDetailInfo.trackList.last(); //Set the duration of the track. lastTrack.trackDuration= textToTime(indexRecord)-lastTrack.startPosition; } break; } case 1: //Start time. { //Set the start position. track.startPosition=textToTime(indexRecord); //Check the start position. if(track.startPosition==-1) { //Failed to parse the track list. return false; } //Check the previous end duration. //Because the previous one cannot play until to the current //track. If the duration of the previous track is -1, we //need to update the data. //Check whether there's tracks in the list. if(!listDetailInfo.trackList.isEmpty()) { //Get the last track from the list. KNMusicListTrackDetailInfo &lastTrack= listDetailInfo.trackList.last(); //Set the duration of the track. lastTrack.trackDuration= track.startPosition-lastTrack.startPosition; } break; } default: break; } } } else { //If the command is not FILE, then it should be metadata. /* * PERFORMER "BiBi ~ Ayase Eri(Nanjo Yoshino), Nishikino Maki(Pile), * Yazawa Nico(Tokui Sora) from μ's~" * TITLE "Cutie Panther" * REM DATE 2013 * REM DISCID 3507C005 * REM COMMENT ExactAudioCopy v1.0b2 */ //Generate the metadata variable. QString metaData; //Get the command index and parse the command. int commandIndex=parseMetaCommand(rawCommand, rawData, metaData, inTrack); //Check the command index, if parse successfully, if(commandIndex!=-1) { if(inTrack && track.index!=-1) { //Write the data to the current track. track.metaData.insert(commandIndex, metaData); } else { //Add the current data to list detail info's data. //The later on global variable will over write the previous //one. listDetailInfo.metaData.insert(commandIndex, metaData); } } } //Read the next line. rawLine=trackStream.readLine(); } //Now the last track is still not be put in the track list. //Check if the last track index valid. if(track.index>-1) { //Add the last track to the list. listDetailInfo.trackList.append(track); } //Parse success. return true; }
bool KNMusicCueParser::parseList(QFile &listFile, KNMusicListDetailInfo &listDetailInfo) { //Initial the file as a text file. QTextStream trackStream(&listFile); //Read the first text line. QString rawLine=trackStream.readLine().simplified(); //Read every line until no text line. while(!rawLine.isNull()) { QString rawCommand, rawData; parseCommand(rawLine, rawCommand, rawData); if(rawCommand=="FILE") { //Get the file name. QString musicFilePath= rawLine.left(rawLine.lastIndexOf('\"')); musicFilePath.remove(0, musicFilePath.indexOf('\"')+1); //Because the cue file must be the same directory of the music file, //so we need to combine the music file path. QFileInfo listFileInfo(listFile); musicFilePath=listFileInfo.absolutePath()+"/"+musicFilePath; //Check is the music file exist. QFileInfo musicFileInfo(musicFilePath); if(musicFileInfo.exists()) { musicFilePath=musicFileInfo.absoluteFilePath(); } else { //If there's no music file, try to find the music file using the //same name. musicFilePath=listFileInfo.absolutePath()+"/"+ listFileInfo.completeBaseName()+"."+musicFileInfo.suffix(); QFileInfo preferMusicFileInfo(musicFilePath); if(!preferMusicFileInfo.exists()) { //If we still can't find this, then is real failed. return false; } musicFilePath=preferMusicFileInfo.absoluteFilePath(); } //Save the path to listDetailInfo. listDetailInfo.musicFilePath=musicFilePath; //Now there must all be track data. QString trackLine=trackStream.readLine().simplified(); parseCommand(trackLine, rawCommand, rawData); //Do parse until the rawCommand is not "TRACK". while(rawCommand=="TRACK" && !trackLine.isNull()) { //Get the current Track Index. int trackIndex=rawData.left(rawData.indexOf(' ')).toInt(); if(trackIndex==0) { trackLine=trackStream.readLine(); parseCommand(trackLine, rawCommand, rawData); //Read line until the next track. while(rawCommand!="TRACK") { trackLine=trackStream.readLine(); parseCommand(trackLine, rawCommand, rawData); } continue; } //Here must be at least track 01. KNMusicListTrackDetailInfo currentTrack; //Parse all the data in the track. currentTrack.index=trackIndex; trackLine=trackStream.readLine().simplified(); parseCommand(trackLine, rawCommand, rawData); while(rawCommand!="TRACK" && !trackLine.isNull()) { if(rawCommand=="INDEX") { //Here's the important part, INDEX command record the //start time(INDEX 01) and stop time for last track //(INDEX 00). int indexSplitter=rawData.indexOf(' '), indexID=rawData.left(indexSplitter).toInt(); QString indexRecordTime=rawData.mid(indexSplitter+1); //Set the track index. switch(indexID) { case 0: //Check is there a track in the list. if(!listDetailInfo.trackList.isEmpty()) { listDetailInfo.trackList.last().trackDuration =timeTextToPosition(indexRecordTime)- listDetailInfo.trackList.last().startPosition; } break; case 1: //Set the start position. currentTrack.startPosition= timeTextToPosition(indexRecordTime); //Check is there a track in the list. if(!listDetailInfo.trackList.isEmpty()) { if(listDetailInfo.trackList.last().trackDuration==-1) { //Check the is the last track's track duration //-1. If so, using the current start as it's //stop. listDetailInfo.trackList.last().trackDuration =currentTrack.startPosition- listDetailInfo.trackList.last().startPosition; } } //Check is the start position available. if(currentTrack.startPosition==-1) { return false; } break; } } else { int commandIndex; QString commandData; //It must be a metadata. parseMetaCommand(rawCommand, rawData, commandIndex, commandData, true); //If there's a command we can't parse, that means parse failed. if(commandIndex!=-1) { currentTrack.metaData[commandIndex]= commandIndex==Comments? currentTrack.metaData[commandIndex]+commandData: commandData; } } //Parse next line. trackLine=trackStream.readLine().simplified(); parseCommand(trackLine, rawCommand, rawData); } //Add current track to track list. listDetailInfo.trackList.append(currentTrack); } //If there's no track in the list, means parse failed. return listDetailInfo.trackList.size()!=0; } //This command must be a meta data. int commandIndex; QString commandData; //Parse the current command. parseMetaCommand(rawCommand, rawData, commandIndex, commandData, false); //If there's a command we can't parse, that means parse failed. if(commandIndex!=-1) { //Add current data to list detail info's data. listDetailInfo.metaData[commandIndex]= commandIndex==Comments? listDetailInfo.metaData[commandIndex]+commandData: commandData; } //Read the next line. rawLine=trackStream.readLine().simplified(); } return true; }