diff options
Diffstat (limited to 'macosx/HBAudio.m')
-rw-r--r-- | macosx/HBAudio.m | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/macosx/HBAudio.m b/macosx/HBAudio.m new file mode 100644 index 000000000..0d5363e41 --- /dev/null +++ b/macosx/HBAudio.m @@ -0,0 +1,430 @@ +/* HBAudio.m $ + + This file is part of the HandBrake source code. + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License. */ + +#import "HBAudio.h" + +#import "HBTitle.h" +#import "HBAudioTrack.h" +#import "HBAudioTrackPreset.h" +#import "HBAudioDefaults.h" + +#import "NSCodingMacro.h" + +#include "hb.h" + +@implementation HBAudio + +- (instancetype)initWithTitle:(HBTitle *)title +{ + self = [super init]; + if (self) + { + _container = HB_MUX_MP4; + + _tracks = [[NSMutableArray alloc] init]; + _defaults = [[HBAudioDefaults alloc] init]; + + _noneTrack = [@{keyAudioTrackIndex: @0, + keyAudioTrackName: NSLocalizedString(@"None", @"None"), + keyAudioInputCodec: @0} retain]; + + NSMutableArray *sourceTracks = [NSMutableArray array]; + [sourceTracks addObject:_noneTrack]; + [sourceTracks addObjectsFromArray:title.audioTracks]; + _masterTrackArray = [sourceTracks copy]; + + [self switchingTrackFromNone: nil]; // this ensures there is a None track at the end of the lis + } + return self; +} + +- (void)addAllTracks +{ + [self addTracksFromDefaults:YES]; +} + +- (void)removeAll +{ + [self _clearAudioArray]; + [self switchingTrackFromNone:nil]; +} + +- (void)reloadDefaults +{ + [self addTracksFromDefaults:NO]; +} + +- (void)_clearAudioArray +{ + while (0 < [self countOfTracks]) + { + [self removeObjectFromTracksAtIndex: 0]; + } +} + +/** + * Uses the templateAudioArray from the preset to create the audios for the specified trackIndex. + * + * @param templateAudioArray the track template. + * @param trackIndex the index of the source track. + * @param firstOnly use only the first track of the template or all. + */ +- (void)_processPresetAudioArray:(NSArray *)templateAudioArray forTrack:(NSUInteger)trackIndex firstOnly:(BOOL)firstOnly +{ + for (HBAudioTrackPreset *preset in templateAudioArray) + { + BOOL fallenBack = NO; + HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; + [newAudio setController: self]; + [self insertObject: newAudio inTracksAtIndex: [self countOfTracks]]; + [newAudio setVideoContainerTag:@(self.container)]; + [newAudio setTrackFromIndex: (int)trackIndex]; + + const char *name = hb_audio_encoder_get_name(preset.encoder); + NSString *audioEncoder = nil; + + // Check if we need to use a fallback + if (name) + { + audioEncoder = @(name); + if (preset.encoder & HB_ACODEC_PASS_FLAG && + ![newAudio setCodecFromName:audioEncoder]) + { + int passthru, fallback; + fallenBack = YES; + passthru = hb_audio_encoder_get_from_name([audioEncoder UTF8String]); + fallback = hb_audio_encoder_get_fallback_for_passthru(passthru); + name = hb_audio_encoder_get_name(fallback); + + // If we couldn't find an encoder for the passthru format + // fall back to the selected encoder fallback + if (name == NULL) + { + name = hb_audio_encoder_get_name(self.defaults.encoderFallback); + } + } + else + { + name = hb_audio_encoder_sanitize_name([audioEncoder UTF8String]); + } + audioEncoder = @(name); + } + + // If our preset wants us to support a codec that the track does not support, instead + // of changing the codec we remove the audio instead. + if ([newAudio setCodecFromName:audioEncoder]) + { + const char *mixdown = hb_mixdown_get_name(preset.mixdown); + if (mixdown) + { + [newAudio setMixdownFromName: @(mixdown)]; + } + + const char *sampleRateName = hb_audio_samplerate_get_name(preset.sampleRate); + if (!sampleRateName) + { + [newAudio setSampleRateFromName: @"Auto"]; + } + else + { + [newAudio setSampleRateFromName: @(sampleRateName)]; + } + if (!fallenBack) + { + [newAudio setBitRateFromName: [NSString stringWithFormat:@"%d", preset.bitRate]]; + } + [newAudio setDrc: @(preset.drc)]; + [newAudio setGain: @(preset.gain)]; + } + else + { + [self removeObjectFromTracksAtIndex: [self countOfTracks] - 1]; + } + [newAudio release]; + + if (firstOnly) + { + break; + } + } +} + +/** + * Matches the source audio tracks with the specific language iso code. + * + * @param isoCode the iso code to match. + * @param selectOnlyFirst whether to match only the first track for the iso code. + * + * @return a NSIndexSet with the index of the matched tracks. + */ +- (NSIndexSet *)_tracksWithISOCode:(NSString *)isoCode selectOnlyFirst:(BOOL)selectOnlyFirst +{ + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; + + // We search for the prefix noting that our titles have the format %d: %s where the %s is the prefix + [self.masterTrackArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx) // Note that we skip the "None" track + { + if ([isoCode isEqualToString:@"und"] || [obj[keyAudioTrackLanguageIsoCode] isEqualToString:isoCode]) + { + [indexes addIndex:idx]; + + if (selectOnlyFirst) + { + *stop = YES; + } + } + } + }]; + + return indexes; +} + +- (void)_processPresetAudioArray:(NSArray *)templateAudioArray forTracks:(NSIndexSet *)trackIndexes firstOnly:(BOOL)firstOnly +{ + __block BOOL firsTrack = firstOnly; + [trackIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + // Add the track + [self _processPresetAudioArray: self.defaults.tracksArray forTrack:idx firstOnly:firsTrack]; + firsTrack = self.defaults.secondaryEncoderMode ? YES : NO; + }]; +} + +- (void)addTracksFromDefaults:(BOOL)allTracks +{ + BOOL firstTrack = NO; + NSMutableIndexSet *tracksAdded = [NSMutableIndexSet indexSet]; + NSMutableIndexSet *tracksToAdd = [NSMutableIndexSet indexSet]; + + // Reinitialize the configured list of audio tracks + [self _clearAudioArray]; + + if (self.defaults.trackSelectionBehavior != HBAudioTrackSelectionBehaviorNone) + { + // Add tracks of Default and Alternate Language by name + for (NSString *languageISOCode in self.defaults.trackSelectionLanguages) + { + NSMutableIndexSet *tracksIndexes = [[self _tracksWithISOCode: languageISOCode + selectOnlyFirst: self.defaults.trackSelectionBehavior == HBAudioTrackSelectionBehaviorFirst] mutableCopy]; + [tracksIndexes removeIndexes:tracksAdded]; + if (tracksIndexes.count) + { + [self _processPresetAudioArray:self.defaults.tracksArray forTracks:tracksIndexes firstOnly:firstTrack]; + firstTrack = self.defaults.secondaryEncoderMode ? YES : NO; + [tracksAdded addIndexes:tracksIndexes]; + } + [tracksIndexes release]; + } + + // If no preferred Language was found, add standard track 1 + if (tracksAdded.count == 0 && self.masterTrackArray.count > 1) + { + [tracksToAdd addIndex:1]; + } + } + + // If all tracks should be added, add all track numbers that are not yet processed + if (allTracks) + { + [tracksToAdd addIndexesInRange:NSMakeRange(1, self.masterTrackArray.count - 1)]; + [tracksToAdd removeIndexes:tracksAdded]; + } + + if (tracksToAdd.count) + { + [self _processPresetAudioArray:self.defaults.tracksArray forTracks:tracksToAdd firstOnly:firstTrack]; + } + + // Add an None item + [self switchingTrackFromNone: nil]; +} + +- (BOOL)anyCodecMatches:(int)codec +{ + BOOL retval = NO; + NSUInteger audioArrayCount = [self countOfTracks]; + for (NSUInteger i = 0; i < audioArrayCount && !retval; i++) + { + HBAudioTrack *anAudio = [self objectInTracksAtIndex: i]; + if ([anAudio enabled] && codec == [[anAudio codec][keyAudioCodec] intValue]) + { + retval = YES; + } + } + return retval; +} + +- (void)addNewAudioTrack +{ + HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; + [newAudio setController: self]; + [self insertObject: newAudio inTracksAtIndex: [self countOfTracks]]; + [newAudio setVideoContainerTag:@(self.container)]; + [newAudio setTrack: self.noneTrack]; + [newAudio setDrc: @0.0f]; + [newAudio setGain: @0.0f]; + [newAudio release]; +} + +#pragma mark - +#pragma mark Notification Handling + +- (void)settingTrackToNone:(HBAudioTrack *)newNoneTrack +{ + // If this is not the last track in the array we need to remove it. We then need to see if a new + // one needs to be added (in the case when we were at maximum count and this switching makes it + // so we are no longer at maximum. + NSUInteger index = [self.tracks indexOfObject: newNoneTrack]; + + if (NSNotFound != index && index < [self countOfTracks] - 1) + { + [self removeObjectFromTracksAtIndex: index]; + } + [self switchingTrackFromNone: nil]; // see if we need to add one to the list +} + +- (void)switchingTrackFromNone:(HBAudioTrack *)noLongerNoneTrack +{ + NSUInteger count = [self countOfTracks]; + BOOL needToAdd = NO; + + // If there is no last track that is None we add one. + if (0 < count) + { + HBAudioTrack *lastAudio = [self objectInTracksAtIndex: count - 1]; + if ([lastAudio enabled]) + { + needToAdd = YES; + } + } + else + { + needToAdd = YES; + } + + if (needToAdd) + { + [self addNewAudioTrack]; + } +} + +// This gets called whenever the video container changes. +- (void)setContainer:(int)container +{ + _container = container; + + // Update each of the instances because this value influences possible settings. + for (HBAudioTrack *audioObject in self.tracks) + { + [audioObject setVideoContainerTag:@(container)]; + } + + // Update the Auto Passthru Fallback Codec Popup + // lets get the tag of the currently selected item first so we might reset it later + [self.defaults validateEncoderFallbackForVideoContainer:container]; +} + +#pragma mark - NSCopying + +- (instancetype)copyWithZone:(NSZone *)zone +{ + HBAudio *copy = [[[self class] alloc] init]; + + if (copy) + { + copy->_container = _container; + + copy->_noneTrack = [_noneTrack copy]; + copy->_masterTrackArray = [_masterTrackArray copy]; + + copy->_tracks = [[NSMutableArray alloc] init]; + [_tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx < _tracks.count) + { + HBAudioTrack *trackCopy = [obj copy]; + trackCopy.controller = copy; + [copy->_tracks addObject:[trackCopy autorelease]]; + } + }]; + + copy->_defaults = [_defaults copy]; + } + + return copy; +} + +#pragma mark - NSCoding + +- (void)encodeWithCoder:(NSCoder *)coder +{ + [coder encodeInt:1 forKey:@"HBAudioVersion"]; + + encodeInt(_container); + + encodeObject(_noneTrack); + encodeObject(_masterTrackArray); + encodeObject(_tracks); + + encodeObject(_defaults); +} + +- (id)initWithCoder:(NSCoder *)decoder +{ + self = [super init]; + + decodeInt(_container); + + decodeObject(_noneTrack); + decodeObject(_masterTrackArray); + decodeObject(_tracks); + + for (HBAudioTrack *track in _tracks) + { + track.controller = self; + } + + decodeObject(_defaults); + + return self; +} + +#pragma mark - Presets + +- (void)writeToPreset:(NSMutableDictionary *)preset +{ + [self.defaults writeToPreset:preset]; +} + +- (void)applyPreset:(NSDictionary *)preset +{ + [self.defaults applyPreset:preset]; + [self addTracksFromDefaults:NO]; +} + +#pragma mark - +#pragma mark KVC + +- (NSUInteger) countOfTracks +{ + return self.tracks.count; +} + +- (HBAudioTrack *)objectInTracksAtIndex:(NSUInteger)index +{ + return self.tracks[index]; +} + +- (void)insertObject:(HBAudioTrack *)track inTracksAtIndex:(NSUInteger)index; +{ + [self.tracks insertObject:track atIndex:index]; +} + +- (void)removeObjectFromTracksAtIndex:(NSUInteger)index +{ + [self.tracks removeObjectAtIndex:index]; +} + +@end |