diff --git a/AudioBinder.h b/AudioBinder.h index 02f4479..eea5f4e 100644 --- a/AudioBinder.h +++ b/AudioBinder.h @@ -29,18 +29,19 @@ #include #include +#import "AudioFile.h" #define DEFAULT_SAMPLE_RATE 44100.f @protocol AudioBinderDelegate --(void) updateStatus: (NSString *)filename handled:(UInt64)handledFrames total:(UInt64)totalFrames; --(void) conversionStart: (NSString*)filename +-(void) updateStatus: (AudioFile*)file handled:(UInt64)handledFrames total:(UInt64)totalFrames; +-(void) conversionStart: (AudioFile*)file format: (AudioStreamBasicDescription*)format formatDescription: (NSString*)description length: (UInt64)frames; --(BOOL) continueFailedConversion:(NSString*)filename reason:(NSString*)reason; --(void) conversionFinished: (NSString*)filename; +-(BOOL) continueFailedConversion:(AudioFile*)file reason:(NSString*)reason; +-(void) conversionFinished: (AudioFile*)file duration: (UInt32)milliseconds; -(void) audiobookReady: (NSString*)filename duration: (UInt32)seconds; -(void) audiobookFailed: (NSString*)filename reason: (NSString*)reason; @@ -65,12 +66,12 @@ -(id) init; -(void) reset; -(void) setDelegate: (id )delegate; --(void) addInputFile: (NSString*)fileName; +-(void) addInputFile: (AudioFile*)file; -(void) setOutputFile: (NSString*)outFileName; -(BOOL) convert; -(BOOL) openOutFile; -(void) closeOutFile; --(BOOL) convertOneFile: (NSString*)inFileName reason: (NSString**)reason; +-(BOOL) convertOneFile: (AudioFile*)inFile reason: (NSString**)reason; -(void) cancel; @end diff --git a/AudioBinder.m b/AudioBinder.m index 2d2c1f2..0f6348d 100644 --- a/AudioBinder.m +++ b/AudioBinder.m @@ -136,9 +136,9 @@ -(void)setDelegate: (id)delegate _delegate = delegate; } --(void)addInputFile: (NSString*)fileName +-(void)addInputFile: (AudioFile*)file { - [_inFiles addObject: fileName]; + [_inFiles addObject: file]; } -(void)setOutputFile: (NSString*)outFileName @@ -167,7 +167,7 @@ -(BOOL)convert return NO; } - for (NSString* inFile in _inFiles) + for (AudioFile* inFile in _inFiles) { NSString *reason; if ([self convertOneFile:inFile reason:&reason] == NO) @@ -251,7 +251,7 @@ -(BOOL)openOutFile status = ExtAudioFileCreateNew(&dirFSRef, (CFStringRef)[_outFileName lastPathComponent], - kAudioFileM4AType, &outputFormat, + kAudioFileMPEG4Type, &outputFormat, NULL, &_outAudioFile); if (status != noErr) @@ -271,7 +271,7 @@ -(void)closeOutFile _outAudioFile = nil; } --(BOOL) convertOneFile: (NSString *)inFileName reason: (NSString**)reason +-(BOOL) convertOneFile: (AudioFile *)inFile reason: (NSString**)reason { // Get description NSString *fileFormat; @@ -292,16 +292,16 @@ -(BOOL) convertOneFile: (NSString *)inFileName reason: (NSString**)reason @try { // open audio file status = FSPathMakeRef( - (const UInt8 *)[inFileName fileSystemRepresentation], + (const UInt8 *)[inFile.filePath fileSystemRepresentation], &ref, &isDirectory); if (status != noErr) [NSException raise:@"ConvertException" format:@"Failed to make reference for file %@: %@", - inFileName, stringForOSStatus(status)]; + inFile.filePath, stringForOSStatus(status)]; if (isDirectory) [NSException raise:@"ConvertException" - format:@"Error: %@ is directory", inFileName]; + format:@"Error: %@ is directory", inFile.filePath]; status = ExtAudioFileOpen(&ref, &inAudioFile); if (status != noErr) @@ -339,7 +339,7 @@ -(BOOL) convertOneFile: (NSString *)inFileName reason: (NSString**)reason format:@"failed to get input file length: %@", stringForOSStatus(status)]; - [_delegate conversionStart: inFileName + [_delegate conversionStart: inFile format: &format formatDescription: fileFormat length: framesTotal]; @@ -422,6 +422,7 @@ -(BOOL) convertOneFile: (NSString *)inFileName reason: (NSString**)reason bufferList.mBuffers[0].mData = audioBuffer; bufferList.mBuffers[0].mDataByteSize = AUDIO_BUFFER_SIZE; + SInt64 prevPos = _outFileLength; do { framesToRead = @@ -444,7 +445,7 @@ -(BOOL) convertOneFile: (NSString *)inFileName reason: (NSString**)reason framesConverted += framesToRead; - [_delegate updateStatus:inFileName + [_delegate updateStatus:inFile handled:framesConverted total:framesTotal]; @@ -459,7 +460,10 @@ -(BOOL) convertOneFile: (NSString *)inFileName reason: (NSString**)reason } while(framesToRead > 0); - [_delegate conversionFinished:inFileName]; + + UInt32 duration = (_outFileLength - prevPos)*1000/_sampleRate; + [_delegate conversionFinished:inFile + duration:duration]; } @catch (NSException *e) { *reason = [e reason]; diff --git a/AudioBookBinder.xcodeproj/project.pbxproj b/AudioBookBinder.xcodeproj/project.pbxproj index 6567ed5..036f9f2 100644 --- a/AudioBookBinder.xcodeproj/project.pbxproj +++ b/AudioBookBinder.xcodeproj/project.pbxproj @@ -38,6 +38,8 @@ 9F86C0D21023DAC000E65B00 /* MP4File.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F86C0D11023DAC000E65B00 /* MP4File.m */; }; 9F95914211658710001C2433 /* iAudiobook256.icns in Resources */ = {isa = PBXBuildFile; fileRef = 9F95914111658710001C2433 /* iAudiobook256.icns */; }; 9FA52D9211C2F100003FD879 /* AudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F6FA4A8111E56090047728F /* AudioFile.m */; }; + 9FA52D9F11C2FFCE003FD879 /* Chapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FA52D9E11C2FFCE003FD879 /* Chapter.m */; }; + 9FA52DA011C2FFCE003FD879 /* Chapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FA52D9E11C2FFCE003FD879 /* Chapter.m */; }; 9FAB146D11619F9F00A2F36A /* ExpandedPathToIconTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FAB146A11619F9F00A2F36A /* ExpandedPathToIconTransformer.m */; }; 9FAB146E11619F9F00A2F36A /* ExpandedPathToPathTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FAB146C11619F9F00A2F36A /* ExpandedPathToPathTransformer.m */; }; 9FAB14821161A51400A2F36A /* PrefsController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9FAB14811161A51400A2F36A /* PrefsController.m */; }; @@ -108,6 +110,8 @@ 9F86C0D11023DAC000E65B00 /* MP4File.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MP4File.m; sourceTree = ""; }; 9F95914111658710001C2433 /* iAudiobook256.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = iAudiobook256.icns; sourceTree = ""; }; 9F9C32D711A750A600F3FBC4 /* Russian */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Russian; path = Russian.lproj/MainMenu.xib; sourceTree = ""; }; + 9FA52D9D11C2FFCE003FD879 /* Chapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Chapter.h; sourceTree = ""; }; + 9FA52D9E11C2FFCE003FD879 /* Chapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Chapter.m; sourceTree = ""; }; 9FAB146911619F9F00A2F36A /* ExpandedPathToIconTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExpandedPathToIconTransformer.h; sourceTree = ""; }; 9FAB146A11619F9F00A2F36A /* ExpandedPathToIconTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ExpandedPathToIconTransformer.m; sourceTree = ""; }; 9FAB146B11619F9F00A2F36A /* ExpandedPathToPathTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExpandedPathToPathTransformer.h; sourceTree = ""; }; @@ -200,6 +204,8 @@ 9F62872B1194D502000D3A05 /* CoverImageView.m */, 9FB4DE0511C05C0E007B3DDE /* MetaEditor.h */, 9FB4DE0611C05C0E007B3DDE /* MetaEditor.mm */, + 9FA52D9D11C2FFCE003FD879 /* Chapter.h */, + 9FA52D9E11C2FFCE003FD879 /* Chapter.m */, ); name = Source; sourceTree = ""; @@ -349,6 +355,7 @@ 9FBCBF46102A6A2C0067AE9F /* ConsoleDelegate.m in Sources */, 9FB4DE0811C05C0E007B3DDE /* MetaEditor.mm in Sources */, 9FA52D9211C2F100003FD879 /* AudioFile.m in Sources */, + 9FA52DA011C2FFCE003FD879 /* Chapter.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -370,6 +377,7 @@ 9FAB14821161A51400A2F36A /* PrefsController.m in Sources */, 9F62872C1194D502000D3A05 /* CoverImageView.m in Sources */, 9FB4DE0711C05C0E007B3DDE /* MetaEditor.mm in Sources */, + 9FA52D9F11C2FFCE003FD879 /* Chapter.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/AudioFile.h b/AudioFile.h index 624df10..340f8a4 100644 --- a/AudioFile.h +++ b/AudioFile.h @@ -13,16 +13,21 @@ NSString *filePath; NSString *name; NSInteger duration; + NSString *artist, *title; + BOOL valid; } @property(readwrite, copy) NSString *filePath; @property(readwrite, copy) NSString *name; @property(readwrite, assign) NSInteger duration; +@property(readwrite, assign) BOOL valid; +@property(readwrite, copy) NSString *artist; +@property(readwrite, copy) NSString *title; + - (id) initWithPath:(NSString*)path; - (void) dealloc; -- (BOOL) isValid; // private function -- (void) updateDuration; +- (void) updateInfo; @end diff --git a/AudioFile.m b/AudioFile.m index c3f7a39..5de7aef 100644 --- a/AudioFile.m +++ b/AudioFile.m @@ -19,50 +19,71 @@ - (id) initWithPath:(NSString*)path self.filePath = path; self.name = [path lastPathComponent]; self.duration = -1; - [self updateDuration]; + self.valid = NO; + [self updateInfo]; } return self; } -- (BOOL) isValid -{ - if (self.duration >= 0) - return TRUE; - - return FALSE; -} - - (void) dealloc { - [filePath release]; - [name release]; + self.filePath = nil; + self.name = nil; + self.artist = nil; + self.title = nil; [super dealloc]; } -@synthesize filePath, name, duration; +@synthesize filePath, name, duration, valid, artist, title; -- (void) updateDuration +- (void) updateInfo { - NSWorkspace *ws = [NSWorkspace sharedWorkspace]; - NSString *extension = [[self.filePath pathExtension] lowercaseString]; - if ([ws filenameExtension:extension isValidForType:@"public.audio"]) - { - AudioFileID audioFile; - CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, - (CFStringRef)self.filePath, - kCFURLPOSIXPathStyle, FALSE); - if (AudioFileOpenURL(url, 0x01, 0, &audioFile) == noErr) - { - UInt32 len = sizeof(NSTimeInterval); - NSTimeInterval dur; - if (AudioFileGetProperty(audioFile, kAudioFilePropertyEstimatedDuration, &len, &dur) == noErr) - self.duration = dur; - AudioFileClose(audioFile); + // NSString *extension = [[self.filePath pathExtension] lowercaseString]; + OSStatus status; + AudioFileID audioFile; + CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, + (CFStringRef)self.filePath, + kCFURLPOSIXPathStyle, FALSE); + if (AudioFileOpenURL(url, 0x01, 0, &audioFile) == noErr) { + UInt32 len = sizeof(NSTimeInterval); + NSTimeInterval dur; + if (AudioFileGetProperty(audioFile, kAudioFilePropertyEstimatedDuration, &len, &dur) == noErr) + self.duration = dur*1000; + + UInt32 writable = 0, size; + status = AudioFileGetPropertyInfo(audioFile, + kAudioFilePropertyInfoDictionary, &size, &writable); + + if ( status == noErr ) { + CFDictionaryRef info = NULL; + status = AudioFileGetProperty(audioFile, + kAudioFilePropertyInfoDictionary, &size, &info); + if ( status == noErr ) { + NSDictionary *properties = (NSDictionary *)info; + // NSLog(@"file properties: %@", properties); + NSString *s = [NSString stringWithUTF8String: + [[properties objectForKey:@"artist"] UTF8String]]; + if (s) + self.artist = s; + else + self.artist = @""; + + + s = [NSString stringWithUTF8String: + [[properties objectForKey:@"title"] UTF8String]]; + if (s) + self.title = s; + else + self.title = @""; + } } - CFRelease(url); + self.valid = YES; + AudioFileClose(audioFile); } - NSLog(@"updateDuration: %d", self.duration); + + CFRelease(url); } + @end diff --git a/ConsoleDelegate.h b/ConsoleDelegate.h index 6c93cea..432f564 100644 --- a/ConsoleDelegate.h +++ b/ConsoleDelegate.h @@ -41,12 +41,11 @@ -(void) setSkipErrors:(BOOL)skip; // AudioBinderDelegate methods --(void) updateStatus: (NSString *)filename handled:(UInt64)handledFrames total:(UInt64)totalFrames; --(void) conversionStart: (NSString*)filename format: (AudioStreamBasicDescription*)asbd formatDescription: (NSString*)description length: (UInt64)frames; --(BOOL) continueFailedConversion:(NSString*)filename reason:(NSString*)reason; --(void) conversionFinished: (NSString*)filename; +-(void) updateStatus: (AudioFile*)file handled:(UInt64)handledFrames total:(UInt64)totalFrames; +-(void) conversionStart: (AudioFile*)file format: (AudioStreamBasicDescription*)asbd formatDescription: (NSString*)description length: (UInt64)frames; +-(BOOL) continueFailedConversion:(AudioFile*)file reason:(NSString*)reason; +-(void) conversionFinished: (AudioFile*)file duration:(UInt32)duration; -(void) audiobookReady: (NSString*)filename duration: (UInt32)seconds; -(void) audiobookFailed: (NSString*)filename reason: (NSString*)reason; - @end diff --git a/ConsoleDelegate.m b/ConsoleDelegate.m index 1613285..0b503b4 100644 --- a/ConsoleDelegate.m +++ b/ConsoleDelegate.m @@ -57,20 +57,20 @@ -(void) setSkipErrors: (BOOL)skip _skipErrors = skip; } --(void)updateStatus: (NSString *)filename handled:(UInt64)handledFrames total:(UInt64)totalFrames +-(void)updateStatus: (AudioFile *)file handled:(UInt64)handledFrames total:(UInt64)totalFrames { if (!_quiet && _istty) { unsigned int percent = handledFrames * 100 / totalFrames; // got to the beginning of the line printf("\r"); printf("%s: [%3d%%] %lld/%lld", - [filename cStringUsingEncoding:NSUTF8StringEncoding], percent, + [file.filePath cStringUsingEncoding:NSUTF8StringEncoding], percent, handledFrames, totalFrames); fflush(stdout); } } --(void) conversionStart: (NSString*)filename +-(void) conversionStart: (AudioFile*)file format: (AudioStreamBasicDescription*)asbd formatDescription: (NSString*)description length: (UInt64)frames; @@ -79,7 +79,7 @@ -(void) conversionStart: (NSString*)filename if (_verbose) { printf("Stream info for %s:\n", - [filename cStringUsingEncoding:NSUTF8StringEncoding]); + [file.filePath cStringUsingEncoding:NSUTF8StringEncoding]); printf("\tFormatID: %s, FormatFlags: %08x\n", (char*)&asbd->mFormatID, asbd->mFormatFlags); printf("\tBytesPerPacket: %d, FramesPerPacker: %d, BytesPerFrame: %d\n", asbd->mBytesPerPacket, asbd->mFramesPerPacket, asbd->mBytesPerFrame); @@ -91,37 +91,43 @@ -(void) conversionStart: (NSString*)filename } - printf("%s: ", [filename cStringUsingEncoding:NSUTF8StringEncoding]); + printf("%s: ", [file.filePath cStringUsingEncoding:NSUTF8StringEncoding]); fflush(stdout); } } --(BOOL)continueFailedConversion:(NSString*)filename reason:(NSString*)reason +-(BOOL)continueFailedConversion:(AudioFile*)file reason:(NSString*)reason { if (!_quiet) { printf("%s: failed, %s", - [filename cStringUsingEncoding:NSUTF8StringEncoding], + [file.filePath cStringUsingEncoding:NSUTF8StringEncoding], [reason cStringUsingEncoding:NSUTF8StringEncoding]); if (_skipErrors) printf(", skipping..."); printf("\n"); } + file.duration = 0; + file.valid = NO; return _skipErrors; } --(void)conversionFinished:(NSString*)filename +-(void) conversionFinished: (AudioFile*)file duration:(UInt32)duration { if (!_quiet) { if (_istty) { // got to the beginning of the line printf("\r"); printf("%s: [100%%] \n", - [filename cStringUsingEncoding:NSUTF8StringEncoding]); + [file.filePath cStringUsingEncoding:NSUTF8StringEncoding]); } else { printf("done\n"); } } + + // set actual duration and mark as valid + file.duration = duration; + file.valid = NO; } -(void) audiobookReady: (NSString*)filename duration: (UInt32)seconds diff --git a/MetaEditor.h b/MetaEditor.h index 20ca54f..1144a99 100644 --- a/MetaEditor.h +++ b/MetaEditor.h @@ -9,11 +9,6 @@ #ifndef __METAEDITOR__ #define __METAEDITOR__ -typedef struct { - uint32_t duration; // milliseconds - char title[1024]; -} Chapter; - -int addChapters(const char *mp4, Chapter *chapters, int count); +int addChapters(const char *mp4, NSArray *chapters); #endif // __METAEDITOR__ diff --git a/MetaEditor.mm b/MetaEditor.mm index 6eb43c3..3921ea8 100644 --- a/MetaEditor.mm +++ b/MetaEditor.mm @@ -12,6 +12,7 @@ }; #include #include +#import "Chapter.h" using namespace std; const double CHAPTERTIMESCALE = 1000.0; @@ -37,7 +38,7 @@ return 0; } -int addChapters(const char *mp4, Chapter *chapters, int count) +int addChapters(const char *mp4, NSArray *chapters) { MP4FileHandle h = MP4Modify( mp4 ); @@ -68,11 +69,12 @@ int addChapters(const char *mp4, Chapter *chapters, int count) trackDuration /= trackTimeScale; vector mp4chapters; - for (int i = 0; i < count; i++) + for (Chapter *chapter in chapters) { MP4Chapter_t chap; - chap.duration = chapters[i].duration; - strncpy(chap.title, chapters[i].title, sizeof(chap.title)-1); + chap.duration = [chapter totalDuration]; + strncpy(chap.title, + [chapter.name UTF8String], sizeof(chap.title)-1); mp4chapters.push_back( chap ); } diff --git a/abbinder.m b/abbinder.m index dee0438..7292617 100644 --- a/abbinder.m +++ b/abbinder.m @@ -33,8 +33,12 @@ #import "AudioBinder.h" #import "ABLog.h" #import "ConsoleDelegate.h" +#import "AudioFile.h" #import "MP4File.h" +#include "MetaEditor.h" +#import "Chapter.h" + #define NUM_VALID_RATES 9 int validRates[NUM_VALID_RATES] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000}; @@ -45,6 +49,8 @@ void usage(char *cmd) printf("\t-a author\tset book author\n"); printf("\t-c 1|2\t\tnumber of channels in audiobook. Default: 2\n"); printf("\t-C file.png\tcover image\n"); + printf("\t-e\t\talias for -E ''\n"); + printf("\t-E template\tmake each file a chapter with name defined by template\n"); printf("\t-h\t\tshow this message\n"); printf("\t-i file\t\tget input files list from file, \"-\" for standard input\n"); printf("\t-q\t\tquiet mode (no output)\n"); @@ -55,6 +61,30 @@ void usage(char *cmd) } +NSString *makeChapterName(NSString *format, int chapterNum, AudioFile *file) +{ + + NSString *numStr = [[NSString stringWithFormat:@"%d", chapterNum + 1] retain]; + NSMutableString *name = [[NSMutableString stringWithString:format] retain]; + + [name replaceOccurrencesOfString:@"%a" + withString:file.artist + options:NSLiteralSearch + range:NSMakeRange(0, [name length])]; + + [name replaceOccurrencesOfString:@"%t" + withString:file.title + options:NSLiteralSearch + range:NSMakeRange(0, [name length])]; + + [name replaceOccurrencesOfString:@"%N" + withString:numStr + options:NSLiteralSearch + range:NSMakeRange(0, [name length])]; + + return name; +} + int main (int argc, char * argv[]) { int c, i; NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; @@ -64,6 +94,7 @@ int main (int argc, char * argv[]) { NSString *outFile = nil; NSString *inputFileList = nil; NSString *coverFile = nil; + NSMutableArray *inputFilenames; NSMutableArray *inputFiles; NSError *error; ConsoleDelegate *delegate; @@ -72,9 +103,13 @@ int main (int argc, char * argv[]) { BOOL skipErrors = NO; int channels = 2; float samplerate = 44100.; + BOOL withChapters = NO; + BOOL eachFileIsChapter = NO; + NSString *chapterNameFormat; + NSMutableArray *chapters = [[NSMutableArray alloc] init]; NSZombieEnabled = YES; - while ((c = getopt(argc, argv, "a:c:C:hi:qr:st:v")) != -1) { + while ((c = getopt(argc, argv, "a:c:C:eE:hi:qr:st:v")) != -1) { switch (c) { case 'h': usage(argv[0]); @@ -106,6 +141,24 @@ int main (int argc, char * argv[]) { case 'q': quiet = YES; break; + case 'e': + if (withChapters) { + fprintf(stderr, "You can't use both -e and -E together"); + exit(1); + } + withChapters = YES; + eachFileIsChapter = YES; + chapterNameFormat = @""; + break; + case 'E': + if (withChapters) { + fprintf(stderr, "You can't use both -e and -E together"); + exit(1); + } + withChapters = YES; + eachFileIsChapter = YES; + chapterNameFormat = [NSString stringWithUTF8String:optarg]; + break; default: usage(argv[0]); exit(1); @@ -148,6 +201,7 @@ int main (int argc, char * argv[]) { // Get input files from all possible sources: // + inputFilenames = [[NSMutableArray alloc] init]; inputFiles = [[NSMutableArray alloc] init]; if (inputFileList != nil) { @@ -177,26 +231,39 @@ int main (int argc, char * argv[]) { NSArray *filesList = [listContent componentsSeparatedByString: @"\n"]; for (NSString *file in filesList) if ([file length] > 0) - [inputFiles addObject:file]; + [inputFilenames addObject:file]; } // Now get input files from the remain of arguments while (optind < argc) { - [inputFiles addObject:[NSString stringWithUTF8String:argv[optind]]]; + NSString *path = [NSString stringWithUTF8String:argv[optind]]; + [inputFilenames addObject:path]; optind++; } - if ([inputFiles count] == 0) + if ([inputFilenames count] == 0) { fprintf(stderr, "No input file specified\n"); usage(argv[0]); exit(1); } + for (NSString *path in inputFilenames) { + AudioFile *file = [[AudioFile alloc] initWithPath:path]; + if (eachFileIsChapter) { + Chapter *chapter = [[Chapter alloc] init]; + chapter.name = makeChapterName(chapterNameFormat, + [chapters count], file); + [chapter addFile:file]; + [chapters addObject:chapter]; + } + [inputFiles addObject:file]; + } + // Feed files to binder [binder setOutputFile:outFile]; - for (NSString *file in inputFiles) + for (AudioFile *file in inputFiles) [binder addInputFile:file]; binder.channels = channels; @@ -227,6 +294,8 @@ int main (int argc, char * argv[]) { printf("done\n"); } + addChapters("1.m4b", chapters); + [pool drain]; return 0; }