Skip to content

Commit

Permalink
Support for reading embedded CUE from tags.
Browse files Browse the repository at this point in the history
  • Loading branch information
PSyton committed Feb 13, 2015
1 parent 809b1f4 commit ad9452f
Show file tree
Hide file tree
Showing 14 changed files with 496 additions and 168 deletions.
284 changes: 191 additions & 93 deletions xbmc/CueDocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,94 @@
using namespace std;
using namespace XFILE;

// Stuff for read CUE data from different sources.
class CueReader
{
public:
virtual bool ready() const = 0;
virtual bool ReadLine(std::string &line) = 0;
virtual ~CueReader() {}
private:
std::string m_sourcePath;
};

class FileReader
: public CueReader
{
public:
FileReader(const std::string &strFile)
{
m_opened = m_file.Open(strFile);
}
virtual bool ReadLine(std::string &line)
{
// Read the next line.
while (m_file.ReadString(m_szBuffer, 1023)) // Bigger than MAX_PATH_SIZE, for usage with relax!
{
// Remove the white space at the beginning and end of the line.
line = m_szBuffer;
StringUtils::Trim(line);
if (!line.empty())
return true;
// If we are here, we have an empty line so try the next line
}
return false;
}
virtual bool ready() const
{
return m_opened;
}
virtual ~FileReader()
{
if (m_opened)
m_file.Close();

}
private:
CFile m_file;
bool m_opened;
char m_szBuffer[1024];
};

class BufferReader
: public CueReader
{
public:
BufferReader(const std::string &strContent)
: m_data(strContent)
, m_pos(0)
{
}
virtual bool ReadLine(std::string &line)
{
// Read the next line.
line.clear();
bool stop = false;
while (m_pos < m_data.size())
{
// Remove the white space at the beginning of the line.
char ch = m_data.at(m_pos++);
if (ch == '\r' || ch == '\n') {
StringUtils::Trim(line);
if (!line.empty())
return true;
}
else
{
line.push_back(ch);
}
}
return false;
}
virtual bool ready() const
{
return m_data.size() > 0;
}
private:
std::string m_data;
size_t m_pos;
};

CCueDocument::CCueDocument(void)
{
m_iYear = 0;
Expand All @@ -82,13 +170,97 @@ CCueDocument::CCueDocument(void)
CCueDocument::~CCueDocument(void)
{}

////////////////////////////////////////////////////////////////////////////////////
// Function: ParseFile()
// Opens the CUE file for reading, and constructs the track database information
////////////////////////////////////////////////////////////////////////////////////
bool CCueDocument::ParseFile(const std::string &strFilePath)
{
FileReader reader(strFilePath);
return Parse(reader, strFilePath);
}

////////////////////////////////////////////////////////////////////////////////////
// Function: ParseTag()
// Reads CUE data from string buffer, and constructs the track database information
////////////////////////////////////////////////////////////////////////////////////
bool CCueDocument::ParseTag(const std::string &strContent)
{
BufferReader reader(strContent);
return Parse(reader);
}

//////////////////////////////////////////////////////////////////////////////////
// Function:GetSongs()
// Returns the track information from the next item in the cuelist
//////////////////////////////////////////////////////////////////////////////////
void CCueDocument::GetSongs(VECSONGS &songs)
{
for (int i = 0; i < m_iTotalTracks; i++)
{
CSong song;
if ((m_Track[i].strArtist.length() == 0) && (m_strArtist.length() > 0))
song.artist = StringUtils::Split(m_strArtist, g_advancedSettings.m_musicItemSeparator);
else
song.artist = StringUtils::Split(m_Track[i].strArtist, g_advancedSettings.m_musicItemSeparator);
song.albumArtist = StringUtils::Split(m_strArtist, g_advancedSettings.m_musicItemSeparator);
song.strAlbum = m_strAlbum;
song.genre = StringUtils::Split(m_strGenre, g_advancedSettings.m_musicItemSeparator);
song.iYear = m_iYear;
song.iTrack = m_Track[i].iTrackNumber;
if ( m_iDiscNumber > 0 )
song.iTrack |= (m_iDiscNumber << 16); // see CMusicInfoTag::GetDiscNumber()
if (m_Track[i].strTitle.length() == 0) // No track information for this track!
song.strTitle = StringUtils::Format("Track %2d", i + 1);
else
song.strTitle = m_Track[i].strTitle;
song.strFileName = m_Track[i].strFile;
song.iStartOffset = m_Track[i].iStartTime;
song.iEndOffset = m_Track[i].iEndTime;
if (song.iEndOffset)
song.iDuration = (song.iEndOffset - song.iStartOffset + 37) / 75;
else
song.iDuration = 0;
// TODO: replayGain goes here
songs.push_back(song);
}
}

void CCueDocument::UpdateMediaFile(const std::string& oldMediaFile, const std::string& mediaFile)
{
for (int i = 0; i < m_iTotalTracks; i++)
{
if (m_Track[i].strFile == oldMediaFile)
{
m_Track[i].strFile = mediaFile;
}
}
}

void CCueDocument::GetMediaFiles(vector<std::string>& mediaFiles)
{
set<std::string> uniqueFiles;
for (int i = 0; i < m_iTotalTracks; i++)
uniqueFiles.insert(m_Track[i].strFile);

for (set<std::string>::iterator it = uniqueFiles.begin(); it != uniqueFiles.end(); it++)
mediaFiles.push_back(*it);
}

std::string CCueDocument::GetMediaTitle()
{
return m_strAlbum;
}

// Private Functions start here

////////////////////////////////////////////////////////////////////////////////////
// Function: Parse()
// Opens the .cue file for reading, and constructs the track database information
// Constructs the track database information from CUE source
////////////////////////////////////////////////////////////////////////////////////
bool CCueDocument::Parse(const std::string &strFile)
bool CCueDocument::Parse(CueReader& reader, const std::string& strFile)
{
if (!m_file.Open(strFile))
if (!reader.ready())
return false;

std::string strLine;
Expand All @@ -100,13 +272,13 @@ bool CCueDocument::Parse(const std::string &strFile)
// Run through the .CUE file and extract the tracks...
while (true)
{
if (!ReadNextLine(strLine))
if (!reader.ReadLine(strLine))
break;
if (StringUtils::StartsWithNoCase(strLine,"INDEX 01"))
if (StringUtils::StartsWithNoCase(strLine, "INDEX 01"))
{
if (bCurrentFileChanged)
{
CLog::Log(LOGERROR, "Track split over multiple files, unsupported ('%s')", strFile.c_str());
CLog::Log(LOGERROR, "Track split over multiple files, unsupported.");
return false;
}

Expand All @@ -123,21 +295,21 @@ bool CCueDocument::Parse(const std::string &strFile)
if (m_iTotalTracks >= 0)
m_Track[m_iTotalTracks].iStartTime = time; // start time of the next track
}
else if (StringUtils::StartsWithNoCase(strLine,"TITLE"))
else if (StringUtils::StartsWithNoCase(strLine, "TITLE"))
{
if (m_iTotalTracks == -1) // No tracks yet
m_strAlbum = ExtractInfo(strLine.substr(5));
else
m_Track[m_iTotalTracks].strTitle = ExtractInfo(strLine.substr(5));
}
else if (StringUtils::StartsWithNoCase(strLine,"PERFORMER"))
else if (StringUtils::StartsWithNoCase(strLine, "PERFORMER"))
{
if (m_iTotalTracks == -1) // No tracks yet
m_strArtist = ExtractInfo(strLine.substr(9));
else // New Artist for this track
m_Track[m_iTotalTracks].strArtist = ExtractInfo(strLine.substr(9));
}
else if (StringUtils::StartsWithNoCase(strLine,"TRACK"))
else if (StringUtils::StartsWithNoCase(strLine, "TRACK"))
{
int iTrackNumber = ExtractNumericInfo(strLine.substr(5));

Expand All @@ -154,41 +326,41 @@ bool CCueDocument::Parse(const std::string &strFile)

bCurrentFileChanged = false;
}
else if (StringUtils::StartsWithNoCase(strLine,"REM DISCNUMBER"))
else if (StringUtils::StartsWithNoCase(strLine, "REM DISCNUMBER"))
{
int iDiscNumber = ExtractNumericInfo(strLine.substr(14));
if (iDiscNumber > 0)
m_iDiscNumber = iDiscNumber;
}
else if (StringUtils::StartsWithNoCase(strLine,"FILE"))
else if (StringUtils::StartsWithNoCase(strLine, "FILE"))
{
// already a file name? then the time computation will be changed
if(strCurrentFile.size() > 0)
if (!strCurrentFile.empty())
bCurrentFileChanged = true;

strCurrentFile = ExtractInfo(strLine.substr(4));

// Resolve absolute paths (if needed).
if (strCurrentFile.length() > 0)
if (!strFile.empty() && !strCurrentFile.empty())
ResolvePath(strCurrentFile, strFile);
}
else if (StringUtils::StartsWithNoCase(strLine,"REM DATE"))
else if (StringUtils::StartsWithNoCase(strLine, "REM DATE"))
{
int iYear = ExtractNumericInfo(strLine.substr(8));
if (iYear > 0)
m_iYear = iYear;
}
else if (StringUtils::StartsWithNoCase(strLine,"REM GENRE"))
else if (StringUtils::StartsWithNoCase(strLine, "REM GENRE"))
{
m_strGenre = ExtractInfo(strLine.substr(9));
}
else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_ALBUM_GAIN"))
else if (StringUtils::StartsWithNoCase(strLine, "REM REPLAYGAIN_ALBUM_GAIN"))
m_replayGainAlbumGain = (float)atof(strLine.substr(26).c_str());
else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_ALBUM_PEAK"))
else if (StringUtils::StartsWithNoCase(strLine, "REM REPLAYGAIN_ALBUM_PEAK"))
m_replayGainAlbumPeak = (float)atof(strLine.substr(26).c_str());
else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_TRACK_GAIN") && m_iTotalTracks >= 0)
else if (StringUtils::StartsWithNoCase(strLine, "REM REPLAYGAIN_TRACK_GAIN") && m_iTotalTracks >= 0)
m_Track[m_iTotalTracks].replayGainTrackGain = (float)atof(strLine.substr(26).c_str());
else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_TRACK_PEAK") && m_iTotalTracks >= 0)
else if (StringUtils::StartsWithNoCase(strLine, "REM REPLAYGAIN_TRACK_PEAK") && m_iTotalTracks >= 0)
m_Track[m_iTotalTracks].replayGainTrackPeak = (float)atof(strLine.substr(26).c_str());
}

Expand All @@ -198,87 +370,13 @@ bool CCueDocument::Parse(const std::string &strFile)
m_Track[m_iTotalTracks].iEndTime = 0;
else
CLog::Log(LOGERROR, "No INDEX 01 tags in CUE file!");
m_file.Close();
if (m_iTotalTracks >= 0)
{
m_iTotalTracks++;
}
return (m_iTotalTracks > 0);
}

//////////////////////////////////////////////////////////////////////////////////
// Function:GetNextItem()
// Returns the track information from the next item in the cuelist
//////////////////////////////////////////////////////////////////////////////////
void CCueDocument::GetSongs(VECSONGS &songs)
{
for (int i = 0; i < m_iTotalTracks; i++)
{
CSong song;
if ((m_Track[i].strArtist.length() == 0) && (m_strArtist.length() > 0))
song.artist = StringUtils::Split(m_strArtist, g_advancedSettings.m_musicItemSeparator);
else
song.artist = StringUtils::Split(m_Track[i].strArtist, g_advancedSettings.m_musicItemSeparator);
song.albumArtist = StringUtils::Split(m_strArtist, g_advancedSettings.m_musicItemSeparator);
song.strAlbum = m_strAlbum;
song.genre = StringUtils::Split(m_strGenre, g_advancedSettings.m_musicItemSeparator);
song.iYear = m_iYear;
song.iTrack = m_Track[i].iTrackNumber;
if ( m_iDiscNumber > 0 )
song.iTrack |= (m_iDiscNumber << 16); // see CMusicInfoTag::GetDiscNumber()
if (m_Track[i].strTitle.length() == 0) // No track information for this track!
song.strTitle = StringUtils::Format("Track %2d", i + 1);
else
song.strTitle = m_Track[i].strTitle;
song.strFileName = m_Track[i].strFile;
song.iStartOffset = m_Track[i].iStartTime;
song.iEndOffset = m_Track[i].iEndTime;
if (song.iEndOffset)
song.iDuration = (song.iEndOffset - song.iStartOffset + 37) / 75;
else
song.iDuration = 0;
// TODO: replayGain goes here
songs.push_back(song);
}
}

void CCueDocument::GetMediaFiles(vector<std::string>& mediaFiles)
{
set<std::string> uniqueFiles;
for (int i = 0; i < m_iTotalTracks; i++)
uniqueFiles.insert(m_Track[i].strFile);

for (set<std::string>::iterator it = uniqueFiles.begin(); it != uniqueFiles.end(); ++it)
mediaFiles.push_back(*it);
}

std::string CCueDocument::GetMediaTitle()
{
return m_strAlbum;
}

// Private Functions start here

////////////////////////////////////////////////////////////////////////////////////
// Function: ReadNextLine()
// Returns the next non-blank line of the textfile, stripping any whitespace from
// the left.
////////////////////////////////////////////////////////////////////////////////////
bool CCueDocument::ReadNextLine(std::string &szLine)
{
// Read the next line.
while (m_file.ReadString(m_szBuffer, 1023)) // Bigger than MAX_PATH_SIZE, for usage with relax!
{
// Remove the white space at the beginning and end of the line.
szLine = m_szBuffer;
StringUtils::Trim(szLine);
if (!szLine.empty())
return true;
// If we are here, we have an empty line so try the next line
}
return false;
}

////////////////////////////////////////////////////////////////////////////////////
// Function: ExtractInfo()
// Extracts the information in quotes from the string line, returning it in quote
Expand Down
Loading

0 comments on commit ad9452f

Please sign in to comment.