diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 07edef90d..656d6472d 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F94DD40B8D0F7000A34E87 /* PluginController.m */; }; 17F94DDD0B8D101100A34E87 /* Plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 17F94DDC0B8D101100A34E87 /* Plugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; }; + 839366671815923C006DD712 /* CogDecoderMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogDecoderMulti.h */; }; + 839366681815923C006DD712 /* CogDecoderMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogDecoderMulti.m */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; 8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; }; @@ -107,6 +109,8 @@ 17F94DDC0B8D101100A34E87 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Plugin.h; sourceTree = ""; }; 32DBCF5E0370ADEE00C91783 /* CogAudio_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogAudio_Prefix.pch; sourceTree = ""; }; 8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; + 839366651815923C006DD712 /* CogDecoderMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogDecoderMulti.h; sourceTree = ""; }; + 839366661815923C006DD712 /* CogDecoderMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogDecoderMulti.m; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = ""; }; @@ -189,6 +193,8 @@ 17B6192F0B909BC300BC003F /* AudioPropertiesReader.m */, 17ADB13A0B97926D00257CA2 /* AudioSource.h */, 17ADB13B0B97926D00257CA2 /* AudioSource.m */, + 839366651815923C006DD712 /* CogDecoderMulti.h */, + 839366661815923C006DD712 /* CogDecoderMulti.m */, 17F94DD30B8D0F7000A34E87 /* PluginController.h */, 17F94DD40B8D0F7000A34E87 /* PluginController.m */, 17D21C750B8BE4BA00D1EBDE /* Chain */, @@ -317,6 +323,7 @@ 17A2D3C50B8D1D37000778C4 /* AudioDecoder.h in Headers */, 17C940230B900909008627D6 /* AudioMetadataReader.h in Headers */, 17B619300B909BC300BC003F /* AudioPropertiesReader.h in Headers */, + 839366671815923C006DD712 /* CogDecoderMulti.h in Headers */, 17ADB13C0B97926D00257CA2 /* AudioSource.h in Headers */, 8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */, 8384912718080FF100E7332D /* Logging.h in Headers */, @@ -397,6 +404,7 @@ 17D21CE00B8BE5B400D1EBDE /* VirtualRingBuffer.m in Sources */, 17D21CF40B8BE5EF00D1EBDE /* Semaphore.m in Sources */, 17D21DC80B8BE79700D1EBDE /* CoreAudioUtils.m in Sources */, + 839366681815923C006DD712 /* CogDecoderMulti.m in Sources */, 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */, 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */, 17A2D3C60B8D1D37000778C4 /* AudioDecoder.m in Sources */, diff --git a/Audio/CogDecoderMulti.h b/Audio/CogDecoderMulti.h new file mode 100644 index 000000000..650463ad6 --- /dev/null +++ b/Audio/CogDecoderMulti.h @@ -0,0 +1,20 @@ +// +// CogDecoderMulti.h +// CogAudio +// +// Created by Christopher Snowhill on 10/21/13. +// +// + +#import +#import "Plugin.h" + +@interface CogDecoderMulti : NSObject { + NSArray *theDecoders; + id theDecoder; + NSMutableArray *cachedObservers; +} + +-(id)initWithDecoders:(NSArray *)decoders; + +@end diff --git a/Audio/CogDecoderMulti.m b/Audio/CogDecoderMulti.m new file mode 100644 index 000000000..4ccfeb1b8 --- /dev/null +++ b/Audio/CogDecoderMulti.m @@ -0,0 +1,129 @@ +// +// CogDecoderMulti.m +// CogAudio +// +// Created by Christopher Snowhill on 10/21/13. +// +// + +#import "CogDecoderMulti.h" + +@implementation CogDecoderMulti + ++ (NSArray *)mimeTypes +{ + return nil; +} + ++ (NSArray *)fileTypes +{ + return nil; +} + ++ (float)priority +{ + return -1.0; +} + +- (id)initWithDecoders:(NSArray *)decoders +{ + self = [super init]; + if ( self ) + { + NSMutableArray *sortedDecoders = [NSMutableArray arrayWithArray:decoders]; + [sortedDecoders sortUsingComparator: + ^NSComparisonResult(id obj1, id obj2) + { + NSString *classString1 = (NSString *)obj1; + NSString *classString2 = (NSString *)obj2; + + Class decoder1 = NSClassFromString(classString1); + Class decoder2 = NSClassFromString(classString2); + + float priority1 = [decoder1 priority]; + float priority2 = [decoder2 priority]; + + if (priority1 == priority2) return NSOrderedSame; + else if (priority1 > priority2) return NSOrderedAscending; + else return NSOrderedDescending; + }]; + theDecoders = sortedDecoders; + theDecoder = nil; + cachedObservers = [[[NSMutableArray alloc] init] autorelease]; + } + return self; +} + +- (NSDictionary *)properties +{ + if ( theDecoder != nil ) return [theDecoder properties]; + return nil; +} + +- (int)readAudio:(void *)buffer frames:(UInt32)frames +{ + if ( theDecoder != nil ) return [theDecoder readAudio:buffer frames:frames]; + return 0; +} + +- (BOOL)open:(id)source +{ + for (NSString *classString in theDecoders) + { + Class decoder = NSClassFromString(classString); + theDecoder = [[decoder alloc] init]; + for (NSDictionary *obsItem in cachedObservers) { + [theDecoder addObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"] options:[obsItem objectForKey:@"options"] context:[obsItem objectForKey:@"context"]]; + } + if ([theDecoder open:source]) + return YES; + for (NSDictionary *obsItem in cachedObservers) { + [theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]]; + } + [theDecoder release]; + [source seek:0 whence:SEEK_SET]; + } + theDecoder = nil; + return NO; +} + +- (long)seek:(long)frame +{ + if ( theDecoder != nil ) return [theDecoder seek:frame]; + return -1; +} + +- (void)close +{ + if ( theDecoder != nil ) { + [theDecoder close]; + [theDecoder release]; + theDecoder = nil; + } +} + +- (BOOL)setTrack:(NSURL *)track +{ + if ( theDecoder != nil && [theDecoder respondsToSelector: @selector(setTrack:)] ) return [theDecoder setTrack:track]; + return NO; +} + +/* By the current design, the core adds its observers to decoders before they are opened */ +- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context +{ + [cachedObservers addObject:[NSDictionary dictionaryWithObjectsAndKeys:observer, @"observer", keyPath, @"keyPath", options, @"options", context, @"context", nil]]; +} + +- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath +{ + if ( theDecoder != nil ) + [theDecoder removeObserver:observer forKeyPath:keyPath]; + for (NSDictionary *obsItem in cachedObservers) + { + if ([obsItem objectForKey:@"observer"] == observer && [keyPath isEqualToString:[obsItem objectForKey:@"keyPath"]]) { + [cachedObservers removeObject:obsItem]; + } + } +} + +@end diff --git a/Audio/Plugin.h b/Audio/Plugin.h index a8add97a1..d1c5b3ddd 100644 --- a/Audio/Plugin.h +++ b/Audio/Plugin.h @@ -25,6 +25,7 @@ @required + (NSArray *)mimeTypes; + (NSArray *)fileTypes; //mp3, ogg, etc ++ (float)priority; // should be 0.0 ... 1.0, higher means you get selected first, should default to 1.0 unless you know a reason why any of your extensions may behave badly, ie. greedily taking over some file type extension without performing any header validation on it //For KVO //- (void)setProperties:(NSDictionary *)p; diff --git a/Audio/PluginController.m b/Audio/PluginController.m index 5f0ec6cbd..86e7c624d 100644 --- a/Audio/PluginController.m +++ b/Audio/PluginController.m @@ -1,5 +1,6 @@ #import "PluginController.h" #import "Plugin.h" +#import "CogDecoderMulti.h" #import "Logging.h" @@ -129,7 +130,16 @@ static PluginController *sharedPluginController = nil; if (decoder && [decoder respondsToSelector:@selector(fileTypes)]) { for (id fileType in [decoder fileTypes]) { - [decodersByExtension setObject:className forKey:[fileType lowercaseString]]; + NSString *ext = [fileType lowercaseString]; + NSMutableArray *decoders; + if (![decodersByExtension objectForKey:ext]) + { + decoders = [[[NSMutableArray alloc] init] autorelease]; + [decodersByExtension setObject:decoders forKey:ext]; + } + else + decoders = [decodersByExtension objectForKey:ext]; + [decoders addObject:className]; } } @@ -216,8 +226,17 @@ static PluginController *sharedPluginController = nil; - (id) audioDecoderForSource:(id )source { NSString *ext = [[[source url] path] pathExtension]; - NSString *classString = [decodersByExtension objectForKey:[ext lowercaseString]]; - if (!classString) { + NSArray *decoders = [decodersByExtension objectForKey:[ext lowercaseString]]; + NSString *classString; + if (decoders) { + if ( [decoders count] > 1 ) { + return [[[CogDecoderMulti alloc] initWithDecoders:decoders] autorelease]; + } + else { + classString = [decoders objectAtIndex:0]; + } + } + else { classString = [decodersByMimeType objectForKey:[[source mimeType] lowercaseString]]; } diff --git a/Plugins/APL/APLDecoder.m b/Plugins/APL/APLDecoder.m index ba4a3adc0..e00324831 100644 --- a/Plugins/APL/APLDecoder.m +++ b/Plugins/APL/APLDecoder.m @@ -13,6 +13,10 @@ return [NSArray arrayWithObjects:@"application/x-apl", nil]; } ++ (float)priority { + return 1.0; +} + - (NSDictionary *)properties { NSMutableDictionary *properties = [[decoder properties] mutableCopy]; diff --git a/Plugins/CoreAudio/CoreAudioDecoder.m b/Plugins/CoreAudio/CoreAudioDecoder.m index 3653e7de7..34a9c487c 100644 --- a/Plugins/CoreAudio/CoreAudioDecoder.m +++ b/Plugins/CoreAudio/CoreAudioDecoder.m @@ -181,6 +181,11 @@ return nil; } ++ (float)priority +{ + return 0.5; +} + - (NSDictionary *)properties { return [NSDictionary dictionaryWithObjectsAndKeys: diff --git a/Plugins/CueSheet/CueSheetDecoder.m b/Plugins/CueSheet/CueSheetDecoder.m index 0df2ff463..0791dbc46 100644 --- a/Plugins/CueSheet/CueSheetDecoder.m +++ b/Plugins/CueSheet/CueSheetDecoder.m @@ -26,6 +26,11 @@ return [CueSheetContainer mimeTypes]; } ++ (float)priority +{ + return 1.0; +} + - (NSDictionary *)properties { NSMutableDictionary *properties = [[decoder properties] mutableCopy]; diff --git a/Plugins/Dumb/DumbDecoder.m b/Plugins/Dumb/DumbDecoder.m index 24b01142f..af52f3c6a 100755 --- a/Plugins/Dumb/DumbDecoder.m +++ b/Plugins/Dumb/DumbDecoder.m @@ -327,4 +327,9 @@ int callbackLoop(void *data) return [NSArray arrayWithObjects:@"audio/x-it", @"audio/x-xm", @"audio/x-s3m", @"audio/x-mod", nil]; } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/FFMPEG/FFMPEGDecoder.m b/Plugins/FFMPEG/FFMPEGDecoder.m index f329eaedd..d5422e7dd 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.m +++ b/Plugins/FFMPEG/FFMPEGDecoder.m @@ -327,6 +327,10 @@ int lockmgr_callback(void ** mutex, enum AVLockOp op) return [NSArray arrayWithObjects:@"application/wma", @"application/x-wma", @"audio/x-wma", @"audio/x-ms-wma", @"audio/x-tak", @"audio/mpeg", @"audio/x-mp3", @"audio/x-mp2", @"audio/x-ape", @"audio/x-ac3", @"audio/x-dts", @"audio/x-dtshd", @"audio/x-at3", @"audio/wav", @"tta", nil]; } ++ (float)priority +{ + return 1.0; +} diff --git a/Plugins/Flac/FlacDecoder.m b/Plugins/Flac/FlacDecoder.m index 9430fac3f..ffce7ba4f 100644 --- a/Plugins/Flac/FlacDecoder.m +++ b/Plugins/Flac/FlacDecoder.m @@ -329,4 +329,9 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS return [NSArray arrayWithObjects:@"audio/x-flac", nil]; } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/GME/GameDecoder.m b/Plugins/GME/GameDecoder.m index 7e343b09b..8a374e866 100755 --- a/Plugins/GME/GameDecoder.m +++ b/Plugins/GME/GameDecoder.m @@ -175,6 +175,11 @@ gme_err_t readCallback( void* data, void* out, long count ) return nil; } ++ (float)priority +{ + return 1.0; +} + - (void)setSource:(id)s { [s retain]; diff --git a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm index 186da860a..ef4ff17d9 100644 --- a/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm +++ b/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm @@ -1531,6 +1531,10 @@ static int twosf_info(void * context, const char * name, const char * value) return [NSArray arrayWithObjects:@"audio/x-psf", nil]; } ++ (float)priority +{ + return 1.0; +} @end diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index c38341184..19c083c8f 100755 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -175,4 +175,9 @@ return [NSArray arrayWithObjects:@"audio/midi", @"audio/x-midi", nil]; } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/Musepack/MusepackDecoder.m b/Plugins/Musepack/MusepackDecoder.m index 622f862ba..4a775642e 100644 --- a/Plugins/Musepack/MusepackDecoder.m +++ b/Plugins/Musepack/MusepackDecoder.m @@ -228,4 +228,9 @@ mpc_bool_t CanSeekProc(void *data) return [NSArray arrayWithObjects:@"audio/x-musepack", nil]; } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/Opus/Opus/OpusDecoder.m b/Plugins/Opus/Opus/OpusDecoder.m index b0511f476..e0f6d91ca 100644 --- a/Plugins/Opus/Opus/OpusDecoder.m +++ b/Plugins/Opus/Opus/OpusDecoder.m @@ -160,4 +160,9 @@ opus_int64 sourceTell(void *_stream) return [NSArray arrayWithObjects:@"audio/x-opus+ogg", nil]; } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/Shorten/ShortenDecoder.mm b/Plugins/Shorten/ShortenDecoder.mm index 213bf4679..e236f0af9 100644 --- a/Plugins/Shorten/ShortenDecoder.mm +++ b/Plugins/Shorten/ShortenDecoder.mm @@ -103,5 +103,10 @@ return [NSArray arrayWithObjects:@"application/x-shorten", nil]; //This is basically useless, since we cant stream shorten yet } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/Vorbis/VorbisDecoder.m b/Plugins/Vorbis/VorbisDecoder.m index fe731889f..3ae176879 100644 --- a/Plugins/Vorbis/VorbisDecoder.m +++ b/Plugins/Vorbis/VorbisDecoder.m @@ -154,4 +154,9 @@ long sourceTell(void *datasource) return [NSArray arrayWithObjects:@"application/ogg", @"application/x-ogg", @"audio/x-vorbis+ogg", nil]; } ++ (float)priority +{ + return 1.0; +} + @end diff --git a/Plugins/WavPack/WavPackDecoder.m b/Plugins/WavPack/WavPackDecoder.m index 4a2a77cad..ec3371594 100644 --- a/Plugins/WavPack/WavPackDecoder.m +++ b/Plugins/WavPack/WavPackDecoder.m @@ -264,5 +264,10 @@ int32_t WriteBytesProc(void *ds, void *data, int32_t bcount) return [NSArray arrayWithObjects:@"audio/x-wavpack", nil]; } ++ (float)priority +{ + return 1.0; +} + @end