add mp3 and ogg torrent url info to JamendoAlbum
[amarok.git] / src / cuefile.cpp
blobc75ccbfbf9a5f008393181d4e64a41ecae16036a
1 // (c) 2005 Martin Ehmke <ehmke@gmx.de>
2 // License: GNU General Public License V2
4 #define DEBUG_PREFIX "CueFile"
6 #include "cuefile.h"
8 #include "debug.h"
9 #include "enginecontroller.h"
10 #include "metabundle.h"
12 #include <KGlobal>
14 #include <QFile>
15 #include <QMap>
16 #include <QStringList>
17 #include <QTextStream>
20 CueFile *CueFile::instance()
22 static CueFile *s_instance = 0;
24 if(!s_instance)
26 s_instance = new CueFile(EngineController::instance()); // FIXME berkus: le grand borkage (if engine is changed, e.g.)?
29 return s_instance;
32 CueFile::~CueFile()
34 debug() << "shmack! destructed";
39 + - set and continue in the same state
40 x - cannot happen
41 track - switch to new state
43 state/next token >
44 v PERFORMER TITLE FILE TRACK INDEX PREGAP
45 begin + + file x x x
46 file x x file track x x
47 track + + file x + +
48 index x x file track + x
50 1. Ignore FILE completely.
51 2. INDEX 00 is gap abs position in cue formats we care about (use it to calc length of prev track and then drop on the floor).
52 3. Ignore subsequent INDEX entries (INDEX 02, INDEX 03 etc). - FIXME? this behavior is different from state chart above.
53 4. For a valid cuefile at least TRACK and INDEX are required.
55 enum {
56 BEGIN = 0,
57 TRACK_FOUND, // track found, index not yet found
58 INDEX_FOUND
62 /**
63 * @return true if the cuefile could be successfully loaded
65 bool CueFile::load(int mediaLength)
67 clear();
68 m_lastSeekPos = -1;
70 if( QFile::exists( m_cueFileName ) )
72 QFile file( m_cueFileName );
73 int track = 0;
74 QString defaultArtist = QString();
75 QString defaultAlbum = QString();
76 QString artist = QString();
77 QString title = QString();
78 long length = 0;
79 long prevIndex = -1;
80 bool index00Present = false;
81 long index = -1;
83 int mode = BEGIN;
84 if( file.open( QIODevice::ReadOnly ) )
86 QTextStream stream( &file );
87 QString line;
89 while ( !stream.atEnd() )
91 line = stream.readLine().simplified();
93 if( line.startsWith( "title", Qt::CaseInsensitive ) )
95 title = line.mid( 6 ).remove( '"' );
96 if( mode == BEGIN )
98 defaultAlbum = title;
99 title.clear();
100 debug() << "Album: " << defaultAlbum;
102 else
103 debug() << "Title: " << title;
106 else if( line.startsWith( "performer", Qt::CaseInsensitive ))
108 artist = line.mid( 10 ).remove( '"' );
109 if( mode == BEGIN )
111 defaultArtist = artist;
112 artist.clear();
113 debug() << "Album Artist: " << defaultArtist;
115 else
116 debug() << "Artist: " << artist;
119 else if( line.startsWith( "track", Qt::CaseInsensitive ) )
121 if( mode == TRACK_FOUND )
123 // not valid, because we have to have an index for the previous track
124 file.close();
125 debug() << "Mode is TRACK_FOUND, abort.";
126 return false;
128 if( mode == INDEX_FOUND )
130 if(artist.isNull())
131 artist = defaultArtist;
133 debug() << "Inserting item: " << title << " - " << artist << " on " << defaultAlbum << " (" << track << ")";
134 // add previous entry to map
135 insert( index, CueFileItem( title, artist, defaultAlbum, track, index ) );
136 prevIndex = index;
137 title.clear();
138 artist.clear();
139 track = 0;
141 track = line.section (' ',1,1).toInt();
142 debug() << "Track: " << track;
143 mode = TRACK_FOUND;
145 else if( line.startsWith( "index", Qt::CaseInsensitive ) )
147 if( mode == TRACK_FOUND)
149 int indexNo = line.section(' ',1,1).toInt();
151 if( indexNo == 1 )
153 QString splitMe = line.section( ' ', -1, -1 );
154 QStringList time = splitMe.split( ":", QString::SkipEmptyParts );
156 index = time[0].toLong()*60*1000 + time[1].toLong()*1000 + time[2].toLong()*1000/75; //75 frames per second
158 if( prevIndex != -1 && !index00Present ) // set the prev track's length if there is INDEX01 present, but no INDEX00
160 length = index - prevIndex;
161 debug() << "Setting length of track " << (*this)[prevIndex].getTitle() << " to " << length << " msecs.";
162 (*this)[prevIndex].setLength(length);
165 index00Present = false;
166 mode = INDEX_FOUND;
167 length = 0;
170 else if( indexNo == 0 ) // gap, use to calc prev track length
172 QString splitMe = line.section( ' ', -1, -1 );
173 QStringList time = splitMe.split( ":", QString::SkipEmptyParts );
175 length = time[0].toLong()*60*1000 + time[1].toLong()*1000 + time[2].toLong()*1000/75; //75 frames per second
177 if( prevIndex != -1 )
179 length -= prevIndex; //this[prevIndex].getIndex();
180 debug() << "Setting length of track " << (*this)[prevIndex].getTitle() << " to " << length << " msecs.";
181 (*this)[prevIndex].setLength(length);
182 index00Present = true;
184 else
185 length = 0;
187 else
189 debug() << "Skipping unsupported INDEX " << indexNo;
192 else
194 // not valid, because we don't have an associated track
195 file.close();
196 debug() << "Mode is not TRACK_FOUND but encountered INDEX, abort.";
197 return false;
199 debug() << "index: " << index;
203 if(artist.isNull())
204 artist = defaultArtist;
206 debug() << "Inserting item: " << title << " - " << artist << " on " << defaultAlbum << " (" << track << ")";
207 // add previous entry to map
208 insert( index, CueFileItem( title, artist, defaultAlbum, track, index ) );
209 file.close();
213 * Because there is no way to set the length for the last track in a normal way,
214 * we have to do some magic here. Having the total length of the media file given
215 * we can set the lenth for the last track after all the cue file was loaded into array.
218 (*this)[index].setLength(mediaLength*1000 - index);
219 debug() << "Setting length of track " << (*this)[index].getTitle() << " to " << mediaLength*1000 - index << " msecs.";
221 return true;
224 else
225 return false;
228 void CueFile::engineTrackPositionChanged( long position, bool userSeek )
230 Q_UNUSED( position ); Q_UNUSED( userSeek );
231 //TODO: port 2.0 reimplement this
232 /*position /= 1000;
233 if(userSeek || position > m_lastSeekPos)
235 CueFile::Iterator it = end();
236 while( it != begin() )
238 --it;
239 // debug() << "Checking " << position << " against pos " << it.key()/1000 << " title " << it.data().getTitle();
240 if(it.key()/1000 <= position)
242 MetaBundle bundle = EngineController::instance()->bundle(); // take current one and modify it
243 if(it.data().getTitle() != bundle.title()
244 || it.data().getArtist() != bundle.artist()
245 || it.data().getAlbum() != bundle.album()
246 || it.data().getTrackNumber() != bundle.track())
248 bundle.setTitle(it.data().getTitle());
249 bundle.setArtist(it.data().getArtist());
250 bundle.setAlbum(it.data().getAlbum());
251 bundle.setTrack(it.data().getTrackNumber());
252 emit metaData(bundle);
254 long length = it.data().getLength();
255 if ( length == -1 ) // need to calculate
257 ++it;
258 long nextKey = it == end() ? bundle.length() * 1000 : it.key();
259 --it;
260 length = qMax( nextKey - it.key(), 0L );
262 emit newCuePoint( position, it.key() / 1000, ( it.key() + length ) / 1000 );
264 break;
269 m_lastSeekPos = position;*/
273 #include "cuefile.moc"