Reputation: 107
iTunes get the lyrics information from a song file, and I want to ask if there are some methods or APIs to get the lyrics from the song file(not use network!),in cocoa or carbon.Thank you very much. :)
Upvotes: 3
Views: 5658
Reputation: 1299
no easy way? what about using ScriptingBridge?. Question is tagged 'osx' so iTunes can be assumed to present and used. If you can't for some reason ask iTunes about this data, this won't work of course
sdef /Applications/iTunes.app | sdp -fh --basename iTunes
to get iTunes.h
for current iTunes song.
iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
iTunesTrack * currentTrack=[iTunes currentTrack];
NSString * lyrics=[currentTrack lyrics];
see https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ScriptingBridgeConcepts/UsingScriptingBridge/UsingScriptingBridge.html#//apple_ref/doc/uid/TP40006104-CH4-SW15 on general ScriptingBridge guide
you can iterate over all music,etc if that's ok for you
Upvotes: 0
Reputation: 365587
And there is no easy way to do this.
You will need to use a third-party library. And, while there are some third-party libraries that transparently handle reading tags from a variety of different formats, they only handle the "basic set" of tags that way; for anything else, you have to actually know about the formats.
For example, with TagLib, to get the artist, you just do this:
TagLib::FileRef f1("myfile.mp3");
cout << f1.tag()->artist();
TagLib::FileRef f2("myotherfile.aac");
cout << f2.tag()->artist();
But for lyrics, it's something like this:
TagLib::MPEG::File f1("myfile.mp3");
TagLib::ID3v2::FrameList frames = f1.ID3v2Tag()->frameListMap()["USLT"];
if (!frames.isEmpty()) {
TagLib::ID3v2::UnsynchronizedLyricsFrame *frame =
dynamic_cast<TagLib::ID3v2::UnsynchronizedLyricsFrame *>(frames.front());
// There could be multiple frames here; you may want to look at language
// and/or description, instead of just picking the first.
if (frame) cout << frames->text;
}
TagLib::MP4::File f2("myotherfile.aac");
TagLib::MP4::Item item = f2.tag()->itemListMap()["\xa9lyr"];
TagLib::StringList strings = item.toStringList();
if (!strings.isEmpty()) {
// As above, there could be multiple strings.
cout << strings->front();
}
That's off the top of my head, so don't expect it to work exactly as-is. And of course there's almost no error handling. But the big thing that's missing is that it doesn't show you how to figure out what type of file you're dealing with, and what type of tag you'll get out of it. (This is pretty easy with the two examples above, but a file named ".flac" could be either an OGG FLAC or a raw FLAC, and could have VORBISCOMMENT, MetaFLAC, ID3, or APE tags.) TagLib has stuff to help there as well, but it's still not trivial.
Fortunately, if you only care about getting exactly the same lyrics as iTunes 10.6.3, it's not that hard; the rules seem to be something like this:
And, since you're only dealing with ID3v2 and ITMF, it may actually be simpler to use separate libraries for each—for example, libmp4v2 handles MPEG4 files more simply than TagLib (because it doesn't handle anything but MPEG4 files), something like this:
MP4FileHandle f = MP4Open("myotherfile.aac");
const MP4Tags *tags = MP4TagsAlloc();
MP4TagsFetch(tags, f);
cout << tags->lyrics;
MP4TagsFree(tags);
MP4Close(f);
Also, if this doesn't have to be in native code (Cocoa or Carbon), there are some simpler libraries in other languages. For example, in Python, with Mutagen, you can do this:
def printlyrics(path):
f = mutagen.File(path)
for key in f.keys():
if key.startswith('USLT') or key == u'\xa9lyr':
print f[key]
return
printlyrics("myfile.mp3")
printlyrics("myotherfile.aac")
Of course I still had to know that ID3v2 calls lyrics "USLT:my desc:'eng'" while ITMF calls them "©lyr", but because of the dynamic nature of Python, Mutagen can hide all of the other details.
Upvotes: 2