diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 5af87de58..211d6ad23 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -39,8 +39,13 @@ 17D21EBD0B8BF44000D1EBDE /* AudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 17D21EBB0B8BF44000D1EBDE /* AudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 17D21EBC0B8BF44000D1EBDE /* AudioPlayer.m */; }; 17F94DD50B8D0F7000A34E87 /* PluginController.h in Headers */ = {isa = PBXBuildFile; fileRef = 17F94DD30B8D0F7000A34E87 /* PluginController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F94DD40B8D0F7000A34E87 /* PluginController.m */; }; + 17F94DD60B8D0F7000A34E87 /* PluginController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 17F94DD40B8D0F7000A34E87 /* PluginController.mm */; }; 17F94DDD0B8D101100A34E87 /* Plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 17F94DDC0B8D101100A34E87 /* Plugin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8328995327CB511000D7F028 /* RedundantPlaylistDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8328995127CB510F00D7F028 /* RedundantPlaylistDataStore.m */; }; + 8328995427CB511000D7F028 /* RedundantPlaylistDataStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 8328995227CB511000D7F028 /* RedundantPlaylistDataStore.h */; }; + 8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */ = {isa = PBXBuildFile; fileRef = 8328995527CB51B700D7F028 /* SHA256Digest.h */; }; + 8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8328995627CB51B700D7F028 /* SHA256Digest.m */; }; + 8328995A27CB51C900D7F028 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8328995927CB51C900D7F028 /* Security.framework */; }; 8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */; }; 8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */; }; 834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EA27AF8F380063BC83 /* AudioChunk.h */; }; @@ -139,9 +144,14 @@ 17D21EBB0B8BF44000D1EBDE /* AudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AudioPlayer.h; sourceTree = ""; }; 17D21EBC0B8BF44000D1EBDE /* AudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AudioPlayer.m; sourceTree = ""; }; 17F94DD30B8D0F7000A34E87 /* PluginController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PluginController.h; sourceTree = ""; }; - 17F94DD40B8D0F7000A34E87 /* PluginController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PluginController.m; sourceTree = ""; }; + 17F94DD40B8D0F7000A34E87 /* PluginController.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = PluginController.mm; sourceTree = ""; }; 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 = ""; }; + 8328995127CB510F00D7F028 /* RedundantPlaylistDataStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RedundantPlaylistDataStore.m; path = ../../Utils/RedundantPlaylistDataStore.m; sourceTree = ""; }; + 8328995227CB511000D7F028 /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RedundantPlaylistDataStore.h; path = ../../Utils/RedundantPlaylistDataStore.h; sourceTree = ""; }; + 8328995527CB51B700D7F028 /* SHA256Digest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SHA256Digest.h; path = ../../Utils/SHA256Digest.h; sourceTree = ""; }; + 8328995627CB51B700D7F028 /* SHA256Digest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SHA256Digest.m; path = ../../Utils/SHA256Digest.m; sourceTree = ""; }; + 8328995927CB51C900D7F028 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSFileHandle+CreateFile.h"; path = "../../Utils/NSFileHandle+CreateFile.h"; sourceTree = ""; }; 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSFileHandle+CreateFile.m"; path = "../../Utils/NSFileHandle+CreateFile.m"; sourceTree = ""; }; 834FD4EA27AF8F380063BC83 /* AudioChunk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AudioChunk.h; sourceTree = ""; }; @@ -190,6 +200,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 8328995A27CB51C900D7F028 /* Security.framework in Frameworks */, 83725A9127AA16D50003F694 /* AVFoundation.framework in Frameworks */, 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, 83725A9027AA16C90003F694 /* Accelerate.framework in Frameworks */, @@ -264,7 +275,7 @@ 839366651815923C006DD712 /* CogPluginMulti.h */, 839366661815923C006DD712 /* CogPluginMulti.m */, 17F94DD30B8D0F7000A34E87 /* PluginController.h */, - 17F94DD40B8D0F7000A34E87 /* PluginController.m */, + 17F94DD40B8D0F7000A34E87 /* PluginController.mm */, 17D21C750B8BE4BA00D1EBDE /* Chain */, 17D21C9B0B8BE4BA00D1EBDE /* Output */, 17D21C9E0B8BE4BA00D1EBDE /* Status.h */, @@ -349,6 +360,10 @@ 17D21CDC0B8BE5B400D1EBDE /* Utils */ = { isa = PBXGroup; children = ( + 8328995527CB51B700D7F028 /* SHA256Digest.h */, + 8328995627CB51B700D7F028 /* SHA256Digest.m */, + 8328995227CB511000D7F028 /* RedundantPlaylistDataStore.h */, + 8328995127CB510F00D7F028 /* RedundantPlaylistDataStore.m */, 835FAC5C27BCA14D00BA8562 /* BadSampleCleaner.h */, 835FAC5D27BCA14D00BA8562 /* BadSampleCleaner.m */, 8399CF2A27B5D1D4008751F1 /* NSDictionary+Merge.h */, @@ -436,6 +451,7 @@ 83725A8F27AA16C90003F694 /* Frameworks */ = { isa = PBXGroup; children = ( + 8328995927CB51C900D7F028 /* Security.framework */, ); name = Frameworks; sourceTree = ""; @@ -470,6 +486,7 @@ 17D21CA70B8BE4BA00D1EBDE /* Node.h in Headers */, 8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */, 17D21CA90B8BE4BA00D1EBDE /* OutputNode.h in Headers */, + 8328995427CB511000D7F028 /* RedundantPlaylistDataStore.h in Headers */, 17D21CC50B8BE4BA00D1EBDE /* OutputCoreAudio.h in Headers */, 834FD4F427AFA2150063BC83 /* Downmix.h in Headers */, 17D21CC70B8BE4BA00D1EBDE /* Status.h in Headers */, @@ -481,6 +498,7 @@ 834FD4F027AF93680063BC83 /* ChunkList.h in Headers */, 17F94DD50B8D0F7000A34E87 /* PluginController.h in Headers */, 17F94DDD0B8D101100A34E87 /* Plugin.h in Headers */, + 8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */, 834FD4EB27AF8F380063BC83 /* AudioChunk.h in Headers */, 17A2D3C50B8D1D37000778C4 /* AudioDecoder.h in Headers */, 8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */, @@ -590,12 +608,14 @@ 17D21CF40B8BE5EF00D1EBDE /* Semaphore.m in Sources */, 8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */, 17D21DC80B8BE79700D1EBDE /* CoreAudioUtils.m in Sources */, + 8328995327CB511000D7F028 /* RedundantPlaylistDataStore.m in Sources */, 8377C64C27B8C51500E8BC0F /* fft_accelerate.c in Sources */, 839366681815923C006DD712 /* CogPluginMulti.m in Sources */, 835C88AA2797D4D400E28EAE /* lpc.c in Sources */, 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */, - 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */, + 17F94DD60B8D0F7000A34E87 /* PluginController.mm in Sources */, 17A2D3C60B8D1D37000778C4 /* AudioDecoder.m in Sources */, + 8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */, 17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */, 17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */, 17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */, diff --git a/Audio/PluginController.m b/Audio/PluginController.mm similarity index 82% rename from Audio/PluginController.m rename to Audio/PluginController.mm index 89068d408..1b52df1a4 100644 --- a/Audio/PluginController.m +++ b/Audio/PluginController.mm @@ -8,6 +8,125 @@ #import "NSDictionary+Merge.h" +#import "RedundantPlaylistDataStore.h" + +#import +#import +#import +#import + +struct Cached_Metadata { + std::chrono::steady_clock::time_point time_accessed; + NSDictionary *properties; + NSDictionary *metadata; + Cached_Metadata() + : properties(nil), metadata(nil) { + } +}; + +static std::mutex Cache_Lock; + +static std::map Cache_List; + +static RedundantPlaylistDataStore *Cache_Data_Store = nil; + +static bool Cache_Running = false; + +static std::thread *Cache_Thread = NULL; + +static void cache_run(); + +static void cache_init() { + Cache_Data_Store = [[RedundantPlaylistDataStore alloc] init]; + Cache_Thread = new std::thread(cache_run); +} + +static void cache_deinit() { + Cache_Running = false; + Cache_Thread->join(); + delete Cache_Thread; + Cache_Data_Store = nil; +} + +static void cache_insert_properties(NSURL *url, NSDictionary *properties) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + properties = [Cache_Data_Store coalesceEntryInfo:properties]; + + Cached_Metadata &entry = Cache_List[path]; + + entry.properties = properties; + entry.time_accessed = std::chrono::steady_clock::now(); +} + +static void cache_insert_metadata(NSURL *url, NSDictionary *metadata) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + metadata = [Cache_Data_Store coalesceEntryInfo:metadata]; + + Cached_Metadata &entry = Cache_List[path]; + + entry.metadata = metadata; + entry.time_accessed = std::chrono::steady_clock::now(); +} + +static NSDictionary *cache_access_properties(NSURL *url) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + + Cached_Metadata &entry = Cache_List[path]; + + if(entry.properties) { + entry.time_accessed = std::chrono::steady_clock::now(); + return entry.properties; + } + + return nil; +} + +static NSDictionary *cache_access_metadata(NSURL *url) { + std::lock_guard lock(Cache_Lock); + + std::string path = [[url absoluteString] UTF8String]; + + Cached_Metadata &entry = Cache_List[path]; + + if(entry.metadata) { + entry.time_accessed = std::chrono::steady_clock::now(); + return entry.metadata; + } + + return nil; +} + +static void cache_run() { + std::chrono::milliseconds dura(250); + + while(Cache_Running) { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + + { + std::lock_guard lock(Cache_Lock); + for(auto it = Cache_List.begin(); it != Cache_List.end();) { + auto elapsed = std::chrono::duration_cast(now - it->second.time_accessed); + if(elapsed.count() >= 10) { + it = Cache_List.erase(it); + continue; + } + ++it; + } + + if(Cache_List.size() == 0) + [Cache_Data_Store reset]; + } + + std::this_thread::sleep_for(dura); + } +} + @implementation PluginController @synthesize sources; @@ -49,11 +168,17 @@ static PluginController *sharedPluginController = nil; self.decodersByMimeType = [[NSMutableDictionary alloc] init]; [self setup]; + + cache_init(); } return self; } +- (void)dealloc { + cache_deinit(); +} + - (void)setup { if(self.configured == NO) { self.configured = YES; @@ -457,6 +582,9 @@ static PluginController *sharedPluginController = nil; [urlScheme isEqualToString:@"https"]) return nil; + NSDictionary *cacheData = cache_access_metadata(url); + if(cacheData) return cacheData; + NSString *ext = [url pathExtension]; NSArray *readers = [metadataReaders objectForKey:[ext lowercaseString]]; NSString *classString; @@ -470,9 +598,13 @@ static PluginController *sharedPluginController = nil; else ++i; } - return [CogMetadataReaderMulti metadataForURL:url readers:_readers]; + cacheData = [CogMetadataReaderMulti metadataForURL:url readers:_readers]; + cache_insert_metadata(url, cacheData); + return cacheData; } - return [CogMetadataReaderMulti metadataForURL:url readers:readers]; + cacheData = [CogMetadataReaderMulti metadataForURL:url readers:readers]; + cache_insert_metadata(url, cacheData); + return cacheData; } else { classString = [readers objectAtIndex:0]; } @@ -482,7 +614,9 @@ static PluginController *sharedPluginController = nil; Class metadataReader = NSClassFromString(classString); - return [metadataReader metadataForURL:url]; + cacheData = [metadataReader metadataForURL:url]; + cache_insert_metadata(url, cacheData); + return cacheData; } // If no properties reader is defined, use the decoder's properties. @@ -493,6 +627,10 @@ static PluginController *sharedPluginController = nil; return nil; NSDictionary *properties = nil; + + properties = cache_access_properties(url); + if(properties) return properties; + NSString *ext = [url pathExtension]; id source = [self audioSourceForURL:url]; @@ -504,8 +642,10 @@ static PluginController *sharedPluginController = nil; if(readers) { if([readers count] > 1) { properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers]; - if(properties != nil && [properties count]) + if(properties != nil && [properties count]) { + cache_insert_properties(url, properties); return properties; + } } else { classString = [readers objectAtIndex:0]; } @@ -514,8 +654,10 @@ static PluginController *sharedPluginController = nil; if(readers) { if([readers count] > 1) { properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers]; - if(properties != nil && [properties count]) + if(properties != nil && [properties count]) { + cache_insert_properties(url, properties); return properties; + } } else { classString = [readers objectAtIndex:0]; } @@ -526,8 +668,10 @@ static PluginController *sharedPluginController = nil; Class propertiesReader = NSClassFromString(classString); properties = [propertiesReader propertiesForSource:source]; - if(properties != nil && [properties count]) + if(properties != nil && [properties count]) { + cache_insert_properties(url, properties); return properties; + } } { @@ -541,7 +685,9 @@ static PluginController *sharedPluginController = nil; [decoder close]; - return [NSDictionary dictionaryByMerging:properties with:metadata]; + NSDictionary *cacheData = [NSDictionary dictionaryByMerging:properties with:metadata]; + cache_insert_properties(url, cacheData); + return cacheData; } } diff --git a/Utils/RedundantPlaylistDataStore.h b/Utils/RedundantPlaylistDataStore.h index 2927f7fa5..2b2899e0d 100644 --- a/Utils/RedundantPlaylistDataStore.h +++ b/Utils/RedundantPlaylistDataStore.h @@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN - (id)init; - (NSDictionary *)coalesceEntryInfo:(NSDictionary *)pe; +- (void)reset; @end diff --git a/Utils/RedundantPlaylistDataStore.m b/Utils/RedundantPlaylistDataStore.m index f8b96f0ee..19c00d82e 100644 --- a/Utils/RedundantPlaylistDataStore.m +++ b/Utils/RedundantPlaylistDataStore.m @@ -69,4 +69,9 @@ return [NSDictionary dictionaryWithDictionary:ret]; } +- (void)reset { + [stringStore removeAllObjects]; + [artStore removeAllObjects]; +} + @end