diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index f1df159ad..59dbb48e4 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -163,7 +163,10 @@ if(amountInBuffer < CHUNK_SIZE) { int framesToRead = CHUNK_SIZE - amountInBuffer; - int framesRead = [decoder readAudio:((char *)inputBuffer) + bytesInBuffer frames:framesToRead]; + int framesRead; + @autoreleasepool { + framesRead = [decoder readAudio:((char *)inputBuffer) + bytesInBuffer frames:framesToRead]; + } if(framesRead > 0 && !seekError) { amountInBuffer += framesRead; diff --git a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj index 502ba8060..87a3be420 100644 --- a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj +++ b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj @@ -66,8 +66,37 @@ 17B5E1DE0CC074D3004E2AF4 /* window.c in Sources */ = {isa = PBXBuildFile; fileRef = 17B5E1970CC074D3004E2AF4 /* window.c */; }; 17B5E2C00CC07904004E2AF4 /* stream_encoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 17B5E1950CC074D3004E2AF4 /* stream_encoder.c */; }; 17B5E2C10CC07905004E2AF4 /* stream_encoder_framing.c in Sources */ = {isa = PBXBuildFile; fileRef = 17B5E1960CC074D3004E2AF4 /* stream_encoder_framing.c */; }; + 8356BD0527B3A4C20074E50C /* ogg_mapping.c in Sources */ = {isa = PBXBuildFile; fileRef = 8356BD0127B3A4C20074E50C /* ogg_mapping.c */; }; + 8356BD0627B3A4C20074E50C /* ogg_encoder_aspect.c in Sources */ = {isa = PBXBuildFile; fileRef = 8356BD0227B3A4C20074E50C /* ogg_encoder_aspect.c */; }; + 8356BD0727B3A4C20074E50C /* ogg_decoder_aspect.c in Sources */ = {isa = PBXBuildFile; fileRef = 8356BD0327B3A4C20074E50C /* ogg_decoder_aspect.c */; }; + 8356BD0827B3A4C20074E50C /* ogg_helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 8356BD0427B3A4C20074E50C /* ogg_helper.c */; }; + 8356BD1327B3A4E20074E50C /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8356BD0F27B3A4D90074E50C /* Ogg.framework */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 8356BD0E27B3A4D90074E50C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8356BD0927B3A4D90074E50C /* Ogg.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8D07F2C80486CC7A007CD1D0; + remoteInfo = Ogg; + }; + 8356BD1027B3A4D90074E50C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8356BD0927B3A4D90074E50C /* Ogg.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 734FB2E50B18B33E00D561D7; + remoteInfo = "libogg (static)"; + }; + 8356BD1427B3A4E70074E50C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8356BD0927B3A4D90074E50C /* Ogg.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0; + remoteInfo = Ogg; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; @@ -131,6 +160,11 @@ 17B5E1950CC074D3004E2AF4 /* stream_encoder.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = stream_encoder.c; sourceTree = ""; }; 17B5E1960CC074D3004E2AF4 /* stream_encoder_framing.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = stream_encoder_framing.c; sourceTree = ""; }; 17B5E1970CC074D3004E2AF4 /* window.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = window.c; sourceTree = ""; }; + 8356BD0127B3A4C20074E50C /* ogg_mapping.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_mapping.c; sourceTree = ""; }; + 8356BD0227B3A4C20074E50C /* ogg_encoder_aspect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_encoder_aspect.c; sourceTree = ""; }; + 8356BD0327B3A4C20074E50C /* ogg_decoder_aspect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_decoder_aspect.c; sourceTree = ""; }; + 8356BD0427B3A4C20074E50C /* ogg_helper.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ogg_helper.c; sourceTree = ""; }; + 8356BD0927B3A4D90074E50C /* Ogg.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Ogg.xcodeproj; path = ../Ogg/macosx/Ogg.xcodeproj; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* FLAC.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FLAC.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -141,6 +175,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8356BD1327B3A4E20074E50C /* Ogg.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -163,6 +198,7 @@ 089C1665FE841158C02AAC07 /* Resources */, 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, 034768DFFF38A50411DB9C8B /* Products */, + 8356BD1227B3A4E20074E50C /* Frameworks */, ); name = flac; sourceTree = ""; @@ -195,6 +231,7 @@ 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { isa = PBXGroup; children = ( + 8356BD0927B3A4D90074E50C /* Ogg.xcodeproj */, 0867D6A5FE840307C02AAC07 /* AppKit.framework */, D2F7E79907B2D74100F64583 /* CoreData.framework */, 0867D69BFE84028FC02AAC07 /* Foundation.framework */, @@ -280,9 +317,13 @@ 17B5E1880CC074D3004E2AF4 /* memory.c */, 17B5E1890CC074D3004E2AF4 /* metadata_iterators.c */, 17B5E18A0CC074D3004E2AF4 /* metadata_object.c */, + 8356BD0327B3A4C20074E50C /* ogg_decoder_aspect.c */, + 8356BD0227B3A4C20074E50C /* ogg_encoder_aspect.c */, + 8356BD0427B3A4C20074E50C /* ogg_helper.c */, + 8356BD0127B3A4C20074E50C /* ogg_mapping.c */, 17B5E1940CC074D3004E2AF4 /* stream_decoder.c */, - 17B5E1950CC074D3004E2AF4 /* stream_encoder.c */, 17B5E1960CC074D3004E2AF4 /* stream_encoder_framing.c */, + 17B5E1950CC074D3004E2AF4 /* stream_encoder.c */, 17B5E1970CC074D3004E2AF4 /* window.c */, ); path = libFLAC; @@ -333,6 +374,22 @@ path = protected; sourceTree = ""; }; + 8356BD0A27B3A4D90074E50C /* Products */ = { + isa = PBXGroup; + children = ( + 8356BD0F27B3A4D90074E50C /* Ogg.framework */, + 8356BD1127B3A4D90074E50C /* libogg.a */, + ); + name = Products; + sourceTree = ""; + }; + 8356BD1227B3A4E20074E50C /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -400,6 +457,7 @@ buildRules = ( ); dependencies = ( + 8356BD1527B3A4E70074E50C /* PBXTargetDependency */, ); name = "FLAC Framework"; productInstallPath = "$(HOME)/Library/Frameworks"; @@ -432,6 +490,12 @@ mainGroup = 0867D691FE84028FC02AAC07 /* flac */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 8356BD0A27B3A4D90074E50C /* Products */; + ProjectRef = 8356BD0927B3A4D90074E50C /* Ogg.xcodeproj */; + }, + ); projectRoot = ""; targets = ( 8DC2EF4F0486A6940098B216 /* FLAC Framework */, @@ -439,6 +503,23 @@ }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + 8356BD0F27B3A4D90074E50C /* Ogg.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Ogg.framework; + remoteRef = 8356BD0E27B3A4D90074E50C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8356BD1127B3A4D90074E50C /* libogg.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libogg.a; + remoteRef = 8356BD1027B3A4D90074E50C /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ 8DC2EF520486A6940098B216 /* Resources */ = { isa = PBXResourcesBuildPhase; @@ -455,6 +536,7 @@ buildActionMask = 2147483647; files = ( 17B5E1AC0CC074D3004E2AF4 /* bitmath.c in Sources */, + 8356BD0827B3A4C20074E50C /* ogg_helper.c in Sources */, 17B5E1AD0CC074D3004E2AF4 /* bitreader.c in Sources */, 17B5E1AE0CC074D3004E2AF4 /* bitwriter.c in Sources */, 17B5E1AF0CC074D3004E2AF4 /* cpu.c in Sources */, @@ -464,9 +546,12 @@ 17B5E1B30CC074D3004E2AF4 /* format.c in Sources */, 17B5E1D00CC074D3004E2AF4 /* lpc.c in Sources */, 17B5E1D10CC074D3004E2AF4 /* md5.c in Sources */, + 8356BD0727B3A4C20074E50C /* ogg_decoder_aspect.c in Sources */, 17B5E1D20CC074D3004E2AF4 /* memory.c in Sources */, 17B5E1D30CC074D3004E2AF4 /* metadata_iterators.c in Sources */, + 8356BD0527B3A4C20074E50C /* ogg_mapping.c in Sources */, 17B5E1D40CC074D3004E2AF4 /* metadata_object.c in Sources */, + 8356BD0627B3A4C20074E50C /* ogg_encoder_aspect.c in Sources */, 17B5E1DB0CC074D3004E2AF4 /* stream_decoder.c in Sources */, 17B5E1DE0CC074D3004E2AF4 /* window.c in Sources */, 17B5E2C00CC07904004E2AF4 /* stream_encoder.c in Sources */, @@ -476,6 +561,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 8356BD1527B3A4E70074E50C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Ogg; + targetProxy = 8356BD1427B3A4E70074E50C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; @@ -488,6 +581,7 @@ DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_SYMBOL_SEPARATION = YES; @@ -508,7 +602,7 @@ "$(OTHER_CFLAGS_QUOTED_1)", "-D__MACOSX__", "-DHAVE_LROUND", - "-DFLAC__HAS_OGG=0", + "-DFLAC__HAS_OGG=1", ); OTHER_CFLAGS_QUOTED_1 = "-DVERSION=\\\"1.3.3\\\" -DPACKAGE_VERSION=\\\"1.3.3\\\""; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; @@ -535,6 +629,7 @@ DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_VERSION = A; GCC_ENABLE_SYMBOL_SEPARATION = YES; GCC_MODEL_TUNING = G5; @@ -553,7 +648,7 @@ "$(OTHER_CFLAGS_QUOTED_1)", "-D__MACOSX__", "-DHAVE_LROUND", - "-DFLAC__HAS_OGG=0", + "-DFLAC__HAS_OGG=1", ); OTHER_CFLAGS_QUOTED_1 = "-DVERSION=\\\"1.3.3\\\" -DPACKAGE_VERSION=\\\"1.3.3\\\""; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; diff --git a/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj b/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj index d9ab5503d..9a87e84ad 100644 --- a/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj +++ b/Plugins/FFMPEG/FFMPEG.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 8356BCE727B37C6F0074E50C /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Merge.m"; path = "../../Utils/NSDictionary+Merge.m"; sourceTree = ""; }; 8356BCE827B37C6F0074E50C /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+Merge.h"; path = "../../Utils/NSDictionary+Merge.h"; sourceTree = ""; }; 8356BCEA27B37DA40074E50C /* TagLibID3v2Reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TagLibID3v2Reader.h; path = ../TagLib/TagLibID3v2Reader.h; sourceTree = ""; }; + 8356BD1A27B3D06F0074E50C /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPSource.h; path = ../HTTPSource/HTTPSource.h; sourceTree = ""; }; 8384913818081F6C00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 83AA7D08279EBCC600087AA4 /* libavcodec.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.59.dylib; path = ../../ThirdParty/ffmpeg/lib/libavcodec.59.dylib; sourceTree = ""; }; 83AA7D09279EBCC600087AA4 /* libavutil.57.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavutil.57.dylib; path = ../../ThirdParty/ffmpeg/lib/libavutil.57.dylib; sourceTree = ""; }; @@ -139,6 +140,7 @@ 08FB77AFFE84173DC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( + 8356BD1A27B3D06F0074E50C /* HTTPSource.h */, 8356BCEA27B37DA40074E50C /* TagLibID3v2Reader.h */, 8356BCE827B37C6F0074E50C /* NSDictionary+Merge.h */, 8356BCE727B37C6F0074E50C /* NSDictionary+Merge.m */, diff --git a/Plugins/FFMPEG/FFMPEGDecoder.h b/Plugins/FFMPEG/FFMPEGDecoder.h index a604c6a53..0eb064221 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.h +++ b/Plugins/FFMPEG/FFMPEGDecoder.h @@ -42,6 +42,7 @@ BOOL endOfAudio; int metadataIndex; + NSString *genre; NSString *artist; NSString *title; NSString *album; diff --git a/Plugins/FFMPEG/FFMPEGDecoder.m b/Plugins/FFMPEG/FFMPEGDecoder.m index 0e6f79521..2bad0e86b 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.m +++ b/Plugins/FFMPEG/FFMPEGDecoder.m @@ -16,6 +16,8 @@ #import "Logging.h" +#import "HTTPSource.h" + #define ST_BUFF 2048 int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size) { @@ -82,8 +84,9 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { // register all available codecs NSURL *url = [s url]; - if([[url scheme] isEqualToString:@"http"] || - [[url scheme] isEqualToString:@"https"]) { + if(([[url scheme] isEqualToString:@"http"] || + [[url scheme] isEqualToString:@"https"]) && + [[url pathExtension] isEqualToString:@"m3u8"]) { source = nil; [s close]; @@ -95,20 +98,13 @@ 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, &dict)) < 0) { - av_dict_free(&dict); + if((errcode = avformat_open_input(&formatCtx, [urlString UTF8String], NULL, NULL)) < 0) { 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) { @@ -415,6 +411,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { seekable = [s seekable]; + genre = @""; album = @""; artist = @""; title = @""; @@ -465,6 +462,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { - (void)updateMetadata { const AVDictionaryEntry *tag = NULL; + NSString *_genre = genre; NSString *_album = album; NSString *_artist = artist; NSString *_title = title; @@ -481,6 +479,8 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { } } else if(!strcasecmp(tag->key, "icy-url")) { _album = [NSString stringWithUTF8String:tag->value]; + } else if(!strcasecmp(tag->key, "icy-genre")) { + _genre = [NSString stringWithUTF8String:tag->value]; } else if(!strcasecmp(tag->key, "artist")) { _artist = [NSString stringWithUTF8String:tag->value]; } else if(!strcasecmp(tag->key, "title")) { @@ -489,9 +489,23 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { } } - if(![_album isEqual:album] || + Class sourceClass = [source class]; + if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) { + HTTPSource *httpSource = (HTTPSource *)source; + if([httpSource hasMetadata]) { + NSDictionary *metadata = [httpSource metadata]; + _genre = [metadata valueForKey:@"genre"]; + _album = [metadata valueForKey:@"album"]; + _artist = [metadata valueForKey:@"artist"]; + _title = [metadata valueForKey:@"title"]; + } + } + + if(![_genre isEqual:genre] || + ![_album isEqual:album] || ![_artist isEqual:artist] || ![_title isEqual:title]) { + genre = _genre; album = _album; artist = _artist; title = _title; @@ -725,7 +739,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { } - (NSDictionary *)metadata { - return [NSDictionary dictionaryByMerging:@{ @"album": album, @"artist": artist, @"title": title } with:id3Metadata]; + return [NSDictionary dictionaryByMerging:@{ @"genre": genre, @"album": album, @"artist": artist, @"title": title } with:id3Metadata]; } + (NSArray *)fileTypes { diff --git a/Plugins/Flac/Flac.xcodeproj/project.pbxproj b/Plugins/Flac/Flac.xcodeproj/project.pbxproj index 61cb799dd..10f54ea2e 100644 --- a/Plugins/Flac/Flac.xcodeproj/project.pbxproj +++ b/Plugins/Flac/Flac.xcodeproj/project.pbxproj @@ -52,6 +52,7 @@ 17C93F040B8FF67A008627D6 /* FlacDecoder.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = FlacDecoder.m; sourceTree = ""; }; 17F5641A0C3BDC460019975C /* flac.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = flac.xcodeproj; path = ../../Frameworks/FLAC/flac.xcodeproj; sourceTree = SOURCE_ROOT; }; 32DBCF630370AF2F00C91783 /* Flac_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Flac_Prefix.pch; sourceTree = ""; }; + 8356BD1927B3CCBB0074E50C /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPSource.h; path = ../HTTPSource/HTTPSource.h; sourceTree = ""; }; 8384912D180816C900E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 8D5B49B6048680CD000E48DA /* Flac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Flac.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -103,6 +104,7 @@ 08FB77AFFE84173DC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( + 8356BD1927B3CCBB0074E50C /* HTTPSource.h */, 8384912D180816C900E7332D /* Logging.h */, 177FCFC10B90C9960011C3B5 /* Plugin.h */, 17C93F030B8FF67A008627D6 /* FlacDecoder.h */, @@ -274,6 +276,7 @@ GCC_PREFIX_HEADER = Flac_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.flac; PRODUCT_NAME = Flac; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -301,6 +304,7 @@ GCC_PREFIX_HEADER = Flac_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; + LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = org.cogx.flac; PRODUCT_NAME = Flac; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Plugins/Flac/FlacDecoder.h b/Plugins/Flac/FlacDecoder.h index d7dc4f457..1e660b0df 100644 --- a/Plugins/Flac/FlacDecoder.h +++ b/Plugins/Flac/FlacDecoder.h @@ -34,6 +34,12 @@ BOOL hasStreamInfo; BOOL streamOpened; + BOOL abortFlag; + + NSString *genre; + NSString *album; + NSString *artist; + NSString *title; } - (void)setSource:(id)s; diff --git a/Plugins/Flac/FlacDecoder.m b/Plugins/Flac/FlacDecoder.m index a260f4e95..4ec8e2119 100644 --- a/Plugins/Flac/FlacDecoder.m +++ b/Plugins/Flac/FlacDecoder.m @@ -10,30 +10,10 @@ #import "Logging.h" +#import "HTTPSource.h" + @implementation FlacDecoder -static const char *CHANNEL_MASK_TAG = "WAVEFORMATEXTENSIBLE_CHANNEL_MASK"; - -static FLAC__bool flac__utils_get_channel_mask_tag(const FLAC__StreamMetadata *object, FLAC__uint32 *channel_mask) { - int offset; - uint32_t val; - char *p; - FLAC__ASSERT(object); - FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); - if(0 > (offset = FLAC__metadata_object_vorbiscomment_find_entry_from(object, /*offset=*/0, CHANNEL_MASK_TAG))) - return false; - if(object->data.vorbis_comment.comments[offset].length < strlen(CHANNEL_MASK_TAG) + 4) - return false; - if(0 == (p = strchr((const char *)object->data.vorbis_comment.comments[offset].entry, '='))) /* should never happen, but just in case */ - return false; - if(strncasecmp(p, "=0x", 3)) - return false; - if(sscanf(p + 3, "%x", &val) != 1) - return false; - *channel_mask = val; - return true; -} - FLAC__StreamDecoderReadStatus ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte blockBuffer[], size_t *bytes, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; long bytesRead = [[flacDecoder source] read:blockBuffer amount:*bytes]; @@ -99,6 +79,9 @@ FLAC__StreamDecoderLengthStatus LengthCallback(const FLAC__StreamDecoder *decode FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const sampleblockBuffer[], void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; + if(flacDecoder->abortFlag) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + uint32_t channels = frame->header.channels; uint32_t bitsPerSample = frame->header.bits_per_sample; uint32_t frequency = frame->header.sample_rate; @@ -200,7 +183,7 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta // to determine stream format (this seems to be consistent with flac spec: http://flac.sourceforge.net/format.html) FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; - if(!flacDecoder->hasStreamInfo) { + if(!flacDecoder->hasStreamInfo && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { flacDecoder->channels = metadata->data.stream_info.channels; flacDecoder->channelConfig = 0; flacDecoder->frequency = metadata->data.stream_info.sample_rate; @@ -212,12 +195,59 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta } if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { - (void)flac__utils_get_channel_mask_tag(metadata, &flacDecoder->channelConfig); + NSString *_genre = flacDecoder->genre; + NSString *_album = flacDecoder->album; + NSString *_artist = flacDecoder->artist; + NSString *_title = flacDecoder->title; + uint8_t nullByte = '\0'; + const FLAC__StreamMetadata_VorbisComment *vorbis_comment = &metadata->data.vorbis_comment; + for(int i = 0; i < vorbis_comment->num_comments; ++i) { + NSMutableData *commentField = [NSMutableData dataWithBytes:vorbis_comment->comments[i].entry length:vorbis_comment->comments[i].length]; + [commentField appendBytes:&nullByte length:1]; + NSString *commentString = [NSString stringWithUTF8String:[commentField bytes]]; + NSArray *splitFields = [commentString componentsSeparatedByString:@"="]; + if([splitFields count] == 2) { + NSString *name = [splitFields objectAtIndex:0]; + NSString *value = [splitFields objectAtIndex:1]; + name = [name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + name = [name lowercaseString]; + if([name isEqualToString:@"genre"]) { + _genre = value; + } else if([name isEqualToString:@"album"]) { + _album = value; + } else if([name isEqualToString:@"artist"]) { + _artist = value; + } else if([name isEqualToString:@"title"]) { + _title = value; + } else if([name isEqualToString:@"waveformatextensible_channel_mask"]) { + if([value hasPrefix:@"0x"]) { + char *end; + const char *_value = [value UTF8String] + 2; + flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16); + } + } + } + } + + if(![_genre isEqual:flacDecoder->genre] || + ![_album isEqual:flacDecoder->album] || + ![_artist isEqual:flacDecoder->artist] || + ![_title isEqual:flacDecoder->title]) { + flacDecoder->genre = _genre; + flacDecoder->album = _album; + flacDecoder->artist = _artist; + flacDecoder->title = _title; + [flacDecoder willChangeValueForKey:@"metadata"]; + [flacDecoder didChangeValueForKey:@"metadata"]; + } } } void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { - // Do nothing? + FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; + if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) + flacDecoder->abortFlag = YES; } - (BOOL)open:(id)s { @@ -230,20 +260,61 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS [s seek:0 whence:SEEK_SET]; } + // Must peek at stream! HTTP reader supports seeking within its buffer + BOOL isOggFlac = NO; + uint8_t buffer[4]; + [s read:buffer amount:4]; + [s seek:0 whence:SEEK_SET]; + if(memcmp(buffer, "OggS", 4) == 0) { + isOggFlac = YES; + } + + genre = @""; + album = @""; + artist = @""; + title = @""; + decoder = FLAC__stream_decoder_new(); if(decoder == NULL) return NO; - if(FLAC__stream_decoder_init_stream(decoder, - ReadCallback, - ([source seekable] ? SeekCallback : NULL), - ([source seekable] ? TellCallback : NULL), - ([source seekable] ? LengthCallback : NULL), - ([source seekable] ? EOFCallback : NULL), - WriteCallback, - MetadataCallback, - ErrorCallback, - (__bridge void *)(self)) != FLAC__STREAM_DECODER_INIT_STATUS_OK) { + if(![source seekable]) { + FLAC__stream_decoder_set_md5_checking(decoder, false); + } + + FLAC__stream_decoder_set_metadata_ignore_all(decoder); + FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO); + FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT); + + abortFlag = NO; + + FLAC__StreamDecoderInitStatus ret; + + if(isOggFlac) { + ret = FLAC__stream_decoder_init_ogg_stream(decoder, + ReadCallback, + ([source seekable] ? SeekCallback : NULL), + ([source seekable] ? TellCallback : NULL), + ([source seekable] ? LengthCallback : NULL), + ([source seekable] ? EOFCallback : NULL), + WriteCallback, + MetadataCallback, + ErrorCallback, + (__bridge void *)(self)); + } else { + ret = FLAC__stream_decoder_init_stream(decoder, + ReadCallback, + ([source seekable] ? SeekCallback : NULL), + ([source seekable] ? TellCallback : NULL), + ([source seekable] ? LengthCallback : NULL), + ([source seekable] ? EOFCallback : NULL), + WriteCallback, + MetadataCallback, + ErrorCallback, + (__bridge void *)(self)); + } + + if(ret != FLAC__STREAM_DECODER_INIT_STATUS_OK) { return NO; } @@ -271,7 +342,9 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS break; } - FLAC__stream_decoder_process_single(decoder); + if(!FLAC__stream_decoder_process_single(decoder)) { + break; + } } int bytesPerFrame = ((bitsPerSample + 7) / 8) * channels; @@ -292,6 +365,29 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS } } + Class sourceClass = [source class]; + if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) { + HTTPSource *httpSource = (HTTPSource *)source; + if([httpSource hasMetadata]) { + NSDictionary *metadata = [httpSource metadata]; + NSString *_genre = [metadata valueForKey:@"genre"]; + NSString *_album = [metadata valueForKey:@"album"]; + NSString *_artist = [metadata valueForKey:@"artist"]; + NSString *_title = [metadata valueForKey:@"title"]; + if(![_genre isEqualToString:genre] || + ![_album isEqualToString:album] || + ![_artist isEqualToString:artist] || + ![_title isEqualToString:title]) { + genre = _genre; + album = _album; + artist = _artist; + title = _title; + [self willChangeValueForKey:@"metadata"]; + [self didChangeValueForKey:@"metadata"]; + } + } + } + return framesRead; } @@ -367,7 +463,7 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS } - (NSDictionary *)metadata { - return @{}; + return @{ @"genre": genre, @"album": album, @"artist": artist, @"title": title }; } + (NSArray *)fileTypes { @@ -375,7 +471,7 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS } + (NSArray *)mimeTypes { - return @[@"audio/x-flac"]; + return @[@"audio/x-flac", @"application/ogg", @"audio/ogg"]; } + (float)priority { diff --git a/Plugins/Opus/Opus/OpusDecoder.h b/Plugins/Opus/Opus/OpusDecoder.h index 9db9c350c..df3b34346 100644 --- a/Plugins/Opus/Opus/OpusDecoder.h +++ b/Plugins/Opus/Opus/OpusDecoder.h @@ -29,6 +29,11 @@ int bitrate; int channels; long totalFrames; + + NSString *genre; + NSString *album; + NSString *artist; + NSString *title; } @end diff --git a/Plugins/Opus/Opus/OpusDecoder.m b/Plugins/Opus/Opus/OpusDecoder.m index 5b898a447..839085c11 100644 --- a/Plugins/Opus/Opus/OpusDecoder.m +++ b/Plugins/Opus/Opus/OpusDecoder.m @@ -12,6 +12,8 @@ #import "Logging.h" +#import "HTTPSource.h" + @implementation OpusFile static const int MAXCHANNELS = 8; @@ -115,9 +117,92 @@ opus_int64 sourceTell(void *_stream) { [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; + genre = @""; + album = @""; + artist = @""; + title = @""; + [self updateMetadata]; + return YES; } +- (void)updateMetadata { + const OpusTags *comment = op_tags(opusRef, -1); + + if(comment) { + uint8_t nullByte = '\0'; + NSString *_genre = genre; + NSString *_album = album; + NSString *_artist = artist; + NSString *_title = title; + for(int i = 0; i < comment->comments; ++i) { + NSMutableData *commentField = [NSMutableData dataWithBytes:comment->user_comments[i] length:comment->comment_lengths[i]]; + [commentField appendBytes:&nullByte length:1]; + NSString *commentString = [NSString stringWithUTF8String:[commentField bytes]]; + NSArray *splitFields = [commentString componentsSeparatedByString:@"="]; + if([splitFields count] == 2) { + NSString *name = [splitFields objectAtIndex:0]; + NSString *value = [splitFields objectAtIndex:1]; + name = [name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + name = [name lowercaseString]; + if([name isEqualToString:@"genre"]) { + _genre = value; + } else if([name isEqualToString:@"album"]) { + _album = value; + } else if([name isEqualToString:@"artist"]) { + _artist = value; + } else if([name isEqualToString:@"title"]) { + _title = value; + } + } + } + + if(![_genre isEqual:genre] || + ![_album isEqual:album] || + ![_artist isEqual:artist] || + ![_title isEqual:title]) { + genre = _genre; + album = _album; + artist = _artist; + title = _title; + [self willChangeValueForKey:@"metadata"]; + [self didChangeValueForKey:@"metadata"]; + } + } +} + +- (void)updateIcyMetadata { + NSString *_genre = genre; + NSString *_album = album; + NSString *_artist = artist; + NSString *_title = title; + + Class sourceClass = [source class]; + if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) { + HTTPSource *httpSource = (HTTPSource *)source; + if([httpSource hasMetadata]) { + NSDictionary *metadata = [httpSource metadata]; + _genre = [metadata valueForKey:@"genre"]; + _album = [metadata valueForKey:@"album"]; + _artist = [metadata valueForKey:@"artist"]; + _title = [metadata valueForKey:@"title"]; + } + } + + if(![_genre isEqual:genre] || + ![_album isEqual:album] || + ![_artist isEqual:artist] || + ![_title isEqual:title]) { + genre = _genre; + album = _album; + artist = _artist; + title = _title; + [self willChangeValueForKey:@"metadata"]; + [self didChangeValueForKey:@"metadata"]; + } +} + - (int)readAudio:(void *)buf frames:(UInt32)frames { int numread; int total = 0; @@ -128,6 +213,8 @@ opus_int64 sourceTell(void *_stream) { [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; + + [self updateMetadata]; } int size = frames * channels; @@ -157,6 +244,8 @@ opus_int64 sourceTell(void *_stream) { } while(total != size && numread != 0); + [self updateIcyMetadata]; + return total / channels; } @@ -189,7 +278,7 @@ opus_int64 sourceTell(void *_stream) { } - (NSDictionary *)metadata { - return @{}; + return @{ @"genre": genre, @"album": album, @"artist": artist, @"title": title }; } + (NSArray *)fileTypes { diff --git a/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj b/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj index 2c094a85b..d5228d0e4 100644 --- a/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj +++ b/Plugins/Opus/OpusPlugin.xcodeproj/project.pbxproj @@ -46,6 +46,7 @@ /* Begin PBXFileReference section */ 833F68411CDBCABC00AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + 8356BD1B27B469B80074E50C /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPSource.h; path = ../../HTTPSource/HTTPSource.h; sourceTree = ""; }; 8375B03C17FFEA400092A79F /* OpusPlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpusPlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 8375B03F17FFEA400092A79F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 8375B04217FFEA400092A79F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -122,6 +123,7 @@ 8375B04517FFEA400092A79F /* Opus */ = { isa = PBXGroup; children = ( + 8356BD1B27B469B80074E50C /* HTTPSource.h */, 8384913718081F2700E7332D /* Logging.h */, 8375B36D17FFF1FE0092A79F /* Plugin.h */, 8375B36A17FFF1CB0092A79F /* OpusDecoder.h */, diff --git a/Plugins/Vorbis/VorbisDecoder.h b/Plugins/Vorbis/VorbisDecoder.h index 5f29652c6..704f14ba9 100644 --- a/Plugins/Vorbis/VorbisDecoder.h +++ b/Plugins/Vorbis/VorbisDecoder.h @@ -31,6 +31,11 @@ int channels; float frequency; long totalFrames; + + NSString *genre; + NSString *album; + NSString *artist; + NSString *title; } @end diff --git a/Plugins/Vorbis/VorbisDecoder.m b/Plugins/Vorbis/VorbisDecoder.m index 703781de4..5e344ed26 100644 --- a/Plugins/Vorbis/VorbisDecoder.m +++ b/Plugins/Vorbis/VorbisDecoder.m @@ -10,6 +10,8 @@ #import "Logging.h" +#import "HTTPSource.h" + @implementation VorbisDecoder static const int MAXCHANNELS = 8; @@ -95,9 +97,92 @@ long sourceTell(void *datasource) { [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; + genre = @""; + album = @""; + artist = @""; + title = @""; + [self updateMetadata]; + return YES; } +- (void)updateMetadata { + const vorbis_comment *comment = ov_comment(&vorbisRef, -1); + + if(comment) { + uint8_t nullByte = '\0'; + NSString *_genre = genre; + NSString *_album = album; + NSString *_artist = artist; + NSString *_title = title; + for(int i = 0; i < comment->comments; ++i) { + NSMutableData *commentField = [NSMutableData dataWithBytes:comment->user_comments[i] length:comment->comment_lengths[i]]; + [commentField appendBytes:&nullByte length:1]; + NSString *commentString = [NSString stringWithUTF8String:[commentField bytes]]; + NSArray *splitFields = [commentString componentsSeparatedByString:@"="]; + if([splitFields count] == 2) { + NSString *name = [splitFields objectAtIndex:0]; + NSString *value = [splitFields objectAtIndex:1]; + name = [name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + name = [name lowercaseString]; + if([name isEqualToString:@"genre"]) { + _genre = value; + } else if([name isEqualToString:@"album"]) { + _album = value; + } else if([name isEqualToString:@"artist"]) { + _artist = value; + } else if([name isEqualToString:@"title"]) { + _title = value; + } + } + } + + if(![_genre isEqual:genre] || + ![_album isEqual:album] || + ![_artist isEqual:artist] || + ![_title isEqual:title]) { + genre = _genre; + album = _album; + artist = _artist; + title = _title; + [self willChangeValueForKey:@"metadata"]; + [self didChangeValueForKey:@"metadata"]; + } + } +} + +- (void)updateIcyMetadata { + NSString *_genre = genre; + NSString *_album = album; + NSString *_artist = artist; + NSString *_title = title; + + Class sourceClass = [source class]; + if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) { + HTTPSource *httpSource = (HTTPSource *)source; + if([httpSource hasMetadata]) { + NSDictionary *metadata = [httpSource metadata]; + _genre = [metadata valueForKey:@"genre"]; + _album = [metadata valueForKey:@"album"]; + _artist = [metadata valueForKey:@"artist"]; + _title = [metadata valueForKey:@"title"]; + } + } + + if(![_genre isEqual:genre] || + ![_album isEqual:album] || + ![_artist isEqual:artist] || + ![_title isEqual:title]) { + genre = _genre; + album = _album; + artist = _artist; + title = _title; + [self willChangeValueForKey:@"metadata"]; + [self didChangeValueForKey:@"metadata"]; + } +} + - (int)readAudio:(void *)buf frames:(UInt32)frames { int numread; int total = 0; @@ -112,6 +197,8 @@ long sourceTell(void *datasource) { [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; + + [self updateMetadata]; } do { @@ -141,6 +228,8 @@ long sourceTell(void *datasource) { } while(total != frames && numread != 0); + [self updateIcyMetadata]; + return total; } @@ -172,7 +261,7 @@ long sourceTell(void *datasource) { } - (NSDictionary *)metadata { - return @{}; + return @{ @"genre": genre, @"album": album, @"artist": artist, @"title": title }; } + (NSArray *)fileTypes { @@ -180,7 +269,7 @@ long sourceTell(void *datasource) { } + (NSArray *)mimeTypes { - return @[@"application/ogg", @"application/x-ogg", @"audio/x-vorbis+ogg"]; + return @[@"application/ogg", @"application/x-ogg", @"audio/ogg", @"audio/x-vorbis+ogg"]; } + (float)priority { diff --git a/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj b/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj index fc79e9bf8..c51a167d3 100644 --- a/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj +++ b/Plugins/Vorbis/VorbisPlugin.xcodeproj/project.pbxproj @@ -59,6 +59,7 @@ 17C93D340B8FDA66008627D6 /* VorbisDecoder.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = VorbisDecoder.m; sourceTree = ""; }; 17F562EF0C3BDAAC0019975C /* Vorbis.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Vorbis.xcodeproj; path = ../../Frameworks/Vorbis/macosx/Vorbis.xcodeproj; sourceTree = SOURCE_ROOT; }; 32DBCF630370AF2F00C91783 /* VorbisPlugin_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VorbisPlugin_Prefix.pch; sourceTree = ""; }; + 8356BD1C27B46A2D0074E50C /* HTTPSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPSource.h; path = ../HTTPSource/HTTPSource.h; sourceTree = ""; }; 8384913418081A3900E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 8D5B49B6048680CD000E48DA /* VorbisPlugin.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VorbisPlugin.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -111,6 +112,7 @@ 08FB77AFFE84173DC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( + 8356BD1C27B46A2D0074E50C /* HTTPSource.h */, 8384913418081A3900E7332D /* Logging.h */, 177FCF9D0B90C9530011C3B5 /* Plugin.h */, 17C93D330B8FDA66008627D6 /* VorbisDecoder.h */,