From 50395e6e72157b47244c3e6ee9bcb00486053b7a Mon Sep 17 00:00:00 2001 From: vspader Date: Wed, 10 Oct 2007 01:59:25 +0000 Subject: [PATCH] Basic cue sheet support working. Bug city. --- Audio/PluginController.m | 10 +- Cog.xcodeproj/project.pbxproj | 51 ++++++- Plugins/CueSheet/CueSheet.h | 2 + Plugins/CueSheet/CueSheet.m | 143 ++++++++++++++++-- .../CueSheet.xcodeproj/project.pbxproj | 6 + Plugins/CueSheet/CueSheetContainer.m | 7 +- Plugins/CueSheet/CueSheetDecoder.h | 6 +- Plugins/CueSheet/CueSheetDecoder.m | 137 +++++++++++------ Plugins/CueSheet/CueSheetPlugin.m | 5 +- Plugins/CueSheet/CueSheetTrack.h | 12 +- Plugins/CueSheet/CueSheetTrack.m | 19 ++- Plugins/MAD/MADDecoder.m | 3 +- 12 files changed, 319 insertions(+), 82 deletions(-) diff --git a/Audio/PluginController.m b/Audio/PluginController.m index f793813d8..95a480408 100644 --- a/Audio/PluginController.m +++ b/Audio/PluginController.m @@ -204,11 +204,11 @@ static PluginController *sharedPluginController = nil; - (void)printPluginInfo { - //NSLog(@"Sources: %@", sources); - //NSLog(@"Containers: %@", containers); - //NSLog(@"Decoders: %@", decoders); - //NSLog(@"Metadata Readers: %@", metadataReaders); - //NSLog(@"Properties Readers: %@", propertiesReaders); + NSLog(@"Sources: %@", sources); + NSLog(@"Containers: %@", containers); + NSLog(@"Decoders: %@", decoders); + NSLog(@"Metadata Readers: %@", metadataReaders); + NSLog(@"Properties Readers: %@", propertiesReaders); } - (NSDictionary *)sources diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 7e4ed55ea..00ea8f056 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -88,6 +88,7 @@ 17C809E60C3BD487005707C4 /* CoreAudio.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C809E30C3BD46D005707C4 /* CoreAudio.bundle */; }; 17E41E070C130DFF00AC744D /* Credits.html in Resources */ = {isa = PBXBuildFile; fileRef = 17E41E060C130DFF00AC744D /* Credits.html */; }; 17E41E230C130EE200AC744D /* Help in Resources */ = {isa = PBXBuildFile; fileRef = 17E41E220C130EE200AC744D /* Help */; }; + 17F3BB890CBC565900864489 /* CueSheet.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F3BB880CBC565100864489 /* CueSheet.bundle */; }; 17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17F561330C3BD4DC0019975C /* CogAudio.framework */; }; 17F562390C3BD91B0019975C /* General.preferencePane in Resources */ = {isa = PBXBuildFile; fileRef = 17F5622E0C3BD8FB0019975C /* General.preferencePane */; }; 17F94CC20B8D08FB00A34E87 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17F94CC10B8D08FB00A34E87 /* Sparkle.framework */; }; @@ -279,6 +280,20 @@ remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = CoreAudio; }; + 17F3BB870CBC565100864489 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8D5B49B6048680CD000E48DA /* CueSheet.bundle */; + remoteInfo = CueSheet; + }; + 17F3BB8A0CBC566200864489 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8D5B49AC048680CD000E48DA /* CueSheet */; + remoteInfo = CueSheet; + }; 17F561320C3BD4DC0019975C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */; @@ -311,28 +326,28 @@ isa = PBXContainerItemProxy; containerPortal = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 8D5B49B6048680CD000E48DA /* M3u.bundle */; + remoteGlobalIDString = 8D5B49B6048680CD000E48DA; remoteInfo = M3u; }; 8E8D40920CBB03AF00135C1B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8E8D40820CBB036600135C1B /* M3u.xcodeproj */; proxyType = 1; - remoteGlobalIDString = 8D5B49AC048680CD000E48DA /* M3u */; + remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = M3u; }; 8E8D41C60CBB0DA000135C1B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */; proxyType = 2; - remoteGlobalIDString = 8D5B49B6048680CD000E48DA /* Pls.bundle */; + remoteGlobalIDString = 8D5B49B6048680CD000E48DA; remoteInfo = Pls; }; 8E8D41CB0CBB0DD200135C1B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */; proxyType = 1; - remoteGlobalIDString = 8D5B49AC048680CD000E48DA /* Pls */; + remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = Pls; }; /* End PBXContainerItemProxy section */ @@ -344,6 +359,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 17F3BB890CBC565900864489 /* CueSheet.bundle in CopyFiles */, 8E8D41C80CBB0DA900135C1B /* Pls.bundle in CopyFiles */, 8E8D40880CBB038E00135C1B /* M3u.bundle in CopyFiles */, 17C809E60C3BD487005707C4 /* CoreAudio.bundle in CopyFiles */, @@ -507,6 +523,7 @@ 17E41C470C1304BB00AC744D /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Swedish; path = Swedish.lproj/MainMenu.nib; sourceTree = ""; }; 17E41C480C1304C700AC744D /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Swedish; path = Swedish.lproj/OpenURLPanel.nib; sourceTree = ""; }; 17E41C490C1304D200AC744D /* Swedish */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Swedish; path = Swedish.lproj/Localizable.strings; sourceTree = ""; }; + 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CueSheet.xcodeproj; path = Plugins/CueSheet/CueSheet.xcodeproj; sourceTree = ""; }; 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CogAudio.xcodeproj; path = Audio/CogAudio.xcodeproj; sourceTree = ""; }; 17F562260C3BD8FB0019975C /* General.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = General.xcodeproj; path = Preferences/General/General.xcodeproj; sourceTree = ""; }; 17F94CC10B8D08FB00A34E87 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = ThirdParty/Frameworks/Sparkle.framework; sourceTree = ""; }; @@ -791,6 +808,7 @@ 17B619FF0B909ED400BC003F /* PlugIns */ = { isa = PBXGroup; children = ( + 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */, 8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */, 8E8D40820CBB036600135C1B /* M3u.xcodeproj */, 17C808C00C3BD1DD005707C4 /* WavPack.xcodeproj */, @@ -896,6 +914,14 @@ name = Products; sourceTree = ""; }; + 17F3BB840CBC565100864489 /* Products */ = { + isa = PBXGroup; + children = ( + 17F3BB880CBC565100864489 /* CueSheet.bundle */, + ); + name = Products; + sourceTree = ""; + }; 17F5612B0C3BD4DC0019975C /* Products */ = { isa = PBXGroup; children = ( @@ -1117,6 +1143,7 @@ 17F5623B0C3BD9280019975C /* PBXTargetDependency */, 8E8D40930CBB03AF00135C1B /* PBXTargetDependency */, 8E8D41CC0CBB0DD200135C1B /* PBXTargetDependency */, + 17F3BB8B0CBC566200864489 /* PBXTargetDependency */, ); name = Cog; productInstallPath = "$(HOME)/Applications"; @@ -1151,6 +1178,10 @@ ProductGroup = 17C809DF0C3BD46D005707C4 /* Products */; ProjectRef = 17C808660C3BD0F8005707C4 /* CoreAudio.xcodeproj */; }, + { + ProductGroup = 17F3BB840CBC565100864489 /* Products */; + ProjectRef = 17F3BB830CBC565100864489 /* CueSheet.xcodeproj */; + }, { ProductGroup = 17C808720C3BD167005707C4 /* Products */; ProjectRef = 17C808710C3BD167005707C4 /* FileSource.xcodeproj */; @@ -1288,6 +1319,13 @@ remoteRef = 17C809E20C3BD46D005707C4 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 17F3BB880CBC565100864489 /* CueSheet.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = CueSheet.bundle; + remoteRef = 17F3BB870CBC565100864489 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 17F561330C3BD4DC0019975C /* CogAudio.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -1483,6 +1521,11 @@ name = CoreAudio; targetProxy = 17C809E40C3BD47C005707C4 /* PBXContainerItemProxy */; }; + 17F3BB8B0CBC566200864489 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = CueSheet; + targetProxy = 17F3BB8A0CBC566200864489 /* PBXContainerItemProxy */; + }; 17F5613F0C3BD4E90019975C /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = CogAudio; diff --git a/Plugins/CueSheet/CueSheet.h b/Plugins/CueSheet/CueSheet.h index 0dc653c07..44d562b9d 100644 --- a/Plugins/CueSheet/CueSheet.h +++ b/Plugins/CueSheet/CueSheet.h @@ -18,6 +18,8 @@ - (id)initWithFile:(NSString *)filename; +- (void)parseFile:(NSString *)filename; + - (NSArray *)tracks; - (CueSheetTrack *)track:(NSString *)fragment; diff --git a/Plugins/CueSheet/CueSheet.m b/Plugins/CueSheet/CueSheet.m index 53a707093..1229c1848 100644 --- a/Plugins/CueSheet/CueSheet.m +++ b/Plugins/CueSheet/CueSheet.m @@ -16,27 +16,146 @@ return [[[CueSheet alloc] initWithFile:filename] autorelease]; } +- (NSURL *)urlForPath:(NSString *)path relativeTo:(NSString *)baseFilename +{ + if ([path hasPrefix:@"/"]) { + return [NSURL fileURLWithPath:path]; + } + + NSRange foundRange = [path rangeOfString:@"://"]; + if (foundRange.location != NSNotFound) + { + return [NSURL URLWithString:path]; + } + + NSString *basePath = [[[baseFilename stringByStandardizingPath] stringByDeletingLastPathComponent] stringByAppendingString:@"/"]; + NSMutableString *unixPath = [path mutableCopy]; + + //Only relative paths would have windows backslashes. + [unixPath replaceOccurrencesOfString:@"\\" withString:@"/" options:0 range:NSMakeRange(0, [unixPath length])]; + + return [NSURL fileURLWithPath:[basePath stringByAppendingString:[unixPath autorelease]]]; +} + + +- (void)parseFile:(NSString *)filename +{ + NSError *error = nil; + NSString *contents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&error]; + if (error || !contents) { + NSLog(@"Could not open file...%@ %@ %@", filename, contents, error); + return; + } + + NSMutableArray *entries = [[NSMutableArray alloc] init]; + + NSString *track = nil; + NSString *path = nil; + BOOL trackAdded = NO; + + NSCharacterSet *whitespace = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + + NSString *line; + NSScanner *scanner = nil; + NSEnumerator *e = [[contents componentsSeparatedByString:@"\n"] objectEnumerator]; + while (line = [e nextObject]) + { + [scanner release]; + scanner = [[NSScanner alloc] initWithString:line]; + + NSString *command; + if (![scanner scanUpToCharactersFromSet:whitespace intoString:&command]) { + continue; + } + + //FILE "filename.shn" WAVE + if ([command isEqualToString:@"FILE"]) { + track = nil; + trackAdded = NO; + + if (![scanner scanString:@"\"" intoString:&command]) { + continue; + } + + //Read in the path + if (![scanner scanUpToString:@"\"" intoString:&path]) { + continue; + } + } + //TRACK 01 AUDIO + else if ([command isEqualToString:@"TRACK"]) { + trackAdded = NO; + + if (![scanner scanUpToCharactersFromSet:whitespace intoString:&track]) { + continue; + } + + NSString *type = nil; + if (![scanner scanUpToCharactersFromSet:whitespace intoString:&type] + || ![type isEqualToString:@"AUDIO"]) { + continue; + } + } + //INDEX 01 00:00:10 + //Note that time is written in Minutes:Seconds:Frames, where frames are 1/75 of a second + else if ([command isEqualToString:@"INDEX"]) { + if (trackAdded) { + continue; + } + + if (!path) { + continue; + } + + NSString *index = nil; + if (![scanner scanUpToCharactersFromSet:whitespace intoString:&index]) { + continue; + } + + [scanner scanCharactersFromSet:whitespace intoString:nil]; + + NSString *time = nil; + if (![scanner scanUpToCharactersFromSet:whitespace intoString:&time]) { + continue; + } + + NSArray *msf = [time componentsSeparatedByString:@":"]; + if ([msf count] != 3) { + continue; + } + + double seconds = (60*[[msf objectAtIndex:0] intValue]) + [[msf objectAtIndex:1] intValue] + ([[msf objectAtIndex:2] floatValue]/75); + + if (track == nil) { + track = @"01"; + } + + //Need to add basePath, and convert to URL + [entries addObject: + [CueSheetTrack trackWithURL:[self urlForPath:path relativeTo:filename] + track: track + time: seconds]]; + trackAdded = YES; + } + } + + [scanner release]; + + tracks = [entries copy]; + + [entries release]; +} + - (id)initWithFile:(NSString *)filename { self = [super init]; if (self) { - - NSError *error = nil; - NSString *contents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:&error]; - if (error || !contents) { - NSLog(@"Could not open file...%@ %@", contents, error); - return nil; - } - - tracks = [[NSMutableArray alloc] init]; - - //Actually parse file here. + [self parseFile:filename]; } return self; } - - (NSArray *)tracks { return tracks; diff --git a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj index 9f41b9019..67af0f649 100644 --- a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj +++ b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 17F3BB680CBC560700864489 /* CueSheetPropertiesReader.m in Sources */ = {isa = PBXBuildFile; fileRef = 17F3BB670CBC560700864489 /* CueSheetPropertiesReader.m */; }; 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; 8E8D42190CBB0F3C00135C1B /* CueSheetPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D42180CBB0F3C00135C1B /* CueSheetPlugin.m */; }; @@ -21,6 +22,8 @@ 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 17F3BB660CBC560700864489 /* CueSheetPropertiesReader.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = CueSheetPropertiesReader.h; sourceTree = ""; }; + 17F3BB670CBC560700864489 /* CueSheetPropertiesReader.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = CueSheetPropertiesReader.m; sourceTree = ""; }; 32DBCF630370AF2F00C91783 /* CueSheet_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CueSheet_Prefix.pch; sourceTree = ""; }; 8D5B49B6048680CD000E48DA /* CueSheet.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CueSheet.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Info.plist; sourceTree = ""; }; @@ -90,6 +93,8 @@ 8E8D42250CBB0F5800135C1B /* CueSheetContainer.m */, 8E8D42350CBB0F9800135C1B /* CueSheetDecoder.h */, 8E8D42360CBB0F9800135C1B /* CueSheetDecoder.m */, + 17F3BB660CBC560700864489 /* CueSheetPropertiesReader.h */, + 17F3BB670CBC560700864489 /* CueSheetPropertiesReader.m */, 8E8D424B0CBB11C600135C1B /* CueSheet.h */, 8E8D424C0CBB11C600135C1B /* CueSheet.m */, 8E8D43550CBB1AE900135C1B /* CueSheetTrack.h */, @@ -189,6 +194,7 @@ 8E8D42370CBB0F9800135C1B /* CueSheetDecoder.m in Sources */, 8E8D424D0CBB11C600135C1B /* CueSheet.m in Sources */, 8E8D43570CBB1AE900135C1B /* CueSheetTrack.m in Sources */, + 17F3BB680CBC560700864489 /* CueSheetPropertiesReader.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Plugins/CueSheet/CueSheetContainer.m b/Plugins/CueSheet/CueSheetContainer.m index 0f1d23cf2..5c76cd4f0 100644 --- a/Plugins/CueSheet/CueSheetContainer.m +++ b/Plugins/CueSheet/CueSheetContainer.m @@ -18,13 +18,16 @@ return [NSArray arrayWithObject:@"cue"]; } -+ (NSArray *)urlsForContainerURL:(NSURL *)url { ++ (NSArray *)urlsForContainerURL:(NSURL *)url +{ if (![url isFileURL]) { return [NSArray array]; } NSMutableArray *tracks = [NSMutableArray array]; - CueSheet *cuesheet = [[CueSheet alloc] initWithFile:[url path]]; + + CueSheet *cuesheet = [CueSheet cueSheetWithFile:[url path]]; + NSEnumerator *e = [[cuesheet tracks] objectEnumerator]; CueSheetTrack *track; while (track = [e nextObject]) { diff --git a/Plugins/CueSheet/CueSheetDecoder.h b/Plugins/CueSheet/CueSheetDecoder.h index a5873834b..85675b5ea 100644 --- a/Plugins/CueSheet/CueSheetDecoder.h +++ b/Plugins/CueSheet/CueSheetDecoder.h @@ -13,10 +13,12 @@ @class CueSheetTrack; @interface CueSheetDecoder : NSObject { + id source; id decoder; - + int bytesPerSecond; - int trackPosition; + int bytePosition; + double trackEnd; CueSheetTrack *track; } diff --git a/Plugins/CueSheet/CueSheetDecoder.m b/Plugins/CueSheet/CueSheetDecoder.m index 6a0e1e7bd..bb8bbcec5 100644 --- a/Plugins/CueSheet/CueSheetDecoder.m +++ b/Plugins/CueSheet/CueSheetDecoder.m @@ -10,49 +10,105 @@ #import "CueSheet.h" #import "CueSheetTrack.h" +#import "CueSheetContainer.h" @implementation CueSheetDecoder + (NSArray *)fileTypes { - return [NSArray arrayWithObject:@"cue"]; + return [CueSheetContainer fileTypes]; } - (NSDictionary *)properties { - return [decoder properties]; + NSMutableDictionary *properties = [[decoder properties] mutableCopy]; + + //Need to alter length + [properties setObject:[NSNumber numberWithDouble:((trackEnd - [track time]) * 1000)] forKey:@"length"]; + + return [properties autorelease]; } -- (BOOL)open:(id)source +- (BOOL)open:(id)s { - //Kind of a hackish way of accessing AudioDecoder - decoder = [NSClassFromString(@"AudioDecoder") audioDecoderForURL:[source url]]; - [decoder retain]; - - BOOL r = [decoder open:source]; - if (r) - { - CueSheet *cuesheet = [CueSheet cueSheetWithFile:[[source url] path]]; - - track = [cuesheet track:[[source url] fragment]]; - - NSDictionary *properties = [decoder properties]; - int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue]; - int channels = [[properties objectForKey:@"channels"] intValue]; - float sampleRate = [[properties objectForKey:@"sampleRate"] floatValue]; - - bytesPerSecond = (int)((bitsPerSample/8) * channels * sampleRate); - - [decoder seekToTime: [track start] * 1000.0]; + if (![[s url] isFileURL]) { + return NO; + } + + NSURL *url = [s url]; + [s close]; + + CueSheet *cuesheet = [CueSheet cueSheetWithFile:[url path]]; + + NSArray *tracks = [cuesheet tracks]; + int i; + for (i = 0; i < [tracks count]; i++) + { + if ([[[tracks objectAtIndex:i] track] isEqualToString:[url fragment]]){ + track = [tracks objectAtIndex:i]; + + //Kind of a hackish way of accessing outside classes. + source = [NSClassFromString(@"AudioSource") audioSourceForURL:[track url]]; + [source retain]; + + if (![source open:[track url]]) { + NSLog(@"Could not open cuesheet source"); + return NO; + } + + decoder = [NSClassFromString(@"AudioDecoder") audioDecoderForURL:[source url]]; + [decoder retain]; + + if (![decoder open:source]) { + NSLog(@"Could not open cuesheet decoder"); + return NO; + } + + CueSheetTrack *nextTrack = nil; + if (i + 1 < [tracks count]) { + nextTrack = [tracks objectAtIndex:i + 1]; + } + + NSDictionary *properties = [decoder properties]; + int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue]; + int channels = [[properties objectForKey:@"channels"] intValue]; + float sampleRate = [[properties objectForKey:@"sampleRate"] floatValue]; + + bytesPerSecond = (int)((bitsPerSample/8) * channels * sampleRate); + + [decoder seekToTime: [track time] * 1000.0]; + + if (nextTrack && [[[nextTrack url] absoluteString] isEqualToString:[[track url] absoluteString]]) { + trackEnd = [nextTrack time]; + } + else { + trackEnd = [[properties objectForKey:@"length"] doubleValue]/1000.0; + } + + return YES; + } + } + + return NO; +} + +- (void)close { + if (decoder) { + [decoder close]; + [decoder release]; + decoder = nil; + } + + if (source) { + [source release]; + source = nil; } - - return r; } - (double)seekToTime:(double)time //milliseconds { - double trackStartMs = [track start] * 1000.0; - double trackEndMs = [track end] * 1000.0; + double trackStartMs = [track time] * 1000.0; + double trackEndMs = trackEnd * 1000.0; if (time > trackEndMs - trackStartMs) { //need a better way of returning fail. @@ -61,34 +117,29 @@ time += trackStartMs; - trackPosition = (time/1000.0) * bytesPerSecond; + bytePosition = (time/1000.0) * bytesPerSecond; return [decoder seekToTime:time]; } - (int)fillBuffer:(void *)buf ofSize:(UInt32)size { - int n; + long trackByteEnd = trackEnd * bytesPerSecond; - n = [decoder fillBuffer:buf ofSize:size]; - - trackPosition += n; - - int trackEnd = [track end] * bytesPerSecond; - - if (trackPosition + n > trackEnd) { - return trackEnd - trackPosition; + if (bytePosition + size > trackByteEnd) { + size = trackByteEnd - bytePosition; } + + if (!size) { + return 0; + } + + int n = [decoder fillBuffer:buf ofSize:size]; + + bytePosition += n; return n; } -- (void)dealloc -{ - [decoder release]; - - [super dealloc]; -} - @end diff --git a/Plugins/CueSheet/CueSheetPlugin.m b/Plugins/CueSheet/CueSheetPlugin.m index 42c58b2a3..c6a15524b 100644 --- a/Plugins/CueSheet/CueSheetPlugin.m +++ b/Plugins/CueSheet/CueSheetPlugin.m @@ -10,6 +10,7 @@ #import "CueSheetContainer.h" #import "CueSheetDecoder.h" +#import "CueSheetPropertiesReader.h" @implementation CueSheetPlugin @@ -17,7 +18,9 @@ { return [NSDictionary dictionaryWithObjectsAndKeys: kCogContainer, [CueSheetContainer className], - kCogDecoder, [CueSheetDecoder className]]; + kCogDecoder, [CueSheetDecoder className], + kCogPropertiesReader, [CueSheetPropertiesReader className], + nil]; } @end diff --git a/Plugins/CueSheet/CueSheetTrack.h b/Plugins/CueSheet/CueSheetTrack.h index a5fec9bcd..fb668a304 100644 --- a/Plugins/CueSheet/CueSheetTrack.h +++ b/Plugins/CueSheet/CueSheetTrack.h @@ -11,16 +11,18 @@ @interface CueSheetTrack : NSObject { NSString *track; + NSURL *url; - double start; - double end; + double time; } -- (void)initWithTrack:(NSString *)t start:(double)s end:(double)e; ++ (id)trackWithURL:(NSURL *)u track:(NSString *)t time:(double)t; +- (id)initWithURL:(NSURL *)u track:(NSString *)t time:(double)t; + - (NSString *)track; +- (NSURL *)url; -- (double)start; -- (double)end; +- (double)time; @end diff --git a/Plugins/CueSheet/CueSheetTrack.m b/Plugins/CueSheet/CueSheetTrack.m index 135b0874b..94beedc17 100644 --- a/Plugins/CueSheet/CueSheetTrack.m +++ b/Plugins/CueSheet/CueSheetTrack.m @@ -11,14 +11,19 @@ @implementation CueSheetTrack -- (void)initWithTrack:(NSString *)t start:(double)s end:(double)e ++ (id)trackWithURL:(NSURL *)u track:(NSString *)t time:(double)s +{ + return [[[CueSheetTrack alloc] initWithURL:u track:t time:s] autorelease]; +} + +- (id)initWithURL:(NSURL *)u track:(NSString *)t time:(double)s { self = [super init]; if (self) { track = [t copy]; - start = s; - end = e; + url = [u copy]; + time = s; } return self; @@ -29,14 +34,14 @@ return track; } -- (double)start +- (NSURL *)url { - return start; + return url; } -- (double)end +- (double)time { - return end; + return time; } @end diff --git a/Plugins/MAD/MADDecoder.m b/Plugins/MAD/MADDecoder.m index 1acfc22e8..22787a518 100644 --- a/Plugins/MAD/MADDecoder.m +++ b/Plugins/MAD/MADDecoder.m @@ -567,7 +567,8 @@ static inline signed int scale (mad_fixed_t sample) new_position = ((double) seconds / (double) total_seconds) * _fileSize; [_source seek:new_position whence:SEEK_SET]; - mad_stream_sync(&_stream); + + //mad_stream_sync(&_stream); _stream.error = MAD_ERROR_BUFLEN; _stream.sync = 0; _outputAvailable = 0;