/* vim: set ft=objc ts=4 et sw=4 nowrap: */ /* * ConvertAudioHelper.m * * Copyright (c) 2005 * * Author: Andreas Heppel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ConvertAudioHelper.h" #include "Constants.h" #include "Functions.h" #include "AppController.h" #include "Track.h" #include "Project.h" #include "Burn/ExternalTools.h" /** * A private helper class to hold the data for one * conversion process. */ @interface ConvertProcess : NSObject { @public id tool; NSMutableArray *tracks; } - (id) init; - (void) setTool: (id) aTool; @end @implementation ConvertProcess - (id) init { self = [super init]; tool = nil; tracks = [NSMutableArray new]; return self; } - (void) dealloc { RELEASE(tracks); RELEASE(tool); } - (void) setTool: (id) aTool { ASSIGN(tool, aTool); } @end @implementation ConvertAudioHelper - (id) initWithController: (BurnProgressController *)aController { self = [super init]; if (self) { controller = aController; tempFiles = nil; processes = [NSMutableArray new]; } return self; } - (void) dealloc { RELEASE(tempFiles); RELEASE(processes); } - (enum StartHelperStatus) start: (NSArray *)audioTracks { enum StartHelperStatus ret = Done; NSDictionary *burnParameters = [controller burnParameters]; NSMutableDictionary *processHelper = [NSMutableDictionary new]; NSEnumerator *eTracks = [audioTracks objectEnumerator]; Track *track = nil; if ((audioTracks == nil) || ([audioTracks count] == 0)) { return Done; } /* * We iterate over the list of audio tracks. For each type we try to * find an appropriate converter bundle. If we do, we add the track * to an array which itself will be associated with the bundle's main * object. */ while ((track = [eTracks nextObject]) != nil) { NSString *trackType = [track type]; NSString *type = nil; /* * Ignore the built-in types. */ if ([trackType isEqualToString: @"audio:cd"] || [trackType isEqualToString: @"audio:wav"] || [trackType isEqualToString: @"audio:au"]) continue; if (![trackType hasPrefix: @"audio:"]) continue; type = [trackType substringFromIndex: [@"audio:" length]]; /* * Try to get a handle to the converter bundle for this file type. * We try to get this from the user defaults. If they are not configured, * yet, we simply get the first one registered with the app. */ id tool = [[AppController appController] bundleForKey: [[burnParameters objectForKey: @"SelectedTools"] objectForKey: type]]; if (!tool) { NSArray *c = [[AppController appController] bundlesForFileType: type]; tool = [c objectAtIndex: 0]; } if (!tool) { NSRunAlertPanel(APP_NAME, [NSString stringWithFormat: @"%@\n%@", _(@"ConvertAudioHelper.noProgram"), _(@"Common.stopProcess")], _(@"Common.OK"), nil, nil); ret = Failed; goto clean_up; } ConvertProcess *process = [processHelper objectForKey: type]; if (!process) { process = [ConvertProcess new]; [process setTool: tool]; [processHelper setObject: process forKey: type]; [processes addObject: process]; } [process->tracks addObject: track]; } /* * Release the helper dict and start the second stage. */ if ([processes count] != 0) { ret = Started; nextProcess = 0; [self startNextProcess]; } clean_up: RELEASE(processHelper); return ret; } - (void) stop: (BOOL) immediately { if (currentTool != nil) { [(id)currentTool stop: immediately]; logToConsole(MessageStatusError, _(@"Common.cancelled")); } } - (void) cleanUp: (BOOL) success { NSDictionary *burnParameters = [controller burnParameters]; BOOL keepTempFiles = [[[[controller burnParameters] objectForKey: @"SessionParameters"] objectForKey: @"KeepTempWavs"] boolValue]; NSFileManager *fileMan = [NSFileManager defaultManager]; NSEnumerator *e = [processes objectEnumerator]; ConvertProcess *p; while ((p = [e nextObject]) != nil) { [p->tool cleanUp]; } if ((keepTempFiles == NO) && tempFiles) { NSString *file; int i, count = [tempFiles count]; for (i = 0; i < count; i++) { file = [tempFiles objectAtIndex: i]; logToConsole(MessageStatusInfo, [NSString stringWithFormat: _(@"Common.removeTempFile"), file]); if (![fileMan removeFileAtPath: file handler: nil]) { logToConsole(MessageStatusError, _(@"Common.removeFail")); } } RELEASE(tempFiles); tempFiles = nil; } } - (void) startNextProcess { if (nextProcess >= [processes count]) { logToConsole(MessageStatusInfo, _(@"ConvertAudioHelper.success")); [controller nextStage: YES]; return; } ConvertProcess *process = [processes objectAtIndex: nextProcess++]; NSString *type = [process->tool fileType]; [controller setTitle: [NSString stringWithFormat: _(@"ConvertAudioHelper.preparing"), type]]; [controller setTrackProgress: 0. andLabel: @""]; [controller setEntireProgress: 0. andLabel: _(@"ConvertAudioHelper.allTracks")]; // now get it [NSThread detachNewThreadSelector: @selector(convertThread:) toTarget: self withObject: process]; [NSTimer scheduledTimerWithTimeInterval: 0.4 target: self selector: @selector(updateStatus:) userInfo: nil repeats: NO]; } - (void) convertThread: (id)anObject { int i; BOOL result = YES; id pool = [NSAutoreleasePool new]; NSDictionary *burnParameters = [controller burnParameters]; id converter = ((ConvertProcess *)anObject)->tool; NSArray *tracks = ((ConvertProcess *)anObject)->tracks; currentTool = (id)converter; result = [converter convertTracks: tracks withParameters: burnParameters]; if (result) { if (!tempFiles) { tempFiles = [NSMutableArray new]; } // add file to list of temporary files for (i = 0; i < [tracks count]; i++) { Track *track = [tracks objectAtIndex: i]; [tempFiles addObject: [track storage]]; } } RELEASE(pool); [NSThread exit]; } - (void) updateStatus: (id)timer { SConvertStatus status; id converter = currentTool; status = [converter getStatus]; [controller setMiniwindowToTrack: status.trackProgress Entire: status.entireProgress]; if (status.processStatus == isConverting) { [controller setTrackProgress: status.trackProgress andLabel: [NSString stringWithFormat: _(@"Common.trackTitle"), status.trackName]]; [controller setEntireProgress: status.entireProgress andLabel: nil]; [NSTimer scheduledTimerWithTimeInterval: 0.4 target: self selector: @selector(updateStatus:) userInfo: nil repeats: NO]; return; } // did we stop by 'Cancel' or by terminated thread? if (status.processStatus == isCancelled) { [controller nextStage: NO]; } else { [controller setTrackProgress: status.trackProgress andLabel: nil]; [controller setEntireProgress: status.entireProgress andLabel: nil]; /* * -startNextProcess will determine whether we are * finished or not. Hence, we do not need to call * the controller's -nextStage: method here. */ [self startNextProcess]; } } @end