FFmpeg Input: Implement stream metadata reading
Now reads Icy interval metadata and timed ID3v2 tags. Signed-off-by: Christopher Snowhill <kode54@gmail.com>CQTexperiment
parent
7cea254f4c
commit
e13f83609e
|
@ -100,8 +100,10 @@ namespace
|
|||
// updated. However at some point that list should be created at the same time
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
if(ext.isEmpty()) {
|
||||
// HACK
|
||||
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
8352D49B1CDDB8B2009D16AA /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D49A1CDDB8B2009D16AA /* VideoToolbox.framework */; };
|
||||
8352D49D1CDDB8C0009D16AA /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D49C1CDDB8C0009D16AA /* CoreMedia.framework */; };
|
||||
8352D49F1CDDB8D7009D16AA /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8352D49E1CDDB8D7009D16AA /* CoreVideo.framework */; };
|
||||
8356BCE927B37C6F0074E50C /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 8356BCE727B37C6F0074E50C /* NSDictionary+Merge.m */; };
|
||||
83AA7D0C279EBCC600087AA4 /* libavcodec.59.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AA7D08279EBCC600087AA4 /* libavcodec.59.dylib */; };
|
||||
83AA7D0D279EBCC600087AA4 /* libavutil.57.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AA7D09279EBCC600087AA4 /* libavutil.57.dylib */; };
|
||||
83AA7D0E279EBCC600087AA4 /* libswresample.4.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83AA7D0A279EBCC600087AA4 /* libswresample.4.dylib */; };
|
||||
|
@ -52,6 +53,9 @@
|
|||
8352D49A1CDDB8B2009D16AA /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
|
||||
8352D49C1CDDB8C0009D16AA /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
|
||||
8352D49E1CDDB8D7009D16AA /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
|
||||
8356BCE727B37C6F0074E50C /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge.m"; path = "../../Utils/NSDictionary+Merge.m"; sourceTree = "<group>"; };
|
||||
8356BCE827B37C6F0074E50C /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Merge.h"; path = "../../Utils/NSDictionary+Merge.h"; sourceTree = "<group>"; };
|
||||
8356BCEA27B37DA40074E50C /* TagLibID3v2Reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TagLibID3v2Reader.h; path = ../TagLib/TagLibID3v2Reader.h; sourceTree = "<group>"; };
|
||||
8384913818081F6C00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||
83AA7D08279EBCC600087AA4 /* libavcodec.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.59.dylib; path = ../../ThirdParty/ffmpeg/lib/libavcodec.59.dylib; sourceTree = "<group>"; };
|
||||
83AA7D09279EBCC600087AA4 /* libavutil.57.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavutil.57.dylib; path = ../../ThirdParty/ffmpeg/lib/libavutil.57.dylib; sourceTree = "<group>"; };
|
||||
|
@ -135,6 +139,9 @@
|
|||
08FB77AFFE84173DC02AAC07 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8356BCEA27B37DA40074E50C /* TagLibID3v2Reader.h */,
|
||||
8356BCE827B37C6F0074E50C /* NSDictionary+Merge.h */,
|
||||
8356BCE727B37C6F0074E50C /* NSDictionary+Merge.m */,
|
||||
8384913818081F6C00E7332D /* Logging.h */,
|
||||
B09E94370D747FAD0064F138 /* Plugin.h */,
|
||||
B09E942D0D747F410064F138 /* FFMPEGDecoder.h */,
|
||||
|
@ -253,6 +260,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B09E942F0D747F410064F138 /* FFMPEGDecoder.m in Sources */,
|
||||
8356BCE927B37C6F0074E50C /* NSDictionary+Merge.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -40,6 +40,12 @@
|
|||
int64_t skipSamples;
|
||||
BOOL endOfStream;
|
||||
BOOL endOfAudio;
|
||||
|
||||
int metadataIndex;
|
||||
NSString *artist;
|
||||
NSString *title;
|
||||
NSString *album;
|
||||
NSDictionary *id3Metadata;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
// test
|
||||
#import "FFMPEGDecoder.h"
|
||||
|
||||
#import "NSDictionary+Merge.h"
|
||||
#import "TagLibID3v2Reader.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#import "Logging.h"
|
||||
|
@ -92,13 +95,20 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
return NO;
|
||||
}
|
||||
|
||||
AVDictionary *dict = NULL;
|
||||
|
||||
av_dict_set_int(&dict, "icy", 1, 0); // Enable Icy interval metadata, if supported
|
||||
|
||||
NSString *urlString = [url absoluteString];
|
||||
if((errcode = avformat_open_input(&formatCtx, [urlString UTF8String], NULL, NULL)) < 0) {
|
||||
if((errcode = avformat_open_input(&formatCtx, [urlString UTF8String], NULL, &dict)) < 0) {
|
||||
av_dict_free(&dict);
|
||||
char errDescr[4096];
|
||||
av_strerror(errcode, errDescr, 4096);
|
||||
ALog(@"Error opening file, errcode = %d, error = %s", errcode, errDescr);
|
||||
return NO;
|
||||
}
|
||||
|
||||
av_dict_free(&dict);
|
||||
} else {
|
||||
buffer = av_malloc(32 * 1024);
|
||||
if(!buffer) {
|
||||
|
@ -136,6 +146,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
}
|
||||
|
||||
streamIndex = -1;
|
||||
metadataIndex = -1;
|
||||
AVCodecParameters *codecPar;
|
||||
|
||||
for(i = 0; i < formatCtx->nb_streams; i++) {
|
||||
|
@ -144,6 +155,8 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
if(streamIndex < 0 && codecPar->codec_type == AVMEDIA_TYPE_AUDIO) {
|
||||
DLog(@"audio codec found");
|
||||
streamIndex = i;
|
||||
} else if(codecPar->codec_id == AV_CODEC_ID_TIMED_ID3) {
|
||||
metadataIndex = i;
|
||||
} else {
|
||||
stream->discard = AVDISCARD_ALL;
|
||||
}
|
||||
|
@ -402,6 +415,12 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
|
||||
seekable = [s seekable];
|
||||
|
||||
album = @"";
|
||||
artist = @"";
|
||||
title = @"";
|
||||
id3Metadata = @{};
|
||||
[self updateMetadata];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
@ -444,6 +463,56 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
[self close];
|
||||
}
|
||||
|
||||
- (void)updateMetadata {
|
||||
const AVDictionaryEntry *tag = NULL;
|
||||
NSString *_album = album;
|
||||
NSString *_artist = artist;
|
||||
NSString *_title = title;
|
||||
if(formatCtx->metadata) {
|
||||
while((tag = av_dict_get(formatCtx->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||
if(!strcasecmp(tag->key, "streamtitle")) {
|
||||
NSString *artistTitle = [NSString stringWithUTF8String:tag->value];
|
||||
NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "];
|
||||
_artist = @"";
|
||||
_title = [splitValues objectAtIndex:0];
|
||||
if([splitValues count] > 1) {
|
||||
_artist = _title;
|
||||
_title = [splitValues objectAtIndex:1];
|
||||
}
|
||||
} else if(!strcasecmp(tag->key, "icy-url")) {
|
||||
_album = [NSString stringWithUTF8String:tag->value];
|
||||
} else if(!strcasecmp(tag->key, "artist")) {
|
||||
_artist = [NSString stringWithUTF8String:tag->value];
|
||||
} else if(!strcasecmp(tag->key, "title")) {
|
||||
_title = [NSString stringWithUTF8String:tag->value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(![_album isEqual:album] ||
|
||||
![_artist isEqual:artist] ||
|
||||
![_title isEqual:title]) {
|
||||
album = _album;
|
||||
artist = _artist;
|
||||
title = _title;
|
||||
[self willChangeValueForKey:@"metadata"];
|
||||
[self didChangeValueForKey:@"metadata"];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateID3Metadata {
|
||||
NSData *tag = [NSData dataWithBytes:lastReadPacket->data length:lastReadPacket->size];
|
||||
Class tagReader = NSClassFromString(@"TagLibID3v2Reader");
|
||||
if(tagReader && [tagReader respondsToSelector:@selector(metadataForTag:)]) {
|
||||
NSDictionary *_id3Metadata = [tagReader metadataForTag:tag];
|
||||
if(![_id3Metadata isEqualTo:id3Metadata]) {
|
||||
id3Metadata = _id3Metadata;
|
||||
[self willChangeValueForKey:@"metadata"];
|
||||
[self didChangeValueForKey:@"metadata"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (int)readAudio:(void *)buf frames:(UInt32)frames {
|
||||
if(totalFrames && framesRead >= totalFrames)
|
||||
return 0;
|
||||
|
@ -482,6 +551,12 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
}
|
||||
if(formatCtx->pb && formatCtx->pb->error) break;
|
||||
}
|
||||
|
||||
if(lastReadPacket->stream_index == metadataIndex) {
|
||||
[self updateID3Metadata];
|
||||
continue;
|
||||
}
|
||||
|
||||
if(lastReadPacket->stream_index != streamIndex)
|
||||
continue;
|
||||
}
|
||||
|
@ -593,6 +668,8 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
bytesRead += toConsume;
|
||||
}
|
||||
|
||||
[self updateMetadata];
|
||||
|
||||
int framesReadNow = bytesRead / frameSize;
|
||||
if(totalFrames && (framesRead + framesReadNow > totalFrames))
|
||||
framesReadNow = (int)(totalFrames - framesRead);
|
||||
|
@ -648,7 +725,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
|
|||
}
|
||||
|
||||
- (NSDictionary *)metadata {
|
||||
return @{};
|
||||
return [NSDictionary dictionaryByMerging:@{ @"album": album, @"artist": artist, @"title": title } with:id3Metadata];
|
||||
}
|
||||
|
||||
+ (NSArray *)fileTypes {
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
17C93FC30B90056C008627D6 /* TagLibMetadataReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */; };
|
||||
17F563B40C3BDBB30019975C /* TagLib.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F563A60C3BDB8F0019975C /* TagLib.framework */; };
|
||||
17F563B60C3BDBB50019975C /* TagLib.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F563A60C3BDB8F0019975C /* TagLib.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
8356BCE527B377C20074E50C /* TagLibID3v2Reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */; };
|
||||
8356BCE627B377C20074E50C /* TagLibID3v2Reader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */; };
|
||||
8384913A18081FFC00E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384913918081FFC00E7332D /* Logging.h */; };
|
||||
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
@ -56,6 +58,8 @@
|
|||
17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; path = TagLibMetadataReader.m; sourceTree = "<group>"; };
|
||||
17F563A00C3BDB8F0019975C /* TagLib.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = TagLib.xcodeproj; path = ../../Frameworks/TagLib/TagLib.xcodeproj; sourceTree = SOURCE_ROOT; };
|
||||
32DBCF630370AF2F00C91783 /* TagLib_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TagLib_Prefix.pch; sourceTree = "<group>"; };
|
||||
8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TagLibID3v2Reader.h; sourceTree = "<group>"; };
|
||||
8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TagLibID3v2Reader.mm; sourceTree = "<group>"; };
|
||||
8384913918081FFC00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||
8D5B49B6048680CD000E48DA /* TagLib.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TagLib.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -113,6 +117,8 @@
|
|||
177FCFA40B90C9600011C3B5 /* Plugin.h */,
|
||||
17C93FC10B90056C008627D6 /* TagLibMetadataReader.h */,
|
||||
17C93FC20B90056C008627D6 /* TagLibMetadataReader.m */,
|
||||
8356BCE327B377C20074E50C /* TagLibID3v2Reader.h */,
|
||||
8356BCE427B377C20074E50C /* TagLibID3v2Reader.mm */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
|
@ -168,6 +174,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8384913A18081FFC00E7332D /* Logging.h in Headers */,
|
||||
8356BCE527B377C20074E50C /* TagLibID3v2Reader.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -257,6 +264,7 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
8356BCE627B377C20074E50C /* TagLibID3v2Reader.mm in Sources */,
|
||||
17C93FC30B90056C008627D6 /* TagLibMetadataReader.m in Sources */,
|
||||
07CACE8B0ED1AD1000C0F1E8 /* TagLibMetadataWriter.m in Sources */,
|
||||
);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// TagLibID3v2Reader.h
|
||||
// TagLib Plugin
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/8/22.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TagLibID3v2Reader : NSObject
|
||||
+ (NSDictionary *)metadataForTag:(NSData *)tagBlock;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// TagLibID3v2Reader.m
|
||||
// TagLib Plugin
|
||||
//
|
||||
// Created by Christopher Snowhill on 2/8/22.
|
||||
//
|
||||
|
||||
#import "TagLibID3v2Reader.h"
|
||||
|
||||
#import <taglib/fileref.h>
|
||||
#import <taglib/mpeg/id3v2/frames/attachedpictureframe.h>
|
||||
#import <taglib/mpeg/id3v2/id3v2tag.h>
|
||||
#import <taglib/mpeg/mpegfile.h>
|
||||
#import <taglib/toolkit/tbytevectorstream.h>
|
||||
|
||||
@implementation TagLibID3v2Reader
|
||||
|
||||
+ (NSDictionary *)metadataForTag:(NSData *)tagBlock {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// if ( !*TagLib::ascii_encoding ) {
|
||||
// NSStringEncoding enc = [NSString defaultCStringEncoding];
|
||||
// CFStringEncoding cfenc = CFStringConvertNSStringEncodingToEncoding(enc);
|
||||
// NSString *ref = (NSString *)CFStringConvertEncodingToIANACharSetName(cfenc);
|
||||
// UInt32 cp = CFStringConvertEncodingToWindowsCodepage(cfenc);
|
||||
//
|
||||
// // Most tags are using windows codepage, so remap OS X codepage to Windows one.
|
||||
//
|
||||
// static struct {
|
||||
// UInt32 from, to;
|
||||
// } codepage_remaps[] = {
|
||||
// { 10001, 932 }, // Japanese Shift-JIS
|
||||
// { 10002, 950 }, // Traditional Chinese
|
||||
// { 10003, 949 }, // Korean
|
||||
// { 10004, 1256 }, // Arabic
|
||||
// { 10005, 1255 }, // Hebrew
|
||||
// { 10006, 1253 }, // Greek
|
||||
// { 10007, 1251 }, // Cyrillic
|
||||
// { 10008, 936 }, // Simplified Chinese
|
||||
// { 10029, 1250 }, // Central European (latin2)
|
||||
// };
|
||||
//
|
||||
// int i;
|
||||
// int max = sizeof(codepage_remaps)/sizeof(codepage_remaps[0]);
|
||||
// for ( i=0; i<max; i++ )
|
||||
// if ( codepage_remaps[i].from == cp )
|
||||
// break;
|
||||
// if ( i < max )
|
||||
// sprintf(TagLib::ascii_encoding, "windows-%d", codepage_remaps[i].to);
|
||||
// else
|
||||
// strcpy(TagLib::ascii_encoding, [ref UTF8String]);
|
||||
//
|
||||
// }
|
||||
|
||||
TagLib::ByteVector vector((const char *)[tagBlock bytes], (unsigned int)[tagBlock length]);
|
||||
TagLib::ByteVectorStream vectorStream(vector);
|
||||
|
||||
TagLib::FileRef f(&vectorStream, false);
|
||||
if(!f.isNull()) {
|
||||
const TagLib::Tag *tag = f.tag();
|
||||
|
||||
if(tag) {
|
||||
TagLib::String artist, albumartist, title, album, genre, comment;
|
||||
int year, track, disc;
|
||||
float rgAlbumGain, rgAlbumPeak, rgTrackGain, rgTrackPeak;
|
||||
TagLib::String cuesheet;
|
||||
TagLib::String soundcheck;
|
||||
|
||||
artist = tag->artist();
|
||||
albumartist = tag->albumartist();
|
||||
title = tag->title();
|
||||
album = tag->album();
|
||||
genre = tag->genre();
|
||||
comment = tag->comment();
|
||||
cuesheet = tag->cuesheet();
|
||||
|
||||
year = tag->year();
|
||||
[dict setObject:[NSNumber numberWithInt:year] forKey:@"year"];
|
||||
|
||||
track = tag->track();
|
||||
[dict setObject:[NSNumber numberWithInt:track] forKey:@"track"];
|
||||
|
||||
disc = tag->disc();
|
||||
[dict setObject:[NSNumber numberWithInt:disc] forKey:@"disc"];
|
||||
|
||||
rgAlbumGain = tag->rgAlbumGain();
|
||||
rgAlbumPeak = tag->rgAlbumPeak();
|
||||
rgTrackGain = tag->rgTrackGain();
|
||||
rgTrackPeak = tag->rgTrackPeak();
|
||||
[dict setObject:[NSNumber numberWithFloat:rgAlbumGain] forKey:@"replayGainAlbumGain"];
|
||||
[dict setObject:[NSNumber numberWithFloat:rgAlbumPeak] forKey:@"replayGainAlbumPeak"];
|
||||
[dict setObject:[NSNumber numberWithFloat:rgTrackGain] forKey:@"replayGainTrackGain"];
|
||||
[dict setObject:[NSNumber numberWithFloat:rgTrackPeak] forKey:@"replayGainTrackPeak"];
|
||||
|
||||
soundcheck = tag->soundcheck();
|
||||
if(!soundcheck.isEmpty()) {
|
||||
TagLib::StringList tag = soundcheck.split(" ");
|
||||
TagLib::StringList wantedTag;
|
||||
for(int i = 0, count = tag.size(); i < count; i++) {
|
||||
if(tag[i].length() == 8)
|
||||
wantedTag.append(tag[i]);
|
||||
}
|
||||
|
||||
if(wantedTag.size() >= 10) {
|
||||
float volume1 = -log10((double)((uint32_t)wantedTag[0].toInt(16)) / 1000) * 10;
|
||||
float volume2 = -log10((double)((uint32_t)wantedTag[1].toInt(16)) / 1000) * 10;
|
||||
float volumeToUse = MIN(volume1, volume2);
|
||||
float volumeScale = pow(10, volumeToUse / 20);
|
||||
[dict setObject:[NSNumber numberWithFloat:volumeScale] forKey:@"volume"];
|
||||
}
|
||||
}
|
||||
|
||||
if(!artist.isEmpty())
|
||||
[dict setObject:[NSString stringWithUTF8String:artist.toCString(true)] forKey:@"artist"];
|
||||
|
||||
if(!albumartist.isEmpty())
|
||||
[dict setObject:[NSString stringWithUTF8String:albumartist.toCString(true)] forKey:@"albumartist"];
|
||||
|
||||
if(!album.isEmpty())
|
||||
[dict setObject:[NSString stringWithUTF8String:album.toCString(true)] forKey:@"album"];
|
||||
|
||||
if(!title.isEmpty())
|
||||
[dict setObject:[NSString stringWithUTF8String:title.toCString(true)] forKey:@"title"];
|
||||
|
||||
if(!genre.isEmpty())
|
||||
[dict setObject:[NSString stringWithUTF8String:genre.toCString(true)] forKey:@"genre"];
|
||||
|
||||
if(!cuesheet.isEmpty())
|
||||
[dict setObject:[NSString stringWithUTF8String:cuesheet.toCString(true)] forKey:@"cuesheet"];
|
||||
|
||||
// Try to load the image.
|
||||
NSData *image = nil;
|
||||
|
||||
TagLib::MPEG::File *mf = dynamic_cast<TagLib::MPEG::File *>(f.file());
|
||||
if(mf) {
|
||||
TagLib::ID3v2::FrameList pictures = mf->ID3v2Tag()->frameListMap()["APIC"];
|
||||
if(!pictures.isEmpty()) {
|
||||
TagLib::ID3v2::AttachedPictureFrame *pic = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(pictures.front());
|
||||
|
||||
image = [NSData dataWithBytes:pic->picture().data() length:pic->picture().size()];
|
||||
}
|
||||
}
|
||||
|
||||
if(nil != image) {
|
||||
[dict setObject:image forKey:@"albumArt"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [NSDictionary dictionaryWithDictionary:dict];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue