/* HBAudioSettings.m $ This file is part of the HandBrake source code. Homepage: . It may be used under the terms of the GNU General Public License. */ #import "HBAudioDefaults.h" #import "HBAudioTrackPreset.h" #import "HBCodingUtilities.h" #import "HBMutablePreset.h" #import "hb.h" #import "lang.h" @interface HBAudioDefaults () @property (nonatomic, readwrite) int container; @end @implementation HBAudioDefaults - (instancetype)init { self = [super init]; if (self) { _encoderFallback = HB_ACODEC_AC3; _trackSelectionLanguages = [[NSMutableArray alloc] init]; _tracksArray = [[NSMutableArray alloc] init]; _trackSelectionBehavior = HBAudioTrackSelectionBehaviorFirst; _container = HB_MUX_MKV; } return self; } - (void)addTrack { HBAudioTrackPreset *track = [[HBAudioTrackPreset alloc] initWithContainer:self.container]; track.undo = self.undo; track.fallbackEncoder = self.encoderFallback; [self insertObject:track inTracksArrayAtIndex:[self countOfTracksArray]]; } #pragma mark - Properties - (void)setTrackSelectionBehavior:(HBAudioTrackSelectionBehavior)trackSelectionBehavior { if (trackSelectionBehavior != _trackSelectionBehavior) { [[self.undo prepareWithInvocationTarget:self] setTrackSelectionBehavior:_trackSelectionBehavior]; } _trackSelectionBehavior = trackSelectionBehavior; } - (void)setTrackSelectionLanguages:(NSMutableArray *)trackSelectionLanguages { if (trackSelectionLanguages != _trackSelectionLanguages) { [[self.undo prepareWithInvocationTarget:self] setTrackSelectionLanguages:_trackSelectionLanguages]; } _trackSelectionLanguages = trackSelectionLanguages; } - (void)setAllowAACPassthru:(BOOL)allowAACPassthru { if (allowAACPassthru != _allowAACPassthru) { [[self.undo prepareWithInvocationTarget:self] setAllowAACPassthru:_allowAACPassthru]; } _allowAACPassthru = allowAACPassthru; } - (void)setAllowAC3Passthru:(BOOL)allowAC3Passthru { if (allowAC3Passthru != _allowAC3Passthru) { [[self.undo prepareWithInvocationTarget:self] setAllowAC3Passthru:_allowAC3Passthru]; } _allowAC3Passthru = allowAC3Passthru; } - (void)setAllowEAC3Passthru:(BOOL)allowEAC3Passthru { if (allowEAC3Passthru != _allowEAC3Passthru) { [[self.undo prepareWithInvocationTarget:self] setAllowEAC3Passthru:_allowEAC3Passthru]; } _allowEAC3Passthru = allowEAC3Passthru; } - (void)setAllowDTSHDPassthru:(BOOL)allowDTSHDPassthru { if (allowDTSHDPassthru != _allowDTSHDPassthru) { [[self.undo prepareWithInvocationTarget:self] setAllowDTSHDPassthru:_allowDTSHDPassthru]; } _allowDTSHDPassthru = allowDTSHDPassthru; } - (void)setAllowDTSPassthru:(BOOL)allowDTSPassthru { if (allowDTSPassthru != _allowDTSPassthru) { [[self.undo prepareWithInvocationTarget:self] setAllowDTSPassthru:_allowDTSPassthru]; } _allowDTSPassthru = allowDTSPassthru; } - (void)setAllowMP3Passthru:(BOOL)allowMP3Passthru { if (allowMP3Passthru != _allowMP3Passthru) { [[self.undo prepareWithInvocationTarget:self] setAllowMP3Passthru:_allowMP3Passthru]; } _allowMP3Passthru = allowMP3Passthru; } - (void)setAllowTrueHDPassthru:(BOOL)allowTrueHDPassthru { if (allowTrueHDPassthru != _allowTrueHDPassthru) { [[self.undo prepareWithInvocationTarget:self] setAllowTrueHDPassthru:_allowTrueHDPassthru]; } _allowTrueHDPassthru = allowTrueHDPassthru; } - (void)setAllowFLACPassthru:(BOOL)allowFLACPassthru { if (allowFLACPassthru != _allowFLACPassthru) { [[self.undo prepareWithInvocationTarget:self] setAllowFLACPassthru:_allowFLACPassthru]; } _allowFLACPassthru = allowFLACPassthru; } - (void)setEncoderFallback:(int)encoderFallback { if (encoderFallback != _encoderFallback) { [[self.undo prepareWithInvocationTarget:self] setEncoderFallback:_encoderFallback]; } _encoderFallback = encoderFallback; for (HBAudioTrackPreset *track in self.tracksArray) { track.fallbackEncoder = encoderFallback; } } - (void)setSecondaryEncoderMode:(BOOL)secondaryEncoderMode { if (secondaryEncoderMode != _secondaryEncoderMode) { [[self.undo prepareWithInvocationTarget:self] setSecondaryEncoderMode:_secondaryEncoderMode]; } _secondaryEncoderMode = secondaryEncoderMode; } - (NSArray *)audioEncoderFallbacks { NSMutableArray *fallbacks = [[NSMutableArray alloc] init]; for (const hb_encoder_t *audio_encoder = hb_audio_encoder_get_next(NULL); audio_encoder != NULL; audio_encoder = hb_audio_encoder_get_next(audio_encoder)) { if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) == 0 && (audio_encoder->muxers & self.container)) { [fallbacks addObject:@(audio_encoder->name)]; } } return fallbacks; } - (NSString *)isoCodeForNativeLang:(NSString *)language { const iso639_lang_t *lang = lang_get_next(NULL); for (lang = lang_get_next(lang); lang != NULL; lang = lang_get_next(lang)) { NSString *nativeLanguage = strlen(lang->native_name) ? @(lang->native_name) : @(lang->eng_name); if ([language isEqualToString:nativeLanguage]) { return @(lang->iso639_2); } } return nil; } #pragma mark - HBPresetCoding - (void)applyPreset:(HBPreset *)preset { // Track selection behavior if ([preset[@"AudioTrackSelectionBehavior"] isEqualToString:@"first"]) { self.trackSelectionBehavior = HBAudioTrackSelectionBehaviorFirst; } else if ([preset[@"AudioTrackSelectionBehavior"] isEqualToString:@"all"]) { self.trackSelectionBehavior = HBAudioTrackSelectionBehaviorAll; } else if ([preset[@"AudioTrackSelectionBehavior"] isEqualToString:@"none"]) { self.trackSelectionBehavior = HBAudioTrackSelectionBehaviorNone; } else { // Keep the previous behavior for the old presets self.trackSelectionBehavior = HBAudioTrackSelectionBehaviorFirst; } self.trackSelectionLanguages = [NSMutableArray arrayWithArray:preset[@"AudioLanguageList"]]; // If the preset is one of the built in, set some additional options if ([preset[@"Type"] intValue] == 0) { if (self.trackSelectionLanguages.count == 0 || [self.trackSelectionLanguages.firstObject isEqualToString:@"und"]) { if ([[NSUserDefaults standardUserDefaults] stringForKey:@"AlternateLanguage"]) { NSString *lang = [self isoCodeForNativeLang:[[NSUserDefaults standardUserDefaults] stringForKey:@"AlternateLanguage"]]; if (lang) { [self.trackSelectionLanguages insertObject:lang atIndex:0]; } } if ([[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"]) { NSString *lang = [self isoCodeForNativeLang:[[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"]]; if (lang) { [self.trackSelectionLanguages insertObject:lang atIndex:0]; } } } } // Auto Passthru settings // first, disable all encoders self.allowAACPassthru = NO; self.allowAC3Passthru = NO; self.allowDTSPassthru = NO; self.allowDTSHDPassthru = NO; self.allowEAC3Passthru = NO; self.allowFLACPassthru = NO; self.allowMP3Passthru = NO; self.allowTrueHDPassthru = NO; // then, enable allowed passthru encoders for (NSString *copyMask in preset[@"AudioCopyMask"]) { int allowedPassthru = hb_audio_encoder_get_from_name(copyMask.UTF8String); if (allowedPassthru & HB_ACODEC_PASS_FLAG) { switch (allowedPassthru) { case HB_ACODEC_AAC_PASS: self.allowAACPassthru = YES; break; case HB_ACODEC_AC3_PASS: self.allowAC3Passthru = YES; break; case HB_ACODEC_DCA_PASS: self.allowDTSPassthru = YES; break; case HB_ACODEC_DCA_HD_PASS: self.allowDTSHDPassthru = YES; break; case HB_ACODEC_EAC3_PASS: self.allowEAC3Passthru = YES; break; case HB_ACODEC_FLAC_PASS: self.allowFLACPassthru = YES; break; case HB_ACODEC_MP3_PASS: self.allowMP3Passthru = YES; break; case HB_ACODEC_TRUEHD_PASS: self.allowTrueHDPassthru = YES; break; default: break; } } } self.secondaryEncoderMode = [preset[@"AudioSecondaryEncoderMode"] boolValue]; if (preset[@"AudioEncoderFallback"]) { // map legacy encoder names via libhb self.encoderFallback = hb_audio_encoder_get_from_name([preset[@"AudioEncoderFallback"] UTF8String]); } while ([self countOfTracksArray]) { [self removeObjectFromTracksArrayAtIndex:0]; } for (NSDictionary *track in preset[@"AudioList"]) { HBAudioTrackPreset *newTrack = [[HBAudioTrackPreset alloc] init]; newTrack.fallbackEncoder = self.encoderFallback; if ([track[@"AudioEncoder"] isKindOfClass:[NSString class]]) { newTrack.encoder = hb_audio_encoder_get_from_name([track[@"AudioEncoder"] UTF8String]); } if ([track[@"AudioMixdown"] isKindOfClass:[NSString class]]) { newTrack.mixdown = hb_mixdown_get_from_name([track[@"AudioMixdown"] UTF8String]); } if ([track[@"AudioSamplerate"] isKindOfClass:[NSString class]]) { newTrack.sampleRate = hb_audio_samplerate_get_from_name([track[@"AudioSamplerate"] UTF8String]); // Set to "Auto" if we didn't find a valid sample rate. if (newTrack.sampleRate == -1) { newTrack.sampleRate = 0; } } newTrack.bitRate = [track[@"AudioBitrate"] intValue]; newTrack.drc = [track[@"AudioTrackDRCSlider"] doubleValue]; newTrack.gain = [track[@"AudioTrackGainSlider"] doubleValue]; [self insertObject:newTrack inTracksArrayAtIndex:[self countOfTracksArray]]; } } - (void)applyPreset:(HBPreset *)preset jobSettings:(NSDictionary *)settings { [self applyPreset:preset]; } - (void)writeToPreset:(HBMutablePreset *)preset { // Track selection behavior if (self.trackSelectionBehavior == HBAudioTrackSelectionBehaviorFirst) { preset[@"AudioTrackSelectionBehavior"] = @"first"; } else if (self.trackSelectionBehavior == HBAudioTrackSelectionBehaviorAll) { preset[@"AudioTrackSelectionBehavior"] = @"all"; } else { preset[@"AudioTrackSelectionBehavior"] = @"none"; } preset[@"AudioLanguageList"] = [self.trackSelectionLanguages copy]; // Passthru settings NSMutableArray *copyMask = [NSMutableArray array]; if (self.allowAACPassthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_AAC_PASS))]; } if (self.allowAC3Passthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_AC3_PASS))]; } if (self.allowEAC3Passthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_EAC3_PASS))]; } if (self.allowDTSHDPassthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_DCA_HD_PASS))]; } if (self.allowDTSPassthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_DCA_PASS))]; } if (self.allowMP3Passthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_MP3_PASS))]; } if (self.allowTrueHDPassthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_TRUEHD_PASS))]; } if (self.allowFLACPassthru) { [copyMask addObject:@(hb_audio_encoder_get_short_name(HB_ACODEC_FLAC_PASS))]; } preset[@"AudioCopyMask"] = [copyMask copy]; preset[@"AudioEncoderFallback"] = @(hb_audio_encoder_get_short_name(self.encoderFallback)); preset[@"AudioSecondaryEncoderMode"] = @(self.secondaryEncoderMode); NSMutableArray *audioList = [[NSMutableArray alloc] init]; for (HBAudioTrackPreset *track in self.tracksArray) { NSString *sampleRate = @"auto"; if (hb_audio_samplerate_get_name(track.sampleRate)) { sampleRate = @(hb_audio_samplerate_get_name(track.sampleRate)); } NSDictionary *newTrack = @{@"AudioEncoder": @(hb_audio_encoder_get_short_name(track.encoder)), @"AudioMixdown": @(hb_mixdown_get_short_name(track.mixdown)), @"AudioSamplerate": sampleRate, @"AudioBitrate": @(track.bitRate), @"AudioTrackDRCSlider": @(track.drc), @"AudioTrackGainSlider": @(track.gain)}; [audioList addObject:newTrack]; } preset[@"AudioList"] = audioList; } - (void)validateEncoderFallbackForVideoContainer:(int)container { BOOL isValid = NO; for (const hb_encoder_t *audio_encoder = hb_audio_encoder_get_next(NULL); audio_encoder != NULL; audio_encoder = hb_audio_encoder_get_next(audio_encoder)) { if (audio_encoder->muxers & container) { if (audio_encoder->codec == self.encoderFallback) { isValid = YES; break; } } } if (!isValid) { self.encoderFallback = HB_ACODEC_AC3; } self.container = container; } - (void)setUndo:(NSUndoManager *)undo { _undo = undo; [self.tracksArray makeObjectsPerformSelector:@selector(setUndo:) withObject:undo]; } #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone { HBAudioDefaults *copy = [[[self class] alloc] init]; if (copy) { copy->_trackSelectionBehavior = _trackSelectionBehavior; copy->_trackSelectionLanguages = [_trackSelectionLanguages mutableCopy]; copy->_tracksArray = [[NSMutableArray alloc] initWithArray:_tracksArray copyItems:YES]; copy->_allowAACPassthru = _allowAACPassthru; copy->_allowAC3Passthru = _allowAC3Passthru; copy->_allowEAC3Passthru = _allowEAC3Passthru; copy->_allowDTSHDPassthru = _allowDTSHDPassthru; copy->_allowDTSPassthru = _allowDTSPassthru; copy->_allowMP3Passthru = _allowMP3Passthru; copy->_allowTrueHDPassthru = _allowTrueHDPassthru; copy->_allowFLACPassthru = _allowFLACPassthru; copy->_encoderFallback = _encoderFallback; copy->_container = _container; copy->_secondaryEncoderMode = _secondaryEncoderMode; } return copy; } #pragma mark - NSCoding + (BOOL)supportsSecureCoding { return YES; } - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeInt:2 forKey:@"HBAudioDefaultsVersion"]; encodeInteger(_trackSelectionBehavior); encodeObject(_trackSelectionLanguages); encodeObject(_tracksArray); encodeBool(_allowAACPassthru); encodeBool(_allowAC3Passthru); encodeBool(_allowEAC3Passthru); encodeBool(_allowDTSHDPassthru); encodeBool(_allowDTSPassthru); encodeBool(_allowMP3Passthru); encodeBool(_allowTrueHDPassthru); encodeBool(_allowFLACPassthru); encodeInt(_encoderFallback); encodeInt(_container); encodeBool(_secondaryEncoderMode); } - (instancetype)initWithCoder:(NSCoder *)decoder { self = [super init]; decodeInteger(_trackSelectionBehavior); decodeObject(_trackSelectionLanguages, NSMutableArray); decodeObject(_tracksArray, NSMutableArray); decodeBool(_allowAACPassthru); decodeBool(_allowAC3Passthru); decodeBool(_allowEAC3Passthru); decodeBool(_allowDTSHDPassthru); decodeBool(_allowDTSPassthru); decodeBool(_allowMP3Passthru); decodeBool(_allowTrueHDPassthru); decodeBool(_allowFLACPassthru); decodeInt(_encoderFallback); decodeInt(_container); decodeBool(_secondaryEncoderMode); return self; } #pragma mark KVC - (NSUInteger)countOfTracksArray { return self.tracksArray.count; } - (HBAudioTrackPreset *)objectInTracksArrayAtIndex:(NSUInteger)index { return self.tracksArray[index]; } - (void)insertObject:(HBAudioTrackPreset *)track inTracksArrayAtIndex:(NSUInteger)index; { [[self.undo prepareWithInvocationTarget:self] removeObjectFromTracksArrayAtIndex:index]; [self.tracksArray insertObject:track atIndex:index]; } - (void)removeObjectFromTracksArrayAtIndex:(NSUInteger)index { id obj = self.tracksArray[index]; [[self.undo prepareWithInvocationTarget:self] insertObject:obj inTracksArrayAtIndex:index]; [self.tracksArray removeObjectAtIndex:index]; } @end