Created
September 6, 2013 07:05
-
-
Save iluvcapra/6460455 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MUZID3Writer.m | |
// Metadata Machinegun | |
// | |
// Created by Jamie Hardt on 8/13/13. | |
// Copyright (c) 2013 Jamie Hardt. All rights reserved. | |
// | |
#import "MUZID3Writer_v2_3.h" | |
@implementation JHRangeSpecifier | |
@end | |
@implementation ID3InvolvedPeopleList { | |
NSMutableArray *_elems; | |
} | |
-(void)addPerson:(NSString *)name role:(NSString *)role { | |
[_elems addObject:name]; | |
[_elems addObject:role]; | |
} | |
@end | |
NSString *ID3_Comments = @"COMM"; // lang-text | |
NSString *ID3_InvolvedPeopleList = @"IPLS"; // text; null-terminated list | |
NSString *ID3_AlbumName = @"TALB"; // text; Album/movie/show name | |
NSString *ID3_BPM = @"TBPM"; // text; integer str | |
NSString *ID3_Composer = @"TCOM"; // text; slash-delimited | |
NSString *ID3_CopyrightMessage = @"TCOP"; // text; "YYYY " <text> | |
NSString *ID3_Date = @"TDAT"; // text; MMDD | |
NSString *ID3_EncodedBy = @"TENC"; // text | |
NSString *ID3_Lyricist = @"TEXT"; // text; slash-delimited | |
NSString *ID3_Time = @"TIME"; // text; HHMM | |
NSString *ID3_ContentGroupDescription = @"TIT1";// text | |
NSString *ID3_SongName = @"TIT2"; // text | |
NSString *ID3_Subtitle = @"TIT3"; // text | |
NSString *ID3_InitialKey = @"TKEY"; // key "F#", "Bb", "o", "Bm" | |
NSString *ID3_Languages = @"TLAN"; // text | |
NSString *ID3_OriginalAlbum = @"TOAL"; // text | |
NSString *ID3_OriginalFilename = @"TOFN"; // text | |
NSString *ID3_OriginalLyricist = @"TOLY"; // slash-delimited | |
NSString *ID3_OriginalArtist = @"TOPE"; // slash-delimited | |
NSString *ID3_OriginalReleaseYear = @"TORY"; // text; YYYY | |
NSString *ID3_FileOwner = @"TOWN"; // text | |
NSString *ID3_LeadPerformer = @"TPE1"; // slash-delimited; | |
NSString *ID3_BandOrchestraAccompaniment =@"TPE2"; // text | |
NSString *ID3_Conductor = @"TPE3"; // text | |
NSString *ID3_InterpreterRemixer = @"TPE4"; // text | |
NSString *ID3_PartOfASet = @"TPOS"; // text; "x/of" | |
NSString *ID3_Publisher = @"TPUB"; // text | |
NSString *ID3_TrackNumber = @"TRCK"; // text "x/of" | |
NSString *ID3_RecordingDates = @"TRDA"; // text | |
NSString *ID3_InternetRadioStationName = @"TRSN"; // text | |
NSString *ID3_InternetRadioStationOwner = @"TRSO"; // text | |
NSString *ID3_Isrc = @"TSRC"; // text; chr[12] | |
NSString *ID3_Year = @"TYER"; // text; YYYY | |
NSString *ID3_TermsOfUse = @"USER"; // lang-text | |
NSString *ID3_UnsynchronizedLyrics = @"USLT"; // lang-description-text | |
NSString *ID3_CopyrightLegalInformation = @"WCOP"; // url | |
NSString *ID3_OfficialFileWebPage =@"WOAF"; // url | |
NSString *ID3_OfficialArtistWebPage = @"WOAR"; // url | |
NSString *ID3_OfficialAudioSourceWebPage = @"WOAS"; // url | |
NSString *ID3_OfficialInternetRadioStationSourceWebPage = @"WORS"; //url | |
NSString *ID3_PublishersWebPage = @"WPUB"; // url | |
enum { | |
ID3Frame_DiscardIfTagAltered = 1, | |
ID3Frame_DiscardIfAudioAltered = 1 << 1, | |
ID3Frame_ReadOnly = 1 << 2, | |
ID3Frame_CompressionUsed = 1 << 8, | |
ID3Frame_EncryptionUsed = 1 << 9, | |
ID3Frame_GroupingIdentity = 1 << 10 | |
} ID3_FrameFlags; | |
enum { | |
ID3HeaderFlag_UnsychronizationUsed = 1 << 7, | |
ID3HeaderFlag_ExtendedHeaderPresent = 1 << 6, | |
ID3HeaderFlag_Experimental = 1 << 5, | |
} MUZID3Writer_HeaderFlags; | |
uint32_t SynchSafe32bitInt(uint32_t input) { | |
uint32_t retVal = 0; | |
retVal |= ((input & 0xFE00000) << 3); | |
retVal |= ((input & 0x1FC000) << 2); | |
retVal |= ((input & 0x3F80) << 1); | |
retVal |= ((input & 0x7F)); | |
return CFSwapInt32HostToBig(retVal); | |
} | |
@implementation MUZID3Writer_v2_3 | |
-(uint8_t)_headerFlags { | |
uint8_t header = 0; | |
return header; | |
} | |
-(uint32_t)_tagSizeForHeader { | |
return (uint32_t)( | |
[[self _framesData] length]); | |
} | |
-(NSData *)_headerData { | |
NSMutableData *retData = [NSMutableData data]; | |
uint8_t versBytes[] = {0x03, 0x00}; | |
[retData appendBytes:"ID3" length:3]; | |
[retData appendBytes:versBytes length:2]; | |
uint8_t flags = [self _headerFlags]; | |
[retData appendBytes:&flags length:1]; | |
uint32_t tagSize = SynchSafe32bitInt([self _tagSizeForHeader]); | |
[retData appendBytes:&tagSize length:sizeof(uint32_t)]; | |
return [NSData dataWithData:retData]; | |
} | |
#pragma Primitive Frame Methods | |
-(NSData *)_tagFrameForData:(NSString *)fourcc | |
flags:(uint16_t)flags | |
data:(NSData *)data { | |
NSMutableData *retData = [NSMutableData data]; | |
uint32_t size = CFSwapInt32HostToBig((uint32_t)[data length]); | |
[retData appendData:[fourcc dataUsingEncoding:NSASCIIStringEncoding]]; | |
[retData appendBytes:&size length:4]; | |
[retData appendBytes:&flags length:2]; | |
[retData appendData:data]; | |
return [NSData dataWithData:retData]; | |
} | |
-(NSData *)_tagFrameForTextTag:(NSString *)fourcc | |
flags:(uint16_t)flags | |
stringValue:(NSString *)val { | |
static uint8_t ID3_Unicode16TagEncoding = 0x01; | |
NSMutableData *retData = [NSMutableData data]; | |
[retData appendBytes:&ID3_Unicode16TagEncoding length:1]; | |
[retData appendData:[val dataUsingEncoding:NSUTF16BigEndianStringEncoding]]; | |
return [self _tagFrameForData:fourcc | |
flags:flags | |
data:retData]; | |
} | |
-(NSData *)_tagFrameForLanguageTextTag:(NSString *)fourcc | |
flags:(uint16_t)flags | |
stringValue:(NSString *)str { | |
} | |
-(NSData *)_tagFrameForArrayTag:(NSString *)fourcc | |
flags:(uint16_t)flags | |
arrayValue:(NSArray *)array { | |
NSMutableArray *cleanedVals = [NSMutableArray array]; | |
for (NSString *val in array) { | |
[cleanedVals addObject:[val stringByReplacingOccurrencesOfString:@"/" | |
withString:@"_"]]; | |
} | |
NSString *strVal = [cleanedVals componentsJoinedByString:@"/"]; | |
return [self _tagFrameForTextTag:fourcc | |
flags:flags | |
stringValue:strVal]; | |
} | |
-(NSData *)_tagFrameForURLTag:(NSString *)fourcc | |
flags:(uint16_t)flags | |
url:(NSURL *)url { | |
NSMutableData *retData = [NSMutableData data]; | |
[retData appendData:[[url absoluteString] | |
dataUsingEncoding:NSISOLatin1StringEncoding]]; | |
return [self _tagFrameForData:fourcc | |
flags:flags | |
data:retData]; | |
} | |
-(NSData *)_tagFrameForRangeSpecifier:(NSString *)fourcc | |
flags:(uint16_t)flags | |
rangeSpecifier:(JHRangeSpecifier *)spec { | |
NSString *strVal; | |
if (spec.range > 0) { | |
strVal = [NSString stringWithFormat:@"%ld/%ld", | |
spec.index, spec.range]; | |
} else { | |
strVal = [NSString stringWithFormat:@"%ld",spec.index]; | |
} | |
return [self _tagFrameForTextTag:fourcc | |
flags:flags | |
stringValue:strVal]; | |
} | |
-(NSData *)_extendedHeaderData { | |
return [NSData data]; | |
} | |
#define TEXT_FRAME( obj_key_name , tag_str ) \ | |
if ( obj_key_name ) {\ | |
return [self _tagFrameForTextTag: tag_str \ | |
flags:0 \ | |
stringValue: obj_key_name ];\ | |
} else { return [NSData data]; } | |
-(NSData *)_frameForISRC { | |
TEXT_FRAME(self.isrc, ID3_Isrc); | |
} | |
-(NSData *)_frameForYear { | |
if (self.date && self.date.year != NSUndefinedDateComponent) { | |
return [self _tagFrameForTextTag:ID3_Year | |
flags:0 | |
stringValue:[NSString stringWithFormat:@"%ld",self.date.year] | |
]; | |
} else { | |
return [NSData data]; | |
} | |
} | |
-(NSData *)_frameForDate { | |
if (self.date && | |
self.date.month != NSUndefinedDateComponent && | |
self.date.day != NSUndefinedDateComponent) { | |
return [self _tagFrameForTextTag:ID3_Date | |
flags:0 | |
stringValue:[NSString stringWithFormat:@"%02ld%02ld", | |
self.date.month,self.date.day] | |
]; | |
} else { | |
return [NSData data]; | |
} | |
} | |
-(NSData *)_frameForTime { | |
if (self.date && | |
self.date.hour != NSUndefinedDateComponent && | |
self.date.minute != NSUndefinedDateComponent) { | |
return [self _tagFrameForTextTag:ID3_Time | |
flags:0 | |
stringValue:[NSString stringWithFormat:@"%02ld%02ld", | |
self.date.hour,self.date.minute] | |
]; | |
} else { | |
return [NSData data]; | |
} | |
} | |
-(NSData *)_frameForUnsynchronizedLyrics { | |
NSString *lyricDescriptor = @""; | |
} | |
-(NSData *)_frameForSongName { | |
TEXT_FRAME(self.songName, ID3_SongName); | |
} | |
-(NSData *)_frameForAlbumName { | |
TEXT_FRAME(self.albumName, ID3_AlbumName); | |
} | |
-(NSData *)_frameForTrackNumber { | |
if (self.trackNumber) { | |
return [self _tagFrameForRangeSpecifier:ID3_TrackNumber | |
flags:0 | |
rangeSpecifier:self.trackNumber]; | |
} else { | |
return [NSData data]; | |
} | |
} | |
-(NSData *)_framesData { | |
NSMutableData *retObj = [NSMutableData data]; | |
[retObj appendData:[self _frameForISRC]]; | |
[retObj appendData:[self _frameForYear]]; | |
[retObj appendData:[self _frameForDate]]; | |
[retObj appendData:[self _frameForTime]]; | |
[retObj appendData:[self _frameForSongName]]; | |
[retObj appendData:[self _frameForTrackNumber]]; | |
[retObj appendData:[self _frameForAlbumName]]; | |
return [NSData dataWithData:retObj]; | |
} | |
-(NSData *)id3Data { | |
NSMutableData *retData = [NSMutableData data]; | |
[retData appendData:[self _headerData]]; | |
[retData appendData:[self _extendedHeaderData]]; | |
[retData appendData:[self _framesData]]; | |
return [NSData dataWithData:retData]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment