Skip to content

Instantly share code, notes, and snippets.

@iluvcapra
Created September 6, 2013 07:05
Show Gist options
  • Save iluvcapra/6460455 to your computer and use it in GitHub Desktop.
Save iluvcapra/6460455 to your computer and use it in GitHub Desktop.
//
// 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