diff options
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/English.lproj/Audio.xib | 142 | ||||
-rw-r--r-- | macosx/HBAudio.h | 6 | ||||
-rw-r--r-- | macosx/HBAudio.m | 455 | ||||
-rw-r--r-- | macosx/HBAudioTrack.h | 65 | ||||
-rw-r--r-- | macosx/HBAudioTrack.m | 761 | ||||
-rw-r--r-- | macosx/HBAudioTrackPreset.h | 16 | ||||
-rw-r--r-- | macosx/HBAudioTrackPreset.m | 143 | ||||
-rw-r--r-- | macosx/HBAudioTransformers.h | 25 | ||||
-rw-r--r-- | macosx/HBAudioTransformers.m | 143 | ||||
-rw-r--r-- | macosx/HBJob+HBJobConversion.m | 28 | ||||
-rw-r--r-- | macosx/HBJob+UIAdditions.m | 66 | ||||
-rw-r--r-- | macosx/HandBrake.xcodeproj/project.pbxproj | 8 |
12 files changed, 866 insertions, 992 deletions
diff --git a/macosx/English.lproj/Audio.xib b/macosx/English.lproj/Audio.xib index ff7a77e8e..d24db6cb9 100644 --- a/macosx/English.lproj/Audio.xib +++ b/macosx/English.lproj/Audio.xib @@ -1,9 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="16A238m" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> +<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11198.2" systemVersion="16A313a" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"> <dependencies> <deployment identifier="macosx"/> - <development version="7000" identifier="xcode"/> - <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/> + <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11198.2"/> </dependencies> <objects> <customObject id="-2" userLabel="File's Owner" customClass="HBAudioController"> @@ -13,32 +12,7 @@ </customObject> <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/> <customObject id="-3" userLabel="Application" customClass="NSObject"/> - <arrayController preservesSelection="NO" avoidsEmptySelection="NO" id="rzb-Si-Kpf"> - <declaredKeys> - <string>tracks</string> - <string>tracks.keyAudioTrackName</string> - <string>track</string> - <string>enabled</string> - <string>mixdownEnabled</string> - <string>drc</string> - <string>codecs</string> - <string>codecs.keyAudioCodecName</string> - <string>codec</string> - <string>mixdowns</string> - <string>mixdowns.keyAudioMixdownName</string> - <string>mixdown</string> - <string>sampleRates</string> - <string>sampleRates.keyAudioSampleRateName</string> - <string>sampleRate</string> - <string>bitRates</string> - <string>bitRates.keyAudioBitrateName</string> - <string>bitRate</string> - <string>DRCEnabled</string> - <string>gain</string> - <string>PassThruEnabled</string> - <string>PassThruDisabled</string> - <string>bitrateEnabled</string> - </declaredKeys> + <arrayController objectClassName="HBAudioTrack" preservesSelection="NO" avoidsEmptySelection="NO" id="rzb-Si-Kpf"> <connections> <binding destination="-2" name="contentArray" keyPath="self.audio.tracks" id="W0u-41-zX4"/> </connections> @@ -148,9 +122,8 @@ <menu key="menu" id="hXU-xc-dxL"/> </popUpButtonCell> <connections> - <binding destination="wJH-tU-zL6" name="content" keyPath="objectValue.masterTrackArray" id="pvQ-pN-TdG"/> - <binding destination="wJH-tU-zL6" name="contentValues" keyPath="objectValue.masterTrackArray.keyAudioTrackName" previousBinding="pvQ-pN-TdG" id="Wec-Fo-DSj"/> - <binding destination="wJH-tU-zL6" name="selectedObject" keyPath="objectValue.track" previousBinding="Wec-Fo-DSj" id="Qoy-ym-QWQ"/> + <binding destination="wJH-tU-zL6" name="selectedIndex" keyPath="objectValue.sourceTrackIdx" previousBinding="LnY-7P-b37" id="CWi-rR-r7Y"/> + <binding destination="wJH-tU-zL6" name="content" keyPath="objectValue.sourceTracksArray" id="LnY-7P-b37"/> </connections> </popUpButton> </subviews> @@ -190,10 +163,13 @@ </connections> </popUpButtonCell> <connections> - <binding destination="8ed-5g-y6e" name="selectedObject" keyPath="objectValue.codec" previousBinding="cvm-Mj-Drh" id="sEl-p7-19g"/> - <binding destination="8ed-5g-y6e" name="content" keyPath="objectValue.codecs" id="pxN-GH-Dfr"/> - <binding destination="8ed-5g-y6e" name="contentValues" keyPath="objectValue.codecs.keyAudioCodecName" previousBinding="pxN-GH-Dfr" id="cvm-Mj-Drh"/> - <binding destination="8ed-5g-y6e" name="enabled" keyPath="objectValue.enabled" id="V0U-a3-ZBW"/> + <binding destination="8ed-5g-y6e" name="selectedValue" keyPath="objectValue.encoder" previousBinding="Ojx-pY-iTj" id="aRL-2h-rz1"> + <dictionary key="options"> + <string key="NSValueTransformerName">HBEncoderTransformer</string> + </dictionary> + </binding> + <binding destination="8ed-5g-y6e" name="content" keyPath="objectValue.encoders" id="Ojx-pY-iTj"/> + <binding destination="8ed-5g-y6e" name="enabled" keyPath="objectValue.isEnabled" id="hNs-V2-g3t"/> </connections> </popUpButton> </subviews> @@ -230,10 +206,21 @@ <menu key="menu" id="5Ha-Of-SJF"/> </popUpButtonCell> <connections> - <binding destination="JWs-Gl-4kd" name="selectedObject" keyPath="objectValue.mixdown" previousBinding="mrr-Rw-0Km" id="fAC-AG-TmT"/> - <binding destination="JWs-Gl-4kd" name="enabled" keyPath="objectValue.mixdownEnabled" id="anJ-dk-glQ"/> + <binding destination="JWs-Gl-4kd" name="enabled" keyPath="objectValue.isEnabled" id="tgd-cw-Xvj"/> + <binding destination="JWs-Gl-4kd" name="selectedValue" keyPath="objectValue.mixdown" previousBinding="N2w-i8-sMD" id="0hr-bn-M8A"> + <dictionary key="options"> + <string key="NSValueTransformerName">HBMixdownTransformer</string> + </dictionary> + </binding> + <binding destination="JWs-Gl-4kd" name="enabled2" keyPath="objectValue.mixdownEnabled" previousBinding="tgd-cw-Xvj" id="cYM-fO-KQf"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> <binding destination="JWs-Gl-4kd" name="content" keyPath="objectValue.mixdowns" id="N2w-i8-sMD"/> - <binding destination="JWs-Gl-4kd" name="contentValues" keyPath="objectValue.mixdowns.keyAudioMixdownName" previousBinding="N2w-i8-sMD" id="mrr-Rw-0Km"/> </connections> </popUpButton> </subviews> @@ -270,10 +257,22 @@ <menu key="menu" id="phf-E4-JNn"/> </popUpButtonCell> <connections> - <binding destination="iQl-Lr-pqe" name="enabled" keyPath="objectValue.mixdownEnabled" id="7np-q0-yTV"/> - <binding destination="iQl-Lr-pqe" name="selectedObject" keyPath="objectValue.sampleRate" previousBinding="lhu-ag-YDS" id="2BU-o2-PJ0"/> + <binding destination="iQl-Lr-pqe" name="enabled" keyPath="objectValue.isEnabled" id="XCQ-bF-dAC"/> + <binding destination="iQl-Lr-pqe" name="enabled2" keyPath="objectValue.mixdownEnabled" previousBinding="XCQ-bF-dAC" id="apI-pR-y7v"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> + <binding destination="iQl-Lr-pqe" name="selectedValue" keyPath="objectValue.sampleRate" previousBinding="Q5x-J9-u3J" id="hlm-8l-ATk"> + <dictionary key="options"> + <string key="NSNullPlaceholder">Auto</string> + <string key="NSValueTransformerName">HBSampleRateTransformer</string> + </dictionary> + </binding> <binding destination="iQl-Lr-pqe" name="content" keyPath="objectValue.sampleRates" id="Q5x-J9-u3J"/> - <binding destination="iQl-Lr-pqe" name="contentValues" keyPath="objectValue.sampleRates.keyAudioSampleRateName" previousBinding="Q5x-J9-u3J" id="lhu-ag-YDS"/> </connections> </popUpButton> </subviews> @@ -310,10 +309,21 @@ <menu key="menu" id="DFY-hE-Hi6"/> </popUpButtonCell> <connections> - <binding destination="Acc-Cw-PLD" name="selectedObject" keyPath="objectValue.bitRate" previousBinding="cgA-Y3-ucA" id="9OJ-Wp-j0I"/> + <binding destination="Acc-Cw-PLD" name="selectedValue" keyPath="objectValue.bitRate" previousBinding="S0T-S3-WF5" id="y8e-kU-Kay"> + <dictionary key="options"> + <string key="NSValueTransformerName">HBIntegerTransformer</string> + </dictionary> + </binding> <binding destination="Acc-Cw-PLD" name="content" keyPath="objectValue.bitRates" id="S0T-S3-WF5"/> - <binding destination="Acc-Cw-PLD" name="contentValues" keyPath="objectValue.bitRates.keyAudioBitrateName" previousBinding="S0T-S3-WF5" id="cgA-Y3-ucA"/> - <binding destination="Acc-Cw-PLD" name="enabled" keyPath="objectValue.bitrateEnabled" id="woZ-ZV-ZQJ"/> + <binding destination="Acc-Cw-PLD" name="enabled2" keyPath="objectValue.bitrateEnabled" previousBinding="vzn-iK-rgP" id="7aV-gO-WvY"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> + <binding destination="Acc-Cw-PLD" name="enabled" keyPath="objectValue.isEnabled" id="vzn-iK-rgP"/> </connections> </popUpButton> </subviews> @@ -351,24 +361,40 @@ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> </textFieldCell> <connections> - <binding destination="wfV-Pi-jHd" name="enabled" keyPath="objectValue.PassThruDisabled" id="NnK-Vf-hM6"/> <binding destination="wfV-Pi-jHd" name="value" keyPath="objectValue.gain" id="7hD-ql-cNT"> <dictionary key="options"> <bool key="NSValidatesImmediately" value="YES"/> </dictionary> </binding> + <binding destination="wfV-Pi-jHd" name="enabled" keyPath="objectValue.isEnabled" id="OrM-Yv-Fs0"/> + <binding destination="wfV-Pi-jHd" name="enabled2" keyPath="objectValue.passThruDisabled" previousBinding="OrM-Yv-Fs0" id="d6m-q5-unj"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> </connections> </textField> <slider horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gtC-MM-esd"> <rect key="frame" x="3" y="2" width="22" height="22"/> <sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" minValue="-5" maxValue="16" doubleValue="0.25" numberOfTickMarks="21" allowsTickMarkValuesOnly="YES" sliderType="circular" id="BBQ-FP-aQN"/> <connections> - <binding destination="wfV-Pi-jHd" name="enabled" keyPath="objectValue.PassThruDisabled" id="Wz3-ZW-SLi"/> <binding destination="wfV-Pi-jHd" name="value" keyPath="objectValue.gain" id="bKN-kD-NjJ"> <dictionary key="options"> <bool key="NSValidatesImmediately" value="YES"/> </dictionary> </binding> + <binding destination="wfV-Pi-jHd" name="enabled" keyPath="objectValue.isEnabled" id="7nL-Mg-i86"/> + <binding destination="wfV-Pi-jHd" name="enabled2" keyPath="objectValue.passThruDisabled" previousBinding="7nL-Mg-i86" id="y1g-gJ-0g6"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> </connections> </slider> </subviews> @@ -403,12 +429,20 @@ <rect key="frame" x="3" y="1" width="22" height="22"/> <sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" maxValue="4" numberOfTickMarks="16" allowsTickMarkValuesOnly="YES" sliderType="circular" id="nII-CW-aWc"/> <connections> - <binding destination="RMf-U7-5Td" name="enabled" keyPath="objectValue.DRCEnabled" id="tzk-tB-jEf"/> + <binding destination="RMf-U7-5Td" name="enabled2" keyPath="objectValue.drcEnabled" previousBinding="UGi-Kk-MwK" id="dVM-pa-gKf"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> <binding destination="RMf-U7-5Td" name="value" keyPath="objectValue.drc" id="GxO-He-2yi"> <dictionary key="options"> <bool key="NSValidatesImmediately" value="YES"/> </dictionary> </binding> + <binding destination="RMf-U7-5Td" name="enabled" keyPath="objectValue.isEnabled" id="UGi-Kk-MwK"/> </connections> </slider> <textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yPh-4R-HRQ"> @@ -420,12 +454,20 @@ <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/> </textFieldCell> <connections> - <binding destination="RMf-U7-5Td" name="enabled" keyPath="objectValue.DRCEnabled" id="FS6-ca-Kmi"/> + <binding destination="RMf-U7-5Td" name="enabled2" keyPath="objectValue.drcEnabled" previousBinding="Vv3-g9-exh" id="lY2-Z2-4bl"> + <dictionary key="options"> + <integer key="NSMultipleValuesPlaceholder" value="-1"/> + <integer key="NSNoSelectionPlaceholder" value="-1"/> + <integer key="NSNotApplicablePlaceholder" value="-1"/> + <integer key="NSNullPlaceholder" value="-1"/> + </dictionary> + </binding> <binding destination="RMf-U7-5Td" name="value" keyPath="objectValue.drc" id="f0P-bz-w9a"> <dictionary key="options"> <bool key="NSValidatesImmediately" value="YES"/> </dictionary> </binding> + <binding destination="RMf-U7-5Td" name="enabled" keyPath="objectValue.isEnabled" id="Vv3-g9-exh"/> </connections> </textField> </subviews> @@ -478,7 +520,7 @@ <constraint firstAttribute="bottom" secondItem="Yzu-Rk-hTv" secondAttribute="bottom" constant="20" symbolic="YES" id="uil-Nm-DIV"/> <constraint firstItem="jrP-M5-2Rq" firstAttribute="baseline" secondItem="vFP-nq-IQg" secondAttribute="baseline" id="y3i-kl-lge"/> </constraints> - <point key="canvasLocation" x="730" y="-14"/> + <point key="canvasLocation" x="557" y="-38"/> </view> <menu id="hyy-qd-qpe"> <items> diff --git a/macosx/HBAudio.h b/macosx/HBAudio.h index 045970e6a..d56f59d16 100644 --- a/macosx/HBAudio.h +++ b/macosx/HBAudio.h @@ -19,7 +19,9 @@ extern NSString *HBAudioChangedNotification; - (instancetype)initWithTitle:(HBTitle *)title; -@property (nonatomic, readonly) NSMutableArray *tracks; +@property (nonatomic, readonly) NSMutableArray<NSDictionary *> *sourceTracks; +@property (nonatomic, readonly) NSMutableArray<HBAudioTrack *> *tracks; + @property (nonatomic, readwrite) HBAudioDefaults *defaults; - (void)addAllTracks; @@ -27,8 +29,6 @@ extern NSString *HBAudioChangedNotification; - (void)reloadDefaults; - (BOOL)anyCodecMatches:(int)codec; -- (void)settingTrackToNone:(HBAudioTrack *)newNoneTrack; -- (void)switchingTrackFromNone:(nullable HBAudioTrack *)noLongerNoneTrack; @property (nonatomic, readwrite) int container; @property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; diff --git a/macosx/HBAudio.m b/macosx/HBAudio.m index 8f8872777..81936d790 100644 --- a/macosx/HBAudio.m +++ b/macosx/HBAudio.m @@ -15,13 +15,11 @@ #include "hb.h" +#define NONE_TRACK_INDEX 0 + NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; @interface HBAudio () <HBAudioTrackDataSource, HBAudioTrackDelegate> - -@property (nonatomic, readonly, strong) NSDictionary *noneTrack; -@property (nonatomic, readonly, strong) NSArray *masterTrackArray; // the master list of audio tracks from the title - @end @implementation HBAudio @@ -33,192 +31,172 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; { _container = HB_MUX_MP4; + _sourceTracks = [title.audioTracks mutableCopy]; _tracks = [[NSMutableArray alloc] init]; _defaults = [[HBAudioDefaults alloc] init]; - _noneTrack = @{keyAudioTrackIndex: @0, - keyAudioTrackName: NSLocalizedString(@"None", @"None"), - keyAudioInputCodec: @0}; - - 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 list + // Add the none and foreign track to the source array + NSDictionary *none = @{keyAudioTrackName: NSLocalizedString(@"None", nil)}; + [_sourceTracks insertObject:none atIndex:0]; } return self; } -- (void)addAllTracks +#pragma mark - Data Source + +- (NSDictionary<NSString *, id> *)sourceTrackAtIndex:(NSUInteger)idx; { - [self addTracksFromDefaults:YES]; + return self.sourceTracks[idx]; } -- (void)removeAll +- (NSArray<NSString *> *)sourceTracksArray { - [self _clearAudioArray]; - [self switchingTrackFromNone:nil]; + NSMutableArray *sourceNames = [NSMutableArray array]; + + for (NSDictionary *track in self.sourceTracks) + { + [sourceNames addObject:track[keyAudioTrackName]]; + } + + return sourceNames; } -- (void)reloadDefaults +#pragma mark - Delegate + +- (void)track:(HBAudioTrack *)track didChangeSourceFrom:(NSUInteger)oldSourceIdx; { - [self addTracksFromDefaults:NO]; + // If the source was changed to None, remove the track + if (track.sourceTrackIdx == NONE_TRACK_INDEX) + { + NSUInteger idx = [self.tracks indexOfObject:track]; + [self removeObjectFromTracksAtIndex:idx]; + } + // Else add a new None track + else if (oldSourceIdx == NONE_TRACK_INDEX) + { + [self addNoneTrack]; + } } -- (void)_clearAudioArray +- (void)addNoneTrack +{ + HBAudioTrack *track = [self trackFromSourceTrackIndex:NONE_TRACK_INDEX]; + [self addTrack:track]; +} + +#pragma mark - Public methods + +- (void)addAllTracks { - while (0 < [self countOfTracks]) + while (self.countOfTracks) { - [self removeObjectFromTracksAtIndex: 0]; + [self removeObjectFromTracksAtIndex:0]; + } + + // Add the remainings tracks + for (NSUInteger idx = 1; idx < self.sourceTracksArray.count; idx++) { + [self addTrack:[self trackFromSourceTrackIndex:idx]]; } + + [self addNoneTrack]; } -/** - * 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 +- (void)removeAll { - for (HBAudioTrackPreset *preset in templateAudioArray) + while (self.countOfTracks) { - BOOL fallenBack = NO; - HBAudioTrack *newAudio = [[HBAudioTrack alloc] init]; - [newAudio setDataSource:self]; - [newAudio setDelegate:self]; - [self insertObject: newAudio inTracksAtIndex: [self countOfTracks]]; - [newAudio setContainer:self.container]; - [newAudio setTrackFromIndex: (int)trackIndex]; - [newAudio setUndo:self.undo]; + [self removeObjectFromTracksAtIndex:0]; + } + [self addNoneTrack]; +} - const char *name = hb_audio_encoder_get_name(preset.encoder); - NSString *audioEncoder = nil; +- (void)reloadDefaults +{ + [self addTracksFromDefaults]; +} - // Check if we need to use a fallback - if (name) +- (void)setContainer:(int)container +{ + _container = container; + if (!(self.undo.isUndoing || self.undo.isRedoing)) + { + for (HBAudioTrack *track in self.tracks) { - 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); + track.container = container; } - // 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)]; - } + // 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]; - 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]; - } + //[self validatePassthru]; + } +} - if (firstOnly) - { - break; - } +- (void)setUndo:(NSUndoManager *)undo +{ + _undo = undo; + for (HBAudioTrack *track in self.tracks) + { + track.undo = undo; + } + self.defaults.undo = undo; +} + +- (void)setDefaults:(HBAudioDefaults *)defaults +{ + if (defaults != _defaults) + { + [[self.undo prepareWithInvocationTarget:self] setDefaults:_defaults]; } + _defaults = defaults; + _defaults.undo = self.undo; } /** - * Matches the source audio tracks with the specific language iso code. + * Convenience method to add a track to tracks array. * - * @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. + * @param track the track to add. */ -- (NSIndexSet *)_tracksWithISOCode:(NSString *)isoCode selectOnlyFirst:(BOOL)selectOnlyFirst +- (void)addTrack:(HBAudioTrack *)newTrack { - 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; + [self insertObject:newTrack inTracksAtIndex:[self countOfTracks]]; } -- (void)_processPresetAudioArray:(NSArray *)templateAudioArray forTracks:(NSIndexSet *)trackIndexes firstOnly:(BOOL)firstOnly +/** + * Creates a new track dictionary from a source track. + * + * @param index the index of the source track in the sourceTracks array + */ +- (HBAudioTrack *)trackFromSourceTrackIndex:(NSInteger)index { - __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; - }]; + HBAudioTrack *track = [[HBAudioTrack alloc] initWithTrackIdx:index container:self.container dataSource:self delegate:self]; + track.undo = self.undo; + return track; } -- (void)addTracksFromDefaults:(BOOL)allTracks +#pragma mark - Defaults + +- (void)addTracksFromDefaults { BOOL firstTrack = NO; + BOOL allTracks = NO; NSMutableIndexSet *tracksAdded = [NSMutableIndexSet indexSet]; NSMutableIndexSet *tracksToAdd = [NSMutableIndexSet indexSet]; // Reinitialize the configured list of audio tracks - [self _clearAudioArray]; + while (self.countOfTracks) + { + [self removeObjectFromTracksAtIndex:0]; + } 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]; + NSMutableIndexSet *tracksIndexes = [[self _tracksWithISOCode:languageISOCode + selectOnlyFirst:self.defaults.trackSelectionBehavior == HBAudioTrackSelectionBehaviorFirst] mutableCopy]; [tracksIndexes removeIndexes:tracksAdded]; if (tracksIndexes.count) { @@ -229,7 +207,7 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; } // If no preferred Language was found, add standard track 1 - if (tracksAdded.count == 0 && self.masterTrackArray.count > 1) + if (tracksAdded.count == 0 && self.sourceTracks.count > 1) { [tracksToAdd addIndex:1]; } @@ -238,7 +216,7 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; // 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 addIndexesInRange:NSMakeRange(1, self.sourceTracks.count - 1)]; [tracksToAdd removeIndexes:tracksAdded]; } @@ -248,119 +226,131 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; } // Add an None item - [self switchingTrackFromNone: nil]; + [self addNoneTrack]; } -- (BOOL)anyCodecMatches:(int)codec +/** + * 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 { - BOOL retval = NO; - NSUInteger audioArrayCount = [self countOfTracks]; - for (NSUInteger i = 0; i < audioArrayCount && !retval; i++) + for (HBAudioTrackPreset *preset in templateAudioArray) { - 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 setDataSource:self]; - [newAudio setDelegate:self]; - [self insertObject:newAudio inTracksAtIndex:[self countOfTracks]]; - [newAudio setContainer:self.container]; - [newAudio setTrack: self.noneTrack]; - [newAudio setUndo:self.undo]; -} + HBAudioTrack *newAudio = [[HBAudioTrack alloc] initWithTrackIdx:trackIndex + container:self.container + dataSource:self + delegate:self]; + [newAudio setUndo:self.undo]; -#pragma mark - -#pragma mark Notification Handling + [self insertObject:newAudio inTracksAtIndex:[self countOfTracks]]; -- (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]; + int inputCodec = [self.sourceTracks[trackIndex][keyAudioInputCodec] intValue]; + int outputCodec = preset.encoder; - if (NSNotFound != index && index < [self countOfTracks] - 1) - { - [self removeObjectFromTracksAtIndex: index]; - } - [self switchingTrackFromNone: nil]; // see if we need to add one to the list -} + // Check if we need to use a fallback + if (preset.encoder & HB_ACODEC_PASS_FLAG && !(preset.encoder & inputCodec & HB_ACODEC_PASS_MASK)) + { + outputCodec = hb_audio_encoder_get_fallback_for_passthru(outputCodec); + + // If we couldn't find an encoder for the passthru format + // fall back to the selected encoder fallback + if (outputCodec == HB_ACODEC_INVALID) + { + outputCodec = self.defaults.encoderFallback; + } + } + + int supportedMuxers = hb_audio_encoder_get_from_codec(outputCodec)->muxers; + + // 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 (supportedMuxers & self.container) + { + newAudio.drc = preset.drc; + newAudio.gain = preset.gain; + newAudio.mixdown = preset.mixdown; + newAudio.sampleRate = preset.sampleRate; + newAudio.bitRate = preset.bitRate; + newAudio.encoder = outputCodec; + } + else + { + [self removeObjectFromTracksAtIndex:[self countOfTracks] - 1]; + } -- (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]) + if (firstOnly) { - needToAdd = YES; + break; } } - else - { - needToAdd = YES; - } - - if (needToAdd) - { - [self addNewAudioTrack]; - } } -// This gets called whenever the video container changes. -- (void)setContainer:(int)container +/** + * 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 { - _container = container; + NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet]; - if (!(self.undo.isUndoing || self.undo.isRedoing)) - { - // Update each of the instances because this value influences possible settings. - for (HBAudioTrack *audioObject in self.tracks) + [self.sourceTracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (idx) // Note that we skip the "None" track { - audioObject.container = container; + if ([isoCode isEqualToString:@"und"] || [obj[keyAudioTrackLanguageIsoCode] isEqualToString:isoCode]) + { + [indexes addIndex:idx]; + + if (selectOnlyFirst) + { + *stop = YES; + } + } } + }]; - // 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]; - } + return indexes; } -- (void)setUndo:(NSUndoManager *)undo +- (void)_processPresetAudioArray:(NSArray<HBAudioTrackPreset *> *)templateAudioArray forTracks:(NSIndexSet *)trackIndexes firstOnly:(BOOL)firstOnly { - _undo = undo; - for (HBAudioTrack *track in self.tracks) - { - track.undo = undo; - } - self.defaults.undo = undo; + __block BOOL firstTrack = firstOnly; + [trackIndexes enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { + // Add the track + [self _processPresetAudioArray: self.defaults.tracksArray forTrack:idx firstOnly:firstTrack]; + firstTrack = self.defaults.secondaryEncoderMode ? YES : NO; + }]; } -- (void)setDefaults:(HBAudioDefaults *)defaults +- (BOOL)anyCodecMatches:(int)codec { - if (defaults != _defaults) + BOOL retval = NO; + NSUInteger audioArrayCount = [self countOfTracks]; + for (NSUInteger i = 0; i < audioArrayCount && !retval; i++) { - [[self.undo prepareWithInvocationTarget:self] setDefaults:_defaults]; + HBAudioTrack *anAudio = [self objectInTracksAtIndex: i]; + if (anAudio.isEnabled && codec == anAudio.encoder) + { + retval = YES; + } } - _defaults = defaults; - _defaults.undo = self.undo; + return retval; } -- (void)mixdownChanged +#pragma mark - +#pragma mark Notification Handling + +- (void)encoderChanged { - [[NSNotificationCenter defaultCenter] postNotificationName: HBAudioChangedNotification object: self]; + [[NSNotificationCenter defaultCenter] postNotificationName:HBAudioChangedNotification object:self]; } #pragma mark - NSCopying @@ -372,17 +362,13 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; if (copy) { copy->_container = _container; - - copy->_noneTrack = [_noneTrack copy]; - copy->_masterTrackArray = [_masterTrackArray copy]; + copy->_sourceTracks = [_sourceTracks mutableCopy]; copy->_tracks = [[NSMutableArray alloc] init]; [_tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { if (idx < _tracks.count) { - HBAudioTrack *trackCopy = [obj copy]; - trackCopy.dataSource = copy; - trackCopy.delegate = copy; + id trackCopy = [obj copy]; [copy->_tracks addObject:trackCopy]; } }]; @@ -402,14 +388,11 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; - (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeInt:1 forKey:@"HBAudioVersion"]; + [coder encodeInt:2 forKey:@"HBAudioVersion"]; encodeInt(_container); - - encodeObject(_noneTrack); - encodeObject(_masterTrackArray); + encodeObject(_sourceTracks); encodeObject(_tracks); - encodeObject(_defaults); } @@ -418,9 +401,7 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; self = [super init]; decodeInt(_container); - - decodeObject(_noneTrack, NSDictionary); - decodeObject(_masterTrackArray, NSArray); + decodeObject(_sourceTracks, NSMutableArray); decodeObject(_tracks, NSMutableArray); for (HBAudioTrack *track in _tracks) @@ -444,13 +425,13 @@ NSString *HBAudioChangedNotification = @"HBAudioChangedNotification"; - (void)applyPreset:(HBPreset *)preset { [self.defaults applyPreset:preset]; - [self addTracksFromDefaults:NO]; + [self addTracksFromDefaults]; } #pragma mark - #pragma mark KVC -- (NSUInteger) countOfTracks +- (NSUInteger)countOfTracks { return self.tracks.count; } diff --git a/macosx/HBAudioTrack.h b/macosx/HBAudioTrack.h index c259812e8..b0a5e0bea 100644 --- a/macosx/HBAudioTrack.h +++ b/macosx/HBAudioTrack.h @@ -22,51 +22,50 @@ extern NSString *keyAudioInputCodecParam; extern NSString *keyAudioInputChannelLayout; extern NSString *keyAudioTrackLanguageIsoCode; -extern NSString *keyAudioCodecName; -extern NSString *keyAudioSampleRateName; -extern NSString *keyAudioBitrateName; -extern NSString *keyAudioMixdownName; -extern NSString *keyAudioCodec; -extern NSString *keyAudioMixdown; -extern NSString *keyAudioSamplerate; -extern NSString *keyAudioBitrate; - @protocol HBAudioTrackDataSource <NSObject> -- (NSDictionary *)noneTrack; -- (NSArray *)masterTrackArray; +- (NSDictionary<NSString *, id> *)sourceTrackAtIndex:(NSUInteger)idx; +- (NSArray<NSString *> *)sourceTracksArray; @end @protocol HBAudioTrackDelegate <NSObject> -- (void)settingTrackToNone:(HBAudioTrack *)newNoneTrack; -- (void)switchingTrackFromNone:(HBAudioTrack *)noLongerNoneTrack; -- (void)mixdownChanged; +- (void)track:(HBAudioTrack *)track didChangeSourceFrom:(NSUInteger)oldSourceIdx; +- (void)encoderChanged; @end @interface HBAudioTrack : NSObject <NSSecureCoding, NSCopying> -@property (nonatomic, strong) NSDictionary *track; -@property (nonatomic, strong, nullable) NSDictionary *codec; -@property (nonatomic, strong, nullable) NSDictionary *mixdown; -@property (nonatomic, strong, nullable) NSDictionary *sampleRate; -@property (nonatomic, strong, nullable) NSDictionary *bitRate; -@property (nonatomic) double drc; -@property (nonatomic) double gain; -@property (nonatomic) int container; +- (instancetype)initWithTrackIdx:(NSUInteger)index + container:(int)container + dataSource:(id<HBAudioTrackDataSource>)dataSource + delegate:(id<HBAudioTrackDelegate>)delegate; + +/// The index of the source in the data source tracks array. +@property (nonatomic, readwrite) NSUInteger sourceTrackIdx; +@property (nonatomic, readwrite) int container; @property (nonatomic, weak, nullable) id<HBAudioTrackDataSource> dataSource; @property (nonatomic, weak, nullable) id<HBAudioTrackDelegate> delegate; -@property (nonatomic, readonly) NSArray *codecs; -@property (nonatomic, readonly) NSArray *mixdowns; -@property (nonatomic, readonly) NSArray *sampleRates; -@property (nonatomic, readonly) NSArray *bitRates; -@property (nonatomic, readonly) BOOL enabled; - -- (void) setTrackFromIndex: (int) aValue; -- (BOOL) setCodecFromName: (nullable NSString *) aValue; -- (void) setMixdownFromName: (NSString *) aValue; -- (void) setSampleRateFromName: (NSString *) aValue; -- (void) setBitRateFromName: (NSString *) aValue; +/** + * track properties. + */ +@property (nonatomic, readwrite) int encoder; +@property (nonatomic, readwrite) int mixdown; +@property (nonatomic, readwrite) int sampleRate; +@property (nonatomic, readwrite) int bitRate; + +@property (nonatomic, readwrite) double gain; +@property (nonatomic, readwrite) double drc; + +@property (nonatomic, readonly, getter=isEnabled) BOOL enabled; + +/** + * Arrays of possible options for the track properties. + */ +@property (nonatomic, readonly) NSArray<NSString *> *encoders; +@property (nonatomic, readonly) NSArray<NSString *> *mixdowns; +@property (nonatomic, readonly) NSArray<NSString *> *sampleRates; +@property (nonatomic, readonly) NSArray<NSString *> *bitRates; @property (nonatomic, readwrite, weak, nullable) NSUndoManager *undo; diff --git a/macosx/HBAudioTrack.m b/macosx/HBAudioTrack.m index 1c998cc4f..9e93eab5d 100644 --- a/macosx/HBAudioTrack.m +++ b/macosx/HBAudioTrack.m @@ -19,339 +19,95 @@ NSString *keyAudioInputCodecParam = @"keyAudioInputCodecParam"; NSString *keyAudioInputChannelLayout = @"keyAudioInputChannelLayout"; NSString *keyAudioTrackLanguageIsoCode = @"keyAudioTrackLanguageIsoCode"; -NSString *keyAudioCodecName = @"keyAudioCodecName"; -NSString *keyAudioSupportedMuxers = @"keyAudioSupportedMuxers"; -NSString *keyAudioSampleRateName = @"keyAudioSampleRateName"; -NSString *keyAudioBitrateName = @"keyAudioBitrateName"; -NSString *keyAudioMustMatchTrack = @"keyAudioMustMatchTrack"; -NSString *keyAudioMixdownName = @"keyAudioMixdownName"; - -NSString *keyAudioCodec = @"codec"; -NSString *keyAudioMixdown = @"mixdown"; -NSString *keyAudioSamplerate = @"samplerate"; -NSString *keyAudioBitrate = @"bitrate"; - -static NSMutableArray *masterCodecArray = nil; -static NSMutableArray *masterMixdownArray = nil; -static NSMutableArray *masterBitRateArray = nil; - -@interface NSArray (HBAudioSupport) -- (NSDictionary *) dictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey; -- (NSDictionary *) lastDictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey; -@end -@implementation NSArray (HBAudioSupport) -- (NSDictionary *) dictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey reverse: (BOOL) reverse -{ - NSDictionary *retval = nil; - NSEnumerator *enumerator = reverse ? [self reverseObjectEnumerator] : [self objectEnumerator]; - NSDictionary *dict; - id aValue; - - while (nil != (dict = [enumerator nextObject]) && !retval) - { - if (nil != (aValue = dict[aKey]) && [aValue isEqual: anObject]) - { - retval = dict; - } - } - return retval; -} -- (NSDictionary *) dictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey -{ - return [self dictionaryWithObject: anObject matchingKey: aKey reverse: NO]; -} -- (NSDictionary *) lastDictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey -{ - return [self dictionaryWithObject: anObject matchingKey: aKey reverse: YES]; -} - -@end +#define DEFAULT_SAMPLERATE 48000 @interface HBAudioTrack () - -@property (nonatomic, readwrite) NSArray *codecs; -@property (nonatomic, readwrite) NSArray *mixdowns; -@property (nonatomic, readwrite) NSArray *bitRates; - +@property (nonatomic, readwrite) BOOL validating; @end @implementation HBAudioTrack -#pragma mark - -#pragma mark Object Setup - -+ (void)initialize +- (instancetype)init { - if ([HBAudioTrack class] == self) + self = [super init]; + if (self) { - masterCodecArray = [[NSMutableArray alloc] init]; // knowingly leaked - 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)) - { - id audioMustMatchTrack; - if ((audio_encoder->codec & HB_ACODEC_PASS_FLAG) && - (audio_encoder->codec != HB_ACODEC_AUTO_PASS)) - { - audioMustMatchTrack = @(audio_encoder->codec & - ~HB_ACODEC_PASS_FLAG); - } - else - { - audioMustMatchTrack = @NO; - } - [masterCodecArray addObject:@{keyAudioCodecName: @(audio_encoder->name), - keyAudioCodec: @(audio_encoder->codec), - keyAudioSupportedMuxers: @(audio_encoder->muxers), - keyAudioMustMatchTrack: audioMustMatchTrack}]; - } - - masterMixdownArray = [[NSMutableArray alloc] init]; // knowingly leaked - for (const hb_mixdown_t *mixdown = hb_mixdown_get_next(NULL); - mixdown != NULL; - mixdown = hb_mixdown_get_next(mixdown)) - { - [masterMixdownArray addObject:@{keyAudioMixdownName: @(mixdown->name), - keyAudioMixdown: @(mixdown->amixdown)}]; - } - - masterBitRateArray = [[NSMutableArray alloc] init]; // knowingly leaked - for (const hb_rate_t *audio_bitrate = hb_audio_bitrate_get_next(NULL); - audio_bitrate != NULL; - audio_bitrate = hb_audio_bitrate_get_next(audio_bitrate)) - { - [masterBitRateArray addObject:@{keyAudioBitrateName: @(audio_bitrate->name), - keyAudioBitrate: @(audio_bitrate->rate)}]; - } + // Defaults settings + _encoder = HB_ACODEC_CA_AAC; + _container = HB_MUX_MKV; + _sampleRate = 0; + _bitRate = 160; + _mixdown = HB_AMIXDOWN_DOLBYPLII; } + return self; } -// Ensure the list of codecs is accurate -// Update the current value of codec based on the revised list -- (void) updateCodecs +- (instancetype)initWithTrackIdx:(NSUInteger)index + container:(int)container + dataSource:(id<HBAudioTrackDataSource>)dataSource + delegate:(id<HBAudioTrackDelegate>)delegate; { - NSMutableArray *permittedCodecs = [NSMutableArray array]; - NSUInteger count = [masterCodecArray count]; - NSDictionary *dict; - - // First get a list of the permitted codecs based on the internal rules - if (nil != self.track && self.enabled) + self = [super init]; + if (self) { - BOOL goodToAdd; - - for (unsigned int i = 0; i < count; i++) - { - dict = masterCodecArray[i]; - - // First make sure only codecs permitted by the container are here - goodToAdd = !!([dict[keyAudioSupportedMuxers] intValue] & self.container); + _dataSource = dataSource; + _sourceTrackIdx = index; + _container = container; - // Now we make sure if DTS or AC3 is not available in the track it is not put in the codec list, but in a general way - if ([dict[keyAudioMustMatchTrack] boolValue]) - { - if ([dict[keyAudioMustMatchTrack] intValue] != [self.track[keyAudioInputCodec] intValue]) - { - goodToAdd = NO; - } - } + [self validateSettings]; - if (goodToAdd) - { - [permittedCodecs addObject: dict]; - } - } + _delegate = delegate; } - // Now make sure the permitted list and the actual ones matches - [self setCodecs: permittedCodecs]; + return self; +} - // Ensure our codec is on the list of permitted codecs - if (!self.codec || ![permittedCodecs containsObject: self.codec]) +- (void)validateSettings +{ + if (_sourceTrackIdx) { - if (0 < [permittedCodecs count]) + if (self.encoder == 0) { - self.codec = permittedCodecs[0]; // This should be defaulting to Core Audio + self.encoder = HB_ACODEC_CA_AAC; + self.bitRate = 160; } else { - self.codec = nil; + self.encoder = [self sanatizeEncoderValue:self.encoder]; } } -} - -- (void)updateMixdowns:(BOOL)shouldSetDefault -{ - NSMutableArray *permittedMixdowns = [NSMutableArray array]; - NSDictionary *dict; - int currentMixdown; - - unsigned long long channelLayout = [self.track[keyAudioInputChannelLayout] unsignedLongLongValue]; - NSUInteger count = [masterMixdownArray count]; - int codecCodec = [self.codec[keyAudioCodec] intValue]; - int theDefaultMixdown = hb_mixdown_get_default(codecCodec, channelLayout); - - for (unsigned int i = 0; i < count; i++) - { - dict = masterMixdownArray[i]; - currentMixdown = [dict[keyAudioMixdown] intValue]; - - if (hb_mixdown_is_supported(currentMixdown, codecCodec, channelLayout)) - { - [permittedMixdowns addObject: dict]; - } - } - - if (!self.enabled) - { - permittedMixdowns = nil; - } - - // Now make sure the permitted list and the actual ones matches - self.mixdowns = permittedMixdowns; - - // Select the proper one - if (shouldSetDefault) - { - self.mixdown = [permittedMixdowns dictionaryWithObject: @(theDefaultMixdown) - matchingKey: keyAudioMixdown]; - } - - if (!self.mixdown || ![permittedMixdowns containsObject: self.mixdown]) + else { - self.mixdown = [permittedMixdowns lastObject]; + self.encoder = 0; + self.mixdown = 0; + self.sampleRate = 0; + self.bitRate = -1; } } -- (void)validateSamplerate -{ - int codec = [self.codec[keyAudioCodec] intValue]; - int samplerate = [self.sampleRate[keyAudioSamplerate] intValue]; +#pragma mark - Track properties - if (codec & HB_ACODEC_PASS_FLAG) - { - [self setSampleRateFromName:@"Auto"]; - } - else if (samplerate) - { - samplerate = hb_audio_samplerate_find_closest(samplerate, codec); - [self setSampleRateFromName:@(hb_audio_samplerate_get_name(samplerate))]; - } -} - -- (void)updateBitRates:(BOOL)shouldSetDefault +- (void)setSourceTrackIdx:(NSUInteger)sourceTrackIdx { - NSMutableArray *permittedBitRates = [NSMutableArray array]; - NSDictionary *dict; - int minBitRate; - int maxBitRate; - int currentBitRate; - BOOL shouldAdd; - - NSUInteger count = [masterBitRateArray count]; - int trackInputBitRate = [self.track[keyAudioInputBitrate] intValue]; - int theSampleRate = [self.sampleRate[keyAudioSamplerate] intValue]; - - if (0 == theSampleRate) // this means Auto + if (sourceTrackIdx != _sourceTrackIdx) { - theSampleRate = [self.track[keyAudioInputSampleRate] intValue]; + [[self.undo prepareWithInvocationTarget:self] setSourceTrackIdx:_sourceTrackIdx]; } - int ourCodec = [self.codec[keyAudioCodec] intValue]; - int ourMixdown = [self.mixdown[keyAudioMixdown] intValue]; - int theDefaultBitRate = hb_audio_bitrate_get_default(ourCodec, theSampleRate, ourMixdown); - hb_audio_bitrate_get_limits(ourCodec, theSampleRate, ourMixdown, &minBitRate, &maxBitRate); - - BOOL codecIsPassthru = ([self.codec[keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG) ? YES : NO; - BOOL codecIsLossless = (theDefaultBitRate == -1) ? YES : NO; - - if (codecIsPassthru) - { - NSDictionary *sourceBitRate = [masterBitRateArray dictionaryWithObject: @(trackInputBitRate) - matchingKey: keyAudioBitrate]; - if (!sourceBitRate) - { - // the source bitrate isn't in the master array - create it - sourceBitRate = @{keyAudioBitrateName: [NSString stringWithFormat: @"%d", trackInputBitRate], - keyAudioBitrate: @(trackInputBitRate)}; - } - [permittedBitRates addObject: sourceBitRate]; - } - else if (codecIsLossless) - { - NSDictionary *bitRateNotApplicable = @{keyAudioBitrateName: @"N/A", - keyAudioBitrate: @-1}; - [permittedBitRates addObject: bitRateNotApplicable]; - } - else - { - for (unsigned int i = 0; i < count; i++) - { - dict = masterBitRateArray[i]; - currentBitRate = [dict[keyAudioBitrate] intValue]; - - // First ensure the bitrate falls within range of the codec - shouldAdd = (currentBitRate >= minBitRate && currentBitRate <= maxBitRate); - - if (shouldAdd) - { - [permittedBitRates addObject: dict]; - } - } - } - - if (!self.enabled) - { - permittedBitRates = nil; - } - - // Make sure we are updated with the permitted list - self.bitRates = permittedBitRates; - - // Select the proper one - if (shouldSetDefault) - { - [self setBitRateFromName: [NSString stringWithFormat:@"%d", theDefaultBitRate]]; - } + NSUInteger oldIdx = _sourceTrackIdx; + _sourceTrackIdx = sourceTrackIdx; - if (!self.bitRate || ![permittedBitRates containsObject: self.bitRate]) + if (!(self.undo.isUndoing || self.undo.isRedoing)) { - self.bitRate = [permittedBitRates lastObject]; - } -} - -#pragma mark - -#pragma mark Accessors + [self validateSettings]; -- (NSArray *)sampleRates -{ - NSMutableArray *samplerates = [[NSMutableArray alloc] init]; - - /* - * Note that for the Auto value we use 0 for the sample rate because our controller will give back the track's - * input sample rate when it finds this 0 value as the selected sample rate. We do this because the input - * sample rate depends on the track, which means it depends on the title, so cannot be nicely set up here. - */ - [samplerates addObject:@{keyAudioSampleRateName: @"Auto", - keyAudioSamplerate: @0}]; - - int codec = [self.codec[keyAudioCodec] intValue]; - for (const hb_rate_t *audio_samplerate = hb_audio_samplerate_get_next(NULL); - audio_samplerate != NULL; - audio_samplerate = hb_audio_samplerate_get_next(audio_samplerate)) - { - int rate = audio_samplerate->rate; - if (rate == hb_audio_samplerate_find_closest(rate, codec)) + if (oldIdx != sourceTrackIdx) { - [samplerates addObject:@{keyAudioSampleRateName: @(audio_samplerate->name), - keyAudioSamplerate: @(rate)}]; + [self.delegate track:self didChangeSourceFrom:oldIdx]; } } - return samplerates; } -#pragma mark - -#pragma mark Setters - - (void)setContainer:(int)container { if (container != _container) @@ -362,54 +118,33 @@ static NSMutableArray *masterBitRateArray = nil; if (!(self.undo.isUndoing || self.undo.isRedoing)) { - [self updateCodecs]; - } -} - -- (void)setTrack:(NSDictionary *)track -{ - if (track != _track) - { - [[self.undo prepareWithInvocationTarget:self] setTrack:_track]; - } - NSDictionary *oldValue = _track; - _track = track; - if (nil != _track && !(self.undo.isUndoing || self.undo.isRedoing)) - { - [self updateCodecs]; - [self updateMixdowns: YES]; - if (self.enabled) - { - self.sampleRate = self.sampleRates[0]; // default to Auto - } - if ([self.dataSource.noneTrack isEqual: oldValue]) - { - [self.delegate switchingTrackFromNone: self]; - } - if ([self.dataSource.noneTrack isEqual: self.track]) + if (self.encoder) { - [self.delegate settingTrackToNone: self]; + self.encoder = [self sanatizeEncoderValue:self.encoder]; } } } -- (void)setCodec:(NSDictionary *)codec +- (void)setEncoder:(int)encoder { - if (codec != _codec) + if (encoder != _encoder) { - [[self.undo prepareWithInvocationTarget:self] setCodec:_codec]; + [[self.undo prepareWithInvocationTarget:self] setEncoder:_encoder]; } - _codec = codec; + _encoder = encoder; - if (!(self.undo.isUndoing || self.undo.isRedoing)) + if (!(self.undo.isUndoing || self.undo.isRedoing) && !self.validating) { - [self validateSamplerate]; - [self updateMixdowns:YES]; - [self updateBitRates:YES]; + self.validating = YES; + [self.delegate encoderChanged]; + self.mixdown = [self sanatizeMixdownValue:self.mixdown]; + self.sampleRate = [self sanatizeSamplerateValue:self.sampleRate]; + self.bitRate = [self sanatizeBitrateValue:self.bitRate]; + self.validating = NO; } } -- (void)setMixdown:(NSDictionary *)mixdown +- (void)setMixdown:(int)mixdown { if (mixdown != _mixdown) { @@ -417,14 +152,15 @@ static NSMutableArray *masterBitRateArray = nil; } _mixdown = mixdown; - if (!(self.undo.isUndoing || self.undo.isRedoing)) + if (!(self.undo.isUndoing || self.undo.isRedoing) && !self.validating) { - [self updateBitRates:YES]; - [self.delegate mixdownChanged]; + self.validating = YES; + self.bitRate = [self sanatizeBitrateValue:self.bitRate]; + self.validating = NO; } } -- (void)setSampleRate:(NSDictionary *)sampleRate +- (void)setSampleRate:(int)sampleRate { if (sampleRate != _sampleRate) { @@ -432,13 +168,15 @@ static NSMutableArray *masterBitRateArray = nil; } _sampleRate = sampleRate; - if (!(self.undo.isUndoing || self.undo.isRedoing)) + if (!(self.undo.isUndoing || self.undo.isRedoing) && !self.validating) { - [self updateBitRates: NO]; + self.validating = YES; + self.bitRate = [self sanatizeBitrateValue:self.bitRate]; + self.validating = NO; } } -- (void)setBitRate:(NSDictionary *)bitRate +- (void)setBitRate:(int)bitRate { if (bitRate != _bitRate) { @@ -447,15 +185,6 @@ static NSMutableArray *masterBitRateArray = nil; _bitRate = bitRate; } -- (void)setDrc:(double)drc -{ - if (drc != _drc) - { - [[self.undo prepareWithInvocationTarget:self] setDrc:_drc]; - } - _drc = drc; -} - - (void)setGain:(double)gain { if (gain != _gain) @@ -465,140 +194,235 @@ static NSMutableArray *masterBitRateArray = nil; _gain = gain; } -#pragma mark - -#pragma mark Special Setters - -- (void)setTrackFromIndex:(int)aValue +// Because we have indicated that the binding for the gain validates immediately we can implement the +// key value binding method to ensure the gain stays in our accepted range. +- (BOOL)validateGain:(id *)ioValue error:(NSError * __autoreleasing *)outError { - self.track = [self.dataSource.masterTrackArray dictionaryWithObject: @(aValue) - matchingKey: keyAudioTrackIndex]; + BOOL retval = YES; + + if (nil != *ioValue) + { + if ([*ioValue intValue] < -20) + { + *ioValue = @(-20); + } + else if ([*ioValue intValue] > 20) + { + *ioValue = @20; + } + } + + return retval; } -// This returns whether it is able to set the actual codec desired. -- (BOOL)setCodecFromName:(NSString *)aValue +- (void)setDrc:(double)drc { - NSDictionary *dict = [self.codecs dictionaryWithObject: aValue matchingKey: keyAudioCodecName]; - if (nil != dict) + if (drc != _drc) { - self.codec = dict; + [[self.undo prepareWithInvocationTarget:self] setDrc:_drc]; } - return (nil != dict); + _drc = drc; } -- (void)setMixdownFromName:(NSString *)aValue +#pragma mark - Validation + +- (int)sanatizeEncoderValue:(int)proposedEncoder { - NSDictionary *dict = [self.mixdowns dictionaryWithObject: aValue matchingKey: keyAudioMixdownName]; - if (nil != dict) + if (proposedEncoder) + { + NSDictionary *sourceTrack = [_dataSource sourceTrackAtIndex:_sourceTrackIdx]; + int inputCodec = [sourceTrack[keyAudioInputCodec] intValue]; + + hb_encoder_t *proposedEncoderInfo = hb_audio_encoder_get_from_codec(proposedEncoder); + + if (proposedEncoderInfo && proposedEncoderInfo->muxers & self.container) + { + // If the codec is passthru, see if the new source supports it. + if (proposedEncoderInfo->codec & HB_ACODEC_PASS_FLAG) + { + if ((proposedEncoderInfo->codec & inputCodec & HB_ACODEC_PASS_MASK)) + { + return proposedEncoder; + } + } + else + { + return proposedEncoder; + } + } + + return HB_ACODEC_CA_AAC; + } + else { - self.mixdown = dict; + return proposedEncoder; } } -- (void)setSampleRateFromName:(NSString *)aValue +- (int)sanatizeMixdownValue:(int)proposedMixdown { - NSDictionary *dict = [self.sampleRates dictionaryWithObject: aValue matchingKey: keyAudioSampleRateName]; - if (nil != dict) + NSDictionary *sourceTrack = [_dataSource sourceTrackAtIndex:_sourceTrackIdx]; + unsigned long long channelLayout = [sourceTrack[keyAudioInputChannelLayout] unsignedLongLongValue]; + + if (!hb_mixdown_is_supported(proposedMixdown, self.encoder, channelLayout)) { - self.sampleRate = dict; + return hb_mixdown_get_default(self.encoder, channelLayout); } + return proposedMixdown; } -- (void)setBitRateFromName:(NSString *)aValue +- (int)sanatizeSamplerateValue:(int)proposedSamplerate { - NSDictionary *dict = [self.bitRates dictionaryWithObject: aValue matchingKey: keyAudioBitrateName]; - if (nil != dict) + if (self.encoder & HB_ACODEC_PASS_FLAG) + { + return 0; // Auto (same as source) + } + else if (proposedSamplerate) { - self.bitRate = dict; + return hb_audio_samplerate_find_closest(proposedSamplerate, self.encoder); } + return proposedSamplerate; } -- (void)setCodecs:(NSArray *)codecs +- (int)sanatizeBitrateValue:(int)proposedBitrate { - if (codecs != _codecs) + if (self.encoder & HB_ACODEC_PASS_FLAG) { - [[self.undo prepareWithInvocationTarget:self] setCodecs:_codecs]; + return -1; + } + else if (proposedBitrate == -1) // switching from passthru + { + return hb_audio_bitrate_get_default(self.encoder, + self.sampleRate ? self.sampleRate : DEFAULT_SAMPLERATE, + self.mixdown); + } + else + { + return hb_audio_bitrate_get_best(self.encoder, proposedBitrate, self.sampleRate, self.mixdown); } - _codecs = codecs; } -- (void)setMixdowns:(NSArray *)mixdowns +#pragma mark - Options + +- (NSArray<NSString *> *)encoders { - if (mixdowns != _mixdowns) + NSMutableArray<NSString *> *encoders = [[NSMutableArray alloc] init]; + + NSDictionary *sourceTrack = [_dataSource sourceTrackAtIndex:_sourceTrackIdx]; + int inputCodec = [sourceTrack[keyAudioInputCodec] intValue]; + + 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)) { - [[self.undo prepareWithInvocationTarget:self] setMixdowns:_mixdowns]; + if (audio_encoder->muxers & self.container) + { + if (audio_encoder->codec & HB_ACODEC_PASS_FLAG) + { + // If the codec is passthru, show only the supported ones. + if ((audio_encoder->codec & inputCodec & HB_ACODEC_PASS_MASK)) + { + [encoders addObject:@(audio_encoder->name)]; + } + } + else + { + [encoders addObject:@(audio_encoder->name)]; + } + } } - _mixdowns = mixdowns; + return encoders; } -- (void)setBitRates:(NSArray *)bitRates +- (NSArray<NSString *> *)mixdowns { - if (bitRates != _bitRates) + NSMutableArray<NSString *> *mixdowns = [[NSMutableArray alloc] init]; + + NSDictionary *sourceTrack = [_dataSource sourceTrackAtIndex:_sourceTrackIdx]; + unsigned long long channelLayout = [sourceTrack[keyAudioInputChannelLayout] unsignedLongLongValue]; + + for (const hb_mixdown_t *mixdown = hb_mixdown_get_next(NULL); + mixdown != NULL; + mixdown = hb_mixdown_get_next(mixdown)) { - [[self.undo prepareWithInvocationTarget:self] setBitRates:_bitRates]; + if (hb_mixdown_is_supported(mixdown->amixdown, self.encoder, channelLayout)) + { + [mixdowns addObject:@(mixdown->name)]; + } } - _bitRates = bitRates; + return mixdowns; } -#pragma mark - -#pragma mark Validation - -// Because we have indicated that the binding for the gain validates immediately we can implement the -// key value binding method to ensure the gain stays in our accepted range. -- (BOOL)validateGain:(id *)ioValue error:(NSError * __autoreleasing *)outError +- (NSArray<NSString *> *)sampleRates { - BOOL retval = YES; + NSMutableArray<NSString *> *sampleRates = [[NSMutableArray alloc] init]; + [sampleRates addObject:@"Auto"]; - if (nil != *ioValue) + for (const hb_rate_t *audio_samplerate = hb_audio_samplerate_get_next(NULL); + audio_samplerate != NULL; + audio_samplerate = hb_audio_samplerate_get_next(audio_samplerate)) { - if ([*ioValue intValue] < -20) + int rate = audio_samplerate->rate; + if (rate == hb_audio_samplerate_find_closest(rate, self.encoder)) { - *ioValue = @(-20); + [sampleRates addObject:@(audio_samplerate->name)]; } - else if ([*ioValue intValue] > 20) + } + return sampleRates; +} + +- (NSArray<NSString *> *)bitRates +{ + int minBitRate = 0; + int maxBitRate = 0; + + hb_audio_bitrate_get_limits(self.encoder, self.sampleRate, self.mixdown, &minBitRate, &maxBitRate); + + NSMutableArray<NSString *> *bitRates = [[NSMutableArray alloc] init]; + for (const hb_rate_t *audio_bitrate = hb_audio_bitrate_get_next(NULL); + audio_bitrate != NULL; + audio_bitrate = hb_audio_bitrate_get_next(audio_bitrate)) + { + if (audio_bitrate->rate >= minBitRate && audio_bitrate->rate <= maxBitRate) { - *ioValue = @20; + [bitRates addObject:@(audio_bitrate->name)]; } } - - return retval; + return bitRates; } -#pragma mark - Bindings Support +#pragma mark - KVO UI Additions -- (NSArray *)masterTrackArray +- (NSArray *)sourceTracksArray { - return self.dataSource.masterTrackArray; + return [self.dataSource sourceTracksArray]; } -- (BOOL)enabled +- (BOOL)isEnabled { - return (nil != self.track) ? (![self.track isEqual: self.dataSource.noneTrack]) : NO; + return self.sourceTrackIdx != 0; } - (BOOL)mixdownEnabled { - BOOL retval = self.enabled; + BOOL retval = self.isEnabled; - if (retval) + if (retval && self.mixdown == HB_AMIXDOWN_NONE) { - int myMixdown = [self.mixdown[keyAudioMixdown] intValue]; - if (myMixdown == HB_AMIXDOWN_NONE) - { - // "None" mixdown (passthru) - retval = NO; - } + // "None" mixdown (passthru) + retval = NO; } + return retval; } - (BOOL)bitrateEnabled { - BOOL retval = self.enabled; + BOOL retval = self.isEnabled; if (retval) { - int myCodecCodec = [self.codec[keyAudioCodec] intValue]; - int myCodecDefaultBitrate = hb_audio_bitrate_get_default(myCodecCodec, 0, 0); + int myCodecDefaultBitrate = hb_audio_bitrate_get_default(self.encoder, 0, 0); if (myCodecDefaultBitrate < 0) { retval = NO; @@ -607,16 +431,17 @@ static NSMutableArray *masterBitRateArray = nil; return retval; } -- (BOOL)DRCEnabled +- (BOOL)drcEnabled { - BOOL retval = self.enabled; + BOOL retval = self.isEnabled; if (retval) { - int myTrackParam = [self.track[keyAudioInputCodecParam] intValue]; - int myTrackCodec = [self.track[keyAudioInputCodec] intValue]; - int myCodecCodec = [self.codec[keyAudioCodec] intValue]; - if (!hb_audio_can_apply_drc(myTrackCodec, myTrackParam, myCodecCodec)) + NSDictionary *sourceTrack = [_dataSource sourceTrackAtIndex:_sourceTrackIdx]; + + int inputCodec = [sourceTrack[keyAudioInputCodec] intValue]; + int inputCodecParam = [sourceTrack[keyAudioInputCodecParam] intValue]; + if (!hb_audio_can_apply_drc(inputCodec, inputCodecParam, self.encoder)) { retval = NO; } @@ -624,52 +449,46 @@ static NSMutableArray *masterBitRateArray = nil; return retval; } -- (BOOL)PassThruDisabled +- (BOOL)passThruDisabled { - BOOL retval = self.enabled; + BOOL retval = YES; - if (retval) + if (self.encoder & HB_ACODEC_PASS_FLAG) { - int myCodecCodec = [self.codec[keyAudioCodec] intValue]; - if (myCodecCodec & HB_ACODEC_PASS_FLAG) - { - retval = NO; - } + retval = NO; } + return retval; } -- (void)setNilValueForKey:(NSString *)key -{ - if ([key isEqualToString:@"drc"] || [key isEqualToString:@"gain"]) - { - [self setValue:@0 forKey:key]; - } -} +#pragma mark - KVO + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { NSSet *retval = nil; - if ([key isEqualToString:@"enabled"]) + if ([key isEqualToString:@"bitrateEnabled"] || + [key isEqualToString:@"passThruDisabled"] || + [key isEqualToString:@"mixdownEnabled"]) { - retval = [NSSet setWithObjects:@"track", nil]; + retval = [NSSet setWithObjects:@"encoder", nil]; } - else if ([key isEqualToString:@"PassThruDisabled"]) + else if ([key isEqualToString:@"mixdowns"] || + [key isEqualToString:@"drcEnabled"]) { - retval = [NSSet setWithObjects:@"track", @"codec", nil]; + retval = [NSSet setWithObjects:@"sourceTrackIdx", @"encoder", nil]; } - else if ([key isEqualToString:@"DRCEnabled"]) + else if ([key isEqualToString:@"sampleRates"]) { - retval = [NSSet setWithObjects:@"track", @"codec", nil]; + retval = [NSSet setWithObjects:@"encoder", @"mixdown", nil]; } - else if ([key isEqualToString:@"bitrateEnabled"]) + else if ([key isEqualToString:@"bitRates"]) { - retval = [NSSet setWithObjects:@"track", @"codec", nil]; + retval = [NSSet setWithObjects:@"encoder", @"mixdown", @"sampleRate", nil]; } - else if ([key isEqualToString:@"mixdownEnabled"]) + else if ([key isEqualToString:@"encoders"]) { - retval = [NSSet setWithObjects:@"track", @"mixdown", nil]; + retval = [NSSet setWithObjects:@"container", @"sourceTrackIdx", nil]; } else { @@ -679,6 +498,14 @@ static NSMutableArray *masterBitRateArray = nil; return retval; } +- (void)setNilValueForKey:(NSString *)key +{ + if ([key isEqualToString:@"drc"] || [key isEqualToString:@"gain"]) + { + [self setValue:@0 forKey:key]; + } +} + #pragma mark - NSCopying - (instancetype)copyWithZone:(NSZone *)zone @@ -687,18 +514,16 @@ static NSMutableArray *masterBitRateArray = nil; if (copy) { - copy->_track = [_track copy]; - copy->_codec = [_codec copy]; - copy->_mixdown = [_mixdown copy]; - copy->_sampleRate = [_sampleRate copy]; - copy->_bitRate = [_bitRate copy]; - copy->_drc = _drc; - copy->_gain = _gain; + copy->_sourceTrackIdx = _sourceTrackIdx; copy->_container = _container; - copy->_codecs = [_codecs copy]; - copy->_mixdowns = [_mixdowns copy]; - copy->_bitRates = [_bitRates copy]; + copy->_encoder = _encoder; + copy->_mixdown = _mixdown; + copy->_sampleRate = _sampleRate; + copy->_bitRate = _bitRate; + + copy->_gain = _gain; + copy->_drc = _drc; } return copy; @@ -713,38 +538,34 @@ static NSMutableArray *masterBitRateArray = nil; - (void)encodeWithCoder:(NSCoder *)coder { - [coder encodeInt:2 forKey:@"HBAudioTrackVersion"]; + [coder encodeInt:3 forKey:@"HBAudioTrackVersion"]; - encodeObject(_track); - encodeObject(_codec); - encodeObject(_mixdown); - encodeObject(_sampleRate); - encodeObject(_bitRate); - encodeDouble(_drc); - encodeDouble(_gain); + encodeInteger(_sourceTrackIdx); encodeInt(_container); - encodeObject(_codecs); - encodeObject(_mixdowns); - encodeObject(_bitRates); + encodeInt(_encoder); + encodeInt(_mixdown); + encodeInt(_sampleRate); + encodeInt(_bitRate); + + encodeDouble(_gain); + encodeDouble(_drc); } - (instancetype)initWithCoder:(NSCoder *)decoder { self = [super init]; - decodeObject(_track, NSDictionary); - decodeObject(_codec, NSDictionary); - decodeObject(_mixdown, NSDictionary); - decodeObject(_sampleRate, NSDictionary); - decodeObject(_bitRate, NSDictionary); - decodeDouble(_drc); - decodeDouble(_gain); + decodeInteger(_sourceTrackIdx); decodeInt(_container); - decodeObject(_codecs, NSMutableArray); - decodeObject(_mixdowns, NSMutableArray); - decodeObject(_bitRates, NSArray); + decodeInt(_encoder); + decodeInt(_mixdown); + decodeInt(_sampleRate); + decodeInt(_bitRate); + + decodeDouble(_gain); + decodeDouble(_drc); return self; } diff --git a/macosx/HBAudioTrackPreset.h b/macosx/HBAudioTrackPreset.h index 1acd14a20..7f3b4a8f4 100644 --- a/macosx/HBAudioTrackPreset.h +++ b/macosx/HBAudioTrackPreset.h @@ -42,19 +42,3 @@ NS_ASSUME_NONNULL_BEGIN @end NS_ASSUME_NONNULL_END - -/** - * A series of value trasformers to bridge the libhb enums - * to the textual rapresentations used in the interface. - */ -@interface HBEncoderTransformer : NSValueTransformer -@end - -@interface HBMixdownTransformer : NSValueTransformer -@end - -@interface HBSampleRateTransformer : NSValueTransformer -@end - -@interface HBIntegerTransformer : NSValueTransformer -@end
\ No newline at end of file diff --git a/macosx/HBAudioTrackPreset.m b/macosx/HBAudioTrackPreset.m index 254a83aae..0e2e66a4c 100644 --- a/macosx/HBAudioTrackPreset.m +++ b/macosx/HBAudioTrackPreset.m @@ -10,8 +10,6 @@ #define DEFAULT_SAMPLERATE 48000 -static void *HBAudioEncoderContex = &HBAudioEncoderContex; - @interface HBAudioTrackPreset () @property (nonatomic, readwrite) int container; @@ -257,6 +255,8 @@ static void *HBAudioEncoderContex = &HBAudioEncoderContex; - (NSArray<NSString *> *)sampleRates { NSMutableArray<NSString *> *sampleRates = [[NSMutableArray alloc] init]; + [sampleRates addObject:@"Auto"]; + for (const hb_rate_t *audio_samplerate = hb_audio_samplerate_get_next(NULL); audio_samplerate != NULL; audio_samplerate = hb_audio_samplerate_get_next(audio_samplerate)) @@ -294,7 +294,7 @@ static void *HBAudioEncoderContex = &HBAudioEncoderContex; { NSSet *retval = nil; - // Tell KVO to reaload the *enabled keyPaths + // Tell KVO to reload the *enabled keyPaths // after a change to encoder. if ([key isEqualToString:@"bitrateEnabled"] || [key isEqualToString:@"passThruDisabled"] || @@ -306,6 +306,10 @@ static void *HBAudioEncoderContex = &HBAudioEncoderContex; { retval = [NSSet setWithObjects:@"encoder", nil]; } + else if ([key isEqualToString:@"sampleRates"]) + { + retval = [NSSet setWithObjects:@"encoder", @"mixdown", nil]; + } else if ([key isEqualToString:@"bitRates"]) { retval = [NSSet setWithObjects:@"encoder", @"mixdown", @"sampleRate", nil]; @@ -388,136 +392,3 @@ static void *HBAudioEncoderContex = &HBAudioEncoderContex; } @end - -#pragma mark - Value Trasformers - -@implementation HBEncoderTransformer - -+ (Class)transformedValueClass -{ - return [NSString class]; -} - -- (id)transformedValue:(id)value -{ - const char *name = hb_audio_encoder_get_name([value intValue]); - if (name) - { - return @(name); - } - else - { - return nil; - } -} - -+ (BOOL)allowsReverseTransformation -{ - return YES; -} - -- (id)reverseTransformedValue:(id)value -{ - return @(hb_audio_encoder_get_from_name([value UTF8String])); -} - -@end - -@implementation HBMixdownTransformer - -+ (Class)transformedValueClass -{ - return [NSString class]; -} - -- (id)transformedValue:(id)value -{ - const char *name = hb_mixdown_get_name([value intValue]); - if (name) - { - return @(name); - } - else - { - return nil; - } -} - -+ (BOOL)allowsReverseTransformation -{ - return YES; -} - -- (id)reverseTransformedValue:(id)value -{ - return @(hb_mixdown_get_from_name([value UTF8String])); -} - -@end - -@implementation HBSampleRateTransformer - -+ (Class)transformedValueClass -{ - return [NSString class]; -} - -- (id)transformedValue:(id)value -{ - const char *name = hb_audio_samplerate_get_name([value intValue]); - if (name) - { - return @(name); - } - else - { - return nil; - } -} - -+ (BOOL)allowsReverseTransformation -{ - return YES; -} - -- (id)reverseTransformedValue:(id)value -{ - int sampleRate = hb_audio_samplerate_get_from_name([value UTF8String]); - if (sampleRate < 0) - { - sampleRate = 0; - } - return @(sampleRate); -} - -@end - -@implementation HBIntegerTransformer - -+ (Class)transformedValueClass -{ - return [NSString class]; -} - -- (id)transformedValue:(id)value -{ - // treat -1 as a special invalid value - // e.g. passthru has no bitrate since we have no source - if ([value intValue] == -1) - { - return @"N/A"; - } - return [value stringValue]; -} - -+ (BOOL)allowsReverseTransformation -{ - return YES; -} - -- (id)reverseTransformedValue:(id)value -{ - return @([value intValue]); -} - -@end diff --git a/macosx/HBAudioTransformers.h b/macosx/HBAudioTransformers.h new file mode 100644 index 000000000..eefaddadc --- /dev/null +++ b/macosx/HBAudioTransformers.h @@ -0,0 +1,25 @@ +// +// HBAudioTransformers.h +// HandBrake +// +// Created by Damiano Galassi on 26/08/2016. +// +// + +#import <Foundation/Foundation.h> + +/** + * A series of value trasformers to bridge the libhb enums + * to the textual rapresentations used in the interface. + */ +@interface HBEncoderTransformer : NSValueTransformer +@end + +@interface HBMixdownTransformer : NSValueTransformer +@end + +@interface HBSampleRateTransformer : NSValueTransformer +@end + +@interface HBIntegerTransformer : NSValueTransformer +@end diff --git a/macosx/HBAudioTransformers.m b/macosx/HBAudioTransformers.m new file mode 100644 index 000000000..4a4adec09 --- /dev/null +++ b/macosx/HBAudioTransformers.m @@ -0,0 +1,143 @@ +// +// HBAudioTransformers.m +// HandBrake +// +// Created by Damiano Galassi on 26/08/2016. +// +// + +#import "HBAudioTransformers.h" +#include "hb.h" + +#pragma mark - Value Trasformers + +@implementation HBEncoderTransformer + ++ (Class)transformedValueClass +{ + return [NSString class]; +} + +- (id)transformedValue:(id)value +{ + const char *name = hb_audio_encoder_get_name([value intValue]); + if (name) + { + return @(name); + } + else + { + return nil; + } +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)reverseTransformedValue:(id)value +{ + return @(hb_audio_encoder_get_from_name([value UTF8String])); +} + +@end + +@implementation HBMixdownTransformer + ++ (Class)transformedValueClass +{ + return [NSString class]; +} + +- (id)transformedValue:(id)value +{ + const char *name = hb_mixdown_get_name([value intValue]); + if (name) + { + return @(name); + } + else + { + return nil; + } +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)reverseTransformedValue:(id)value +{ + return @(hb_mixdown_get_from_name([value UTF8String])); +} + +@end + +@implementation HBSampleRateTransformer + ++ (Class)transformedValueClass +{ + return [NSString class]; +} + +- (id)transformedValue:(id)value +{ + const char *name = hb_audio_samplerate_get_name([value intValue]); + if (name) + { + return @(name); + } + else + { + return nil; + } +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)reverseTransformedValue:(id)value +{ + int sampleRate = hb_audio_samplerate_get_from_name([value UTF8String]); + if (sampleRate < 0) + { + sampleRate = 0; + } + return @(sampleRate); +} + +@end + +@implementation HBIntegerTransformer + ++ (Class)transformedValueClass +{ + return [NSString class]; +} + +- (id)transformedValue:(id)value +{ + // treat -1 as a special invalid value + // e.g. passthru has no bitrate since we have no source + if ([value intValue] == -1) + { + return @"N/A"; + } + return [value stringValue]; +} + ++ (BOOL)allowsReverseTransformation +{ + return YES; +} + +- (id)reverseTransformedValue:(id)value +{ + return @([value intValue]); +} + +@end diff --git a/macosx/HBJob+HBJobConversion.m b/macosx/HBJob+HBJobConversion.m index d26e2cea0..e90ccfd6c 100644 --- a/macosx/HBJob+HBJobConversion.m +++ b/macosx/HBJob+HBJobConversion.m @@ -345,29 +345,31 @@ // Now lets add our new tracks to the audio list here for (HBAudioTrack *audioTrack in self.audio.tracks) { - if (audioTrack.enabled) + if (audioTrack.isEnabled) { hb_audio_config_t *audio = (hb_audio_config_t *)calloc(1, sizeof(*audio)); hb_audio_config_init(audio); - NSNumber *sampleRateToUse = ([audioTrack.sampleRate[keyAudioSamplerate] intValue] == 0 ? - audioTrack.track[keyAudioInputSampleRate] : - audioTrack.sampleRate[keyAudioSamplerate]); + NSDictionary *inputTrack = self.audio.sourceTracks[audioTrack.sourceTrackIdx]; - audio->in.track = [audioTrack.track[keyAudioTrackIndex] intValue] -1; + int sampleRateToUse = (audioTrack.sampleRate == 0 ? + [inputTrack[keyAudioInputSampleRate] intValue] : + audioTrack.sampleRate); + + audio->in.track = (int)audioTrack.sourceTrackIdx - 1; // We go ahead and assign values to our audio->out.<properties> audio->out.track = audio->in.track; - audio->out.codec = [audioTrack.codec[keyAudioCodec] intValue]; + audio->out.codec = audioTrack.encoder; audio->out.compression_level = hb_audio_compression_get_default(audio->out.codec); - audio->out.mixdown = [audioTrack.mixdown[keyAudioMixdown] intValue]; + audio->out.mixdown = audioTrack.mixdown; audio->out.normalize_mix_level = 0; - audio->out.bitrate = [audioTrack.bitRate[keyAudioBitrate] intValue]; - audio->out.samplerate = [sampleRateToUse intValue]; + audio->out.bitrate = audioTrack.bitRate; + audio->out.samplerate = sampleRateToUse; audio->out.dither_method = hb_audio_dither_get_default(); // output is not passthru so apply gain - if (!([[audioTrack codec][keyAudioCodec] intValue] & HB_ACODEC_PASS_FLAG)) + if (!(audioTrack.encoder & HB_ACODEC_PASS_FLAG)) { audio->out.gain = audioTrack.gain; } @@ -377,9 +379,9 @@ audio->out.gain = 0; } - if (hb_audio_can_apply_drc([audioTrack.track[keyAudioInputCodec] intValue], - [audioTrack.track[keyAudioInputCodecParam] intValue], - [audioTrack.codec[keyAudioCodec] intValue])) + if (hb_audio_can_apply_drc([inputTrack[keyAudioInputCodec] intValue], + [inputTrack[keyAudioInputCodecParam] intValue], + audioTrack.encoder)) { audio->out.dynamic_range_compression = audioTrack.drc; } diff --git a/macosx/HBJob+UIAdditions.m b/macosx/HBJob+UIAdditions.m index 829f47300..865cad7d8 100644 --- a/macosx/HBJob+UIAdditions.m +++ b/macosx/HBJob+UIAdditions.m @@ -190,23 +190,25 @@ static NSDictionary *shortHeightAttr; for (HBAudioTrack *audioTrack in self.audio.tracks) { - if (audioTrack.enabled) + if (audioTrack.isEnabled) { - audioCodecSummary = [NSString stringWithFormat: @"%@", audioTrack.codec[keyAudioCodecName]]; - NSNumber *drc = @(audioTrack.drc); - NSNumber *gain = @(audioTrack.gain); - NSString *detailString = [NSString stringWithFormat: @"%@ Encoder: %@ Mixdown: %@ SampleRate: %@(khz) Bitrate: %@(kbps), DRC: %@, Gain: %@", - audioTrack.track[keyAudioTrackName], - audioTrack.codec[keyAudioCodecName], - audioTrack.mixdown[keyAudioMixdownName], - audioTrack.sampleRate[keyAudioSampleRateName], - audioTrack.bitRate[keyAudioBitrateName], - (0.0 < [drc floatValue]) ? (NSObject *)drc : (NSObject *)@"Off", - (0.0 != [gain floatValue]) ? (NSObject *)gain : (NSObject *)@"Off" + const char *codecName = hb_audio_encoder_get_name(audioTrack.encoder); + const char *mixdownName = hb_mixdown_get_name(audioTrack.mixdown); + const char *sampleRateName = audioTrack.sampleRate ? hb_audio_samplerate_get_name(audioTrack.sampleRate) : "Auto"; + + audioCodecSummary = [NSString stringWithFormat: @"%@", @(codecName)]; + NSString *detailString = [NSString stringWithFormat: @"%@ Encoder: %@, Mixdown: %@, SampleRate: %@ khz, Bitrate: %d kbps, DRC: %@, Gain: %@", + self.audio.sourceTracks[audioTrack.sourceTrackIdx][keyAudioTrackName], + @(codecName), + @(mixdownName), + @(sampleRateName), + audioTrack.bitRate, + (0.0 < audioTrack.drc) ? @(audioTrack.drc) : NSLocalizedString(@"Off", nil), + (0.0 != audioTrack.gain) ? @(audioTrack.gain) : NSLocalizedString(@"Off", nil) ]; [audioDetails addObject: detailString]; // check if we have an Auto Passthru output track - if ([audioTrack.codec[keyAudioCodecName] isEqualToString: @"Auto Passthru"]) + if ([@(codecName) isEqualToString: @"Auto Passthru"]) { autoPassthruPresent = YES; } @@ -459,32 +461,28 @@ static NSDictionary *shortHeightAttr; } // Ninth Line Subtitle Details - int i = 0; for (HBSubtitlesTrack *track in self.subtitles.tracks) { // Ignore the none track. - if (i == self.subtitles.tracks.count - 1) + if (track.isEnabled) { - continue; - } - - /* remember that index 0 of Subtitles can contain "Foreign Audio Search*/ - [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr]; - [finalString appendString: self.subtitles.sourceTracks[track.sourceTrackIdx][@"keySubTrackName"] withAttributes:detailAttr]; - if (track.forcedOnly) - { - [finalString appendString: @" - Forced Only" withAttributes:detailAttr]; - } - if (track.burnedIn) - { - [finalString appendString: @" - Burned In" withAttributes:detailAttr]; - } - if (track.def) - { - [finalString appendString: @" - Default" withAttributes:detailAttr]; + // remember that index 0 of Subtitles can contain "Foreign Audio Search + [finalString appendString: @"Subtitle: " withAttributes:detailBoldAttr]; + [finalString appendString: self.subtitles.sourceTracks[track.sourceTrackIdx][@"keySubTrackName"] withAttributes:detailAttr]; + if (track.forcedOnly) + { + [finalString appendString: @" - Forced Only" withAttributes:detailAttr]; + } + if (track.burnedIn) + { + [finalString appendString: @" - Burned In" withAttributes:detailAttr]; + } + if (track.def) + { + [finalString appendString: @" - Default" withAttributes:detailAttr]; + } + [finalString appendString:@"\n" withAttributes:detailAttr]; } - [finalString appendString:@"\n" withAttributes:detailAttr]; - i++; } } diff --git a/macosx/HandBrake.xcodeproj/project.pbxproj b/macosx/HandBrake.xcodeproj/project.pbxproj index 0964e5b73..c6fd4ce65 100644 --- a/macosx/HandBrake.xcodeproj/project.pbxproj +++ b/macosx/HandBrake.xcodeproj/project.pbxproj @@ -171,6 +171,8 @@ A91CE2FC1C7DB99D0068F46F /* HBPreset.h in Headers */ = {isa = PBXBuildFile; fileRef = A9CF25F21990D64E0023F727 /* HBPreset.h */; settings = {ATTRIBUTES = (Public, ); }; }; A91CE2FD1C7DB99D0068F46F /* HBMutablePreset.h in Headers */ = {isa = PBXBuildFile; fileRef = A96CD1741BCC5F9100F372F1 /* HBMutablePreset.h */; settings = {ATTRIBUTES = (Public, ); }; }; A91CE2FE1C7DB99D0068F46F /* HBTreeNode.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D488A31996270300E9B1BA /* HBTreeNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; + A91F97351D7B2A4E00D82DCE /* HBAudioTransformers.h in Headers */ = {isa = PBXBuildFile; fileRef = A91F97331D7B2A4E00D82DCE /* HBAudioTransformers.h */; }; + A91F97361D7B2A4E00D82DCE /* HBAudioTransformers.m in Sources */ = {isa = PBXBuildFile; fileRef = A91F97341D7B2A4E00D82DCE /* HBAudioTransformers.m */; }; A92268781A6E555500A8D5C5 /* HBAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A92268771A6E555500A8D5C5 /* HBAppDelegate.m */; }; A922687B1A6E569B00A8D5C5 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = A92268791A6E569B00A8D5C5 /* MainWindow.xib */; }; A932E26C1988334B0047D13E /* AudioDefaults.xib in Resources */ = {isa = PBXBuildFile; fileRef = A932E26A1988334B0047D13E /* AudioDefaults.xib */; }; @@ -412,6 +414,8 @@ A91CE2CF1C7DABCE0068F46F /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; A91CE2D11C7DABDA0068F46F /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; A91CE2D31C7DABE40068F46F /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; + A91F97331D7B2A4E00D82DCE /* HBAudioTransformers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAudioTransformers.h; sourceTree = "<group>"; }; + A91F97341D7B2A4E00D82DCE /* HBAudioTransformers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAudioTransformers.m; sourceTree = "<group>"; }; A92268761A6E555500A8D5C5 /* HBAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBAppDelegate.h; sourceTree = "<group>"; }; A92268771A6E555500A8D5C5 /* HBAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBAppDelegate.m; sourceTree = "<group>"; }; A922687A1A6E569B00A8D5C5 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = MainWindow.xib; sourceTree = "<group>"; }; @@ -1120,6 +1124,8 @@ A932E272198834130047D13E /* HBAudioDefaults.m */, A90A0CAD1988D57200DA65CE /* HBAudioTrackPreset.h */, A90A0CAE1988D57200DA65CE /* HBAudioTrackPreset.m */, + A91F97331D7B2A4E00D82DCE /* HBAudioTransformers.h */, + A91F97341D7B2A4E00D82DCE /* HBAudioTransformers.m */, ); name = Audio; sourceTree = "<group>"; @@ -1252,6 +1258,7 @@ A91CE2FC1C7DB99D0068F46F /* HBPreset.h in Headers */, A91CE2FD1C7DB99D0068F46F /* HBMutablePreset.h in Headers */, A98B8E241C7DD2A200B810C9 /* HBPresetCoding.h in Headers */, + A91F97351D7B2A4E00D82DCE /* HBAudioTransformers.h in Headers */, A91CE2FE1C7DB99D0068F46F /* HBTreeNode.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1525,6 +1532,7 @@ A91CE27F1C7DA7320068F46F /* HBTitle.m in Sources */, A91CE2821C7DA7320068F46F /* HBJob.m in Sources */, A91CE2841C7DA7320068F46F /* HBJob+HBJobConversion.m in Sources */, + A91F97361D7B2A4E00D82DCE /* HBAudioTransformers.m in Sources */, A91CE2861C7DA7320068F46F /* HBRange.m in Sources */, A91CE2881C7DA7320068F46F /* HBVideo.m in Sources */, A91CE28A1C7DA7320068F46F /* HBPicture.m in Sources */, |