как получить информацию об аудиофайле в iPhone - PullRequest
7 голосов
/ 23 ноября 2011

Я сделал пользовательский плеер с помощью AVAudioPlayer. Теперь я хочу получить подробную информацию об аудиофайле, такую ​​как имя исполнителя, название альбома и т. Д., Который добавлен в папку ресурсов.

MPMusicPlayer предоставляет API для извлечения деталей, но использует библиотеку iPod и не берет ресурс из песочницы приложения. Таким образом, MPMusicPlayer не будет работать в этом сценарии.

Итак, как мы можем получить информацию об аудиофайле в iPhone.

Ответы [ 2 ]

14 голосов
/ 24 ноября 2011

Вы можете получить эту информацию через AudioToolbox.framework. AudioToolbox.framework - это API C, поэтому я написал для него оболочку Objective-C:

ID3Tag .h:

@interface ID3Tag : NSObject <NSCoding> {
    NSString* title_;
    NSString* album_;
    NSString* artist_;
    NSNumber* trackNumber_;
    NSNumber* totalTracks_;
    NSString* genre_;
    NSString* year_;
    NSNumber* approxDuration_;
    NSString* composer_;
    NSString* tempo_;
    NSString* keySignature_;
    NSString* timeSignature_;
    NSString* lyricist_;
    NSString* recordedDate_;
    NSString* comments_;
    NSString* copyright_;
    NSString* sourceEncoder_;
    NSString* encodingApplication_;
    NSString* bitRate_;
    NSStream* sourceBitRate_;
    NSString* channelLayout_;
    NSString* isrc_;
    NSString* subtitle_;  
}

@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *album;
@property (nonatomic, retain) NSString *artist;
@property (nonatomic, retain) NSNumber *trackNumber;
@property (nonatomic, retain) NSNumber *totalTracks;
@property (nonatomic, retain) NSString *genre;
@property (nonatomic, retain) NSString *year;
@property (nonatomic, retain) NSNumber *approxDuration;
@property (nonatomic, retain) NSString *composer;
@property (nonatomic, retain) NSString *tempo;
@property (nonatomic, retain) NSString *keySignature;
@property (nonatomic, retain) NSString *timeSignature;
@property (nonatomic, retain) NSString *lyricist;
@property (nonatomic, retain) NSString *recordedDate;
@property (nonatomic, retain) NSString *comments;
@property (nonatomic, retain) NSString *copyright;
@property (nonatomic, retain) NSString *sourceEncoder;
@property (nonatomic, retain) NSString *encodingApplication;
@property (nonatomic, retain) NSString *bitRate;
@property (nonatomic, retain) NSStream *sourceBitRate;
@property (nonatomic, retain) NSString *channelLayout;
@property (nonatomic, retain) NSString *isrc;
@property (nonatomic, retain) NSString *subtitle;

@end

ID3TagParser.h

#import <Foundation/Foundation.h>
#import "ID3Tag.h"

@interface ID3Parser : NSObject {

}

- (ID3Tag*) parseAudioFileForID3Tag:(NSURL*) url;

@end

ID3TagParser.m

#import "ID3Parser.h"
#import <AudioToolbox/AudioToolbox.h>

@implementation ID3Parser

- (ID3Tag*) parseAudioFileForID3Tag:(NSURL*) url {
    if (url == nil) {
        return nil;
    }

    AudioFileID fileID  = nil;
    OSStatus err = noErr;

    err = AudioFileOpenURL( (CFURLRef) url, kAudioFileReadPermission, 0, &fileID );
    if( err != noErr ) {
        NSLog( @"AudioFileOpenURL failed" );
        return nil;
    } else {
        UInt32 id3DataSize = 0;
        char* rawID3Tag = NULL;

        //  Reads in the raw ID3 tag info
        err = AudioFileGetPropertyInfo(fileID, kAudioFilePropertyID3Tag, &id3DataSize, NULL);
        if(err != noErr) {
            return nil;
        }

        //  Allocate the raw tag data
        rawID3Tag = (char *) malloc(id3DataSize);

        if(rawID3Tag == NULL) {
            return nil;
        }

        err = AudioFileGetProperty(fileID, kAudioFilePropertyID3Tag, &id3DataSize, rawID3Tag);
        if(err != noErr) {
            return nil;
        }

        UInt32 id3TagSize = 0;
        UInt32 id3TagSizeLength = 0;
        err = AudioFormatGetProperty(kAudioFormatProperty_ID3TagSize, id3DataSize, rawID3Tag, &id3TagSizeLength, &id3TagSize);

        if(err != noErr) {
            switch(err) {
                case kAudioFormatUnspecifiedError:
                    NSLog(@"err: audio format unspecified error");
                    return nil;
                case kAudioFormatUnsupportedPropertyError:
                    NSLog(@"err: audio format unsupported property error");
                    return nil;
                case kAudioFormatBadPropertySizeError:
                    NSLog(@"err: audio format bad property size error"); 
                    return nil;
                case kAudioFormatBadSpecifierSizeError:
                    NSLog(@"err: audio format bad specifier size error"); 
                    return nil;
                case kAudioFormatUnsupportedDataFormatError:
                    NSLog(@"err: audio format unsupported data format error"); 
                    return nil;
                case kAudioFormatUnknownFormatError:
                    NSLog(@"err: audio format unknown format error");
                    return nil;
                default:
                    NSLog(@"err: some other audio format error"); 
                    return nil;
            }
        }

        CFDictionaryRef piDict = nil;
        UInt32 piDataSize = sizeof(piDict);

        //  Populates a CFDictionary with the ID3 tag properties
        err = AudioFileGetProperty(fileID, kAudioFilePropertyInfoDictionary, &piDataSize, &piDict);
        if(err != noErr) {
            NSLog(@"AudioFileGetProperty failed for property info dictionary");
            return nil;
        }

        //  Toll free bridge the CFDictionary so that we can interact with it via objc
        NSDictionary* nsDict = (NSDictionary*)piDict;

        ID3Tag* tag = [[[ID3Tag alloc] init] autorelease];

        tag.album = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Album]];
        tag.approxDuration = [NSNumber numberWithInt:[[nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_ApproximateDurationInSeconds]] intValue]];
        tag.artist = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Artist]];
        tag.bitRate = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_NominalBitRate]];
        tag.channelLayout = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_ChannelLayout]];
        tag.comments = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Comments]];
        tag.composer = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Composer]];
        tag.copyright = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Copyright]];
        tag.encodingApplication = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_EncodingApplication]];
        tag.genre = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Genre]];
        tag.isrc = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_ISRC]];
        tag.keySignature = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_KeySignature]];
        tag.lyricist = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Lyricist]];
        tag.recordedDate = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_RecordedDate]];
        tag.sourceBitRate = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_SourceBitDepth]];
        tag.sourceEncoder = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_SourceEncoder]];
        tag.subtitle = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_SubTitle]];
        tag.tempo = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Tempo]];
        tag.timeSignature = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_TimeSignature]];
        tag.title = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Title]];
        tag.year = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_Year]];

        /*  
         *  We're going to parse tracks differently so that we can perform queries on the data. This means we need to look
         *  for a '/' so that we can seperate out the track from the total tracks on the source compilation (if it's there).
         */
        NSString* tracks = [nsDict objectForKey:[NSString stringWithUTF8String: kAFInfoDictionary_TrackNumber]];

        int slashLocation = [tracks rangeOfString:@"/"].location;

        if (slashLocation == NSNotFound) {
            tag.trackNumber = [NSNumber numberWithInt:[tracks intValue]];
        } else {
            tag.trackNumber = [NSNumber numberWithInt:[[tracks substringToIndex:slashLocation] intValue]];
            tag.totalTracks = [NSNumber numberWithInt:[[tracks substringFromIndex:(slashLocation+1 < [tracks length] ? slashLocation+1 : 0 )] intValue]];
        }

        //  ALWAYS CLEAN UP!
        CFRelease(piDict);
        nsDict = nil;
        free(rawID3Tag);

        return tag;
    }
}

@end
6 голосов
/ 05 июля 2013

Я вижу, что код чтения тега Сообщения Уэйна слишком много копируется.Если вы используете kAudioFilePropertyInfoDictionary, вам не нужен id3DataSize, не нужно читать необработанные данные ID3Tag, и id3TagSizeLength id3TagSize также не используются.Просто откройте файл и прочитайте тег:

- (NSDictionary *)id3TagsForURL:(NSURL *)resourceUrl
{
    AudioFileID fileID;
    OSStatus result = AudioFileOpenURL((CFURLRef)resourceUrl, kAudioFileReadPermission, 0, &fileID);

    if (result != noErr) {
        NSLog(@"Error reading tags: %li", result);
        return nil;
    }

    CFDictionaryRef piDict = nil;
    UInt32 piDataSize = sizeof(piDict);

    result = AudioFileGetProperty(fileID, kAudioFilePropertyInfoDictionary, &piDataSize, &piDict);
    if (result != noErr)
        NSLog(@"Error reading tags. AudioFileGetProperty failed");

    AudioFileClose(fileID);

    NSDictionary *tagsDictionary = [NSDictionary dictionaryWithDictionary:(NSDictionary*)piDict];
    CFRelease(piDict);

    return tagsDictionary;
}

Ключи словаря определены в AudioFile.h, начиная с kAFInfoDictionary.

Однако есть еще один ключ чтения тегов, который вернетразные результаты (с другими ключами):

- (NSDictionary *)id3TagsForURL:(NSURL *)resourceUrl
{
    AudioFileID fileID;
    OSStatus result = AudioFileOpenURL((CFURLRef)resourceUrl, kAudioFileReadPermission, 0, &fileID);

    if (result != noErr) {
        return nil;
    }

    //read raw ID3Tag size
    UInt32 id3DataSize = 0;
    char *rawID3Tag = NULL;
    result = AudioFileGetPropertyInfo(fileID, kAudioFilePropertyID3Tag, &id3DataSize, NULL);
    if (result != noErr) {
        AudioFileClose(fileID);
        return nil;
    }

    rawID3Tag = (char *)malloc(id3DataSize);

    //read raw ID3Tag
    result = AudioFileGetProperty(fileID, kAudioFilePropertyID3Tag, &id3DataSize, rawID3Tag);
    if (result != noErr) {
        free(rawID3Tag);
        AudioFileClose(fileID);
        return nil;
    }

    CFDictionaryRef piDict = nil;
    UInt32 piDataSize = sizeof(piDict);

    //this key returns some other dictionary, which works also in iPod library
    result = AudioFormatGetProperty(kAudioFormatProperty_ID3TagToDictionary, id3DataSize, rawID3Tag, &piDataSize, &piDict);
    if (result != noErr) {
        return nil;
    }

    free(rawID3Tag);
    AudioFileClose(fileID);

    NSDictionary *tagsDictionary = [NSDictionary dictionaryWithDictionary:(NSDictionary*)piDict];
    CFRelease(piDict);

    return tagsDictionary;
}
...