mirror of https://github.com/HandBrake/HandBrake
392 lines
10 KiB
Objective-C
392 lines
10 KiB
Objective-C
/* HBAudio.m $
|
|
|
|
This file is part of the HandBrake source code.
|
|
Homepage: <http://handbrake.fr/>.
|
|
It may be used under the terms of the GNU General Public License. */
|
|
|
|
#import "HBAudio.h"
|
|
|
|
#import "HBJob.h"
|
|
#import "HBJob+HBJobConversion.h"
|
|
#import "HBTitle.h"
|
|
#import "HBAudioTrack.h"
|
|
#import "HBAudioTrackPreset.h"
|
|
#import "HBAudioDefaults.h"
|
|
|
|
#import "HBCodingUtilities.h"
|
|
#import "HBLocalizationUtilities.h"
|
|
#import "HBJob+Private.h"
|
|
|
|
#include "handbrake/handbrake.h"
|
|
|
|
#define NONE_TRACK_INDEX 0
|
|
|
|
NSString *HBAudioEncoderChangedNotification = @"HBAudioEncoderChangedNotification";
|
|
|
|
@interface HBAudio () <HBAudioTrackDataSource, HBAudioTrackDelegate>
|
|
|
|
@property (nonatomic, readwrite, weak) HBJob *job;
|
|
@property (nonatomic, readwrite) int container;
|
|
|
|
@end
|
|
|
|
@implementation HBAudio
|
|
|
|
- (instancetype)initWithJob:(HBJob *)job
|
|
{
|
|
self = [super init];
|
|
if (self)
|
|
{
|
|
_job = job;
|
|
_container = HB_MUX_MP4;
|
|
|
|
_tracks = [[NSMutableArray alloc] init];
|
|
_defaults = [[HBAudioDefaults alloc] init];
|
|
|
|
// Add the none track to the source array
|
|
NSMutableArray<HBTitleAudioTrack *> *sourceTracks = [job.title.audioTracks mutableCopy];
|
|
HBTitleAudioTrack *none = [[HBTitleAudioTrack alloc] initWithDisplayName:HBKitLocalizedString(@"None", @"HBAudio -> none track name")];
|
|
[sourceTracks insertObject:none atIndex:0];
|
|
_sourceTracks = [sourceTracks copy];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - Data Source
|
|
|
|
- (nullable NSString *)defaultTitleForTrackAtIndex:(NSUInteger)idx mixdown:(int)mixdown
|
|
{
|
|
HBTitleAudioTrack *track = [self sourceTrackAtIndex:idx];
|
|
|
|
const char *title = hb_audio_name_generate_s(track.title.UTF8String,
|
|
track.chLayout.UTF8String, mixdown,
|
|
self.defaults.passthruName,
|
|
(hb_audio_autonaming_behavior_t)self.defaults.automaticNamingBehavior);
|
|
|
|
return title ? @(title) : nil;
|
|
}
|
|
|
|
- (HBTitleAudioTrack *)sourceTrackAtIndex:(NSUInteger)idx
|
|
{
|
|
return self.sourceTracks[idx];
|
|
}
|
|
|
|
- (NSArray<NSString *> *)sourceTracksArray
|
|
{
|
|
NSMutableArray<NSString *> *sourceNames = [NSMutableArray array];
|
|
|
|
for (HBTitleAudioTrack *track in self.sourceTracks)
|
|
{
|
|
if (track.title.length)
|
|
{
|
|
[sourceNames addObject:[NSString stringWithFormat:@"%@ - %@", track.displayName, track.title]];
|
|
}
|
|
else
|
|
{
|
|
[sourceNames addObject:track.displayName];
|
|
}
|
|
}
|
|
|
|
return sourceNames;
|
|
}
|
|
|
|
#pragma mark - Delegate
|
|
|
|
- (void)track:(HBAudioTrack *)track didChangeSourceFrom:(NSUInteger)oldSourceIdx
|
|
{
|
|
// 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)addNoneTrack
|
|
{
|
|
HBAudioTrack *track = [self trackFromSourceTrackIndex:NONE_TRACK_INDEX];
|
|
[self addTrack:track];
|
|
}
|
|
|
|
#pragma mark - Public methods
|
|
|
|
- (void)addAllTracks
|
|
{
|
|
[self removeTracksAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.tracks.count)]];
|
|
|
|
// Add the remaining tracks
|
|
for (NSUInteger idx = 1; idx < self.sourceTracksArray.count; idx++)
|
|
{
|
|
[self addTrack:[self trackFromSourceTrackIndex:idx]];
|
|
}
|
|
|
|
[self addNoneTrack];
|
|
}
|
|
|
|
- (void)removeAll
|
|
{
|
|
[self removeTracksAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.tracks.count)]];
|
|
[self addNoneTrack];
|
|
}
|
|
|
|
- (void)reloadDefaults
|
|
{
|
|
[self addDefaultTracksFromJobSettings:self.job.jobDict];
|
|
}
|
|
|
|
- (void)setContainer:(int)container
|
|
{
|
|
_container = container;
|
|
if (!(self.undo.isUndoing || self.undo.isRedoing))
|
|
{
|
|
for (HBAudioTrack *track in self.tracks)
|
|
{
|
|
track.container = container;
|
|
}
|
|
|
|
// Update the Auto Passthru Fallback Codec Popup
|
|
// lets get the tag of the currently selected item first so we might reset it later
|
|
[self.defaults validateEncoderFallbackForVideoContainer:container];
|
|
}
|
|
}
|
|
|
|
- (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;
|
|
}
|
|
|
|
/**
|
|
* Convenience method to add a track to tracks array.
|
|
*
|
|
* @param track the track to add.
|
|
*/
|
|
- (void)addTrack:(HBAudioTrack *)newTrack
|
|
{
|
|
[self insertObject:newTrack inTracksAtIndex:[self countOfTracks]];
|
|
}
|
|
|
|
/**
|
|
* Creates a new track dictionary from a source track.
|
|
*
|
|
* @param index the index of the source track in the sourceTracks array
|
|
*/
|
|
- (HBAudioTrack *)trackFromSourceTrackIndex:(NSInteger)trackIndex
|
|
{
|
|
HBAudioTrack *track = [[HBAudioTrack alloc] initWithTrackIdx:trackIndex container:self.container dataSource:self delegate:self];
|
|
track.undo = self.undo;
|
|
|
|
return track;
|
|
}
|
|
|
|
- (HBAudioTrack *)trackFromSourceTitleTrackIndex:(NSInteger)trackIndex
|
|
{
|
|
NSInteger index = 0;
|
|
for (HBTitleAudioTrack *sourceTrack in self.sourceTracks)
|
|
{
|
|
if (sourceTrack.index == trackIndex)
|
|
{
|
|
HBAudioTrack *track = [[HBAudioTrack alloc] initWithTrackIdx:index container:self.container dataSource:self delegate:self];
|
|
track.undo = self.undo;
|
|
return track;
|
|
}
|
|
index += 1;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
#pragma mark - Defaults
|
|
|
|
- (void)addDefaultTracksFromJobSettings:(NSDictionary *)settings
|
|
{
|
|
NSMutableArray<HBAudioTrack *> *tracks = [NSMutableArray array];
|
|
NSArray<NSDictionary<NSString *, id> *> *settingsTracks = settings[@"Audio"][@"AudioList"];
|
|
|
|
// Reinitialize the configured list of audio tracks
|
|
[self removeTracksAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.tracks.count)]];
|
|
|
|
// Add the tracks
|
|
for (NSDictionary *trackDict in settingsTracks)
|
|
{
|
|
HBAudioTrack *track = [self trackFromSourceTitleTrackIndex:[trackDict[@"Track"] unsignedIntegerValue]];
|
|
if (track)
|
|
{
|
|
track.drc = [trackDict[@"DRC"] doubleValue];
|
|
track.gain = [trackDict[@"Gain"] doubleValue];
|
|
track.mixdown = hb_mixdown_get_from_name([trackDict[@"Mixdown"] UTF8String]);
|
|
track.sampleRate = [trackDict[@"Samplerate"] intValue] == -1 ? 0 : [trackDict[@"Samplerate"] intValue];
|
|
track.bitRate = [trackDict[@"Bitrate"] intValue];
|
|
track.encoder = hb_audio_encoder_get_from_name([trackDict[@"Encoder"] UTF8String]);
|
|
track.title = trackDict[@"Name"];
|
|
|
|
[tracks addObject:track];
|
|
}
|
|
}
|
|
|
|
[self insertTracks:tracks atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, tracks.count)]];
|
|
|
|
// Add an None item
|
|
[self addNoneTrack];
|
|
}
|
|
|
|
- (BOOL)anyCodecMatches:(int)codec
|
|
{
|
|
for (HBAudioTrack *track in self.tracks)
|
|
{
|
|
if (track.isEnabled && codec == track.encoder)
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark Notification Handling
|
|
|
|
- (void)encoderChanged
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:HBAudioEncoderChangedNotification object:self];
|
|
}
|
|
|
|
#pragma mark - NSCopying
|
|
|
|
- (instancetype)copyWithZone:(NSZone *)zone
|
|
{
|
|
HBAudio *copy = [[[self class] alloc] init];
|
|
|
|
if (copy)
|
|
{
|
|
copy->_container = _container;
|
|
copy->_sourceTracks = [_sourceTracks copy];
|
|
|
|
copy->_tracks = [[NSMutableArray alloc] init];
|
|
|
|
for (HBAudioTrack *track in _tracks)
|
|
{
|
|
HBAudioTrack *trackCopy = [track copy];
|
|
[copy->_tracks addObject:trackCopy];
|
|
|
|
trackCopy.dataSource = copy;
|
|
trackCopy.delegate = copy;
|
|
}
|
|
|
|
copy->_defaults = [_defaults copy];
|
|
}
|
|
|
|
return copy;
|
|
}
|
|
|
|
#pragma mark - NSCoding
|
|
|
|
+ (BOOL)supportsSecureCoding
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (void)encodeWithCoder:(NSCoder *)coder
|
|
{
|
|
[coder encodeInt:2 forKey:@"HBAudioVersion"];
|
|
|
|
encodeInt(_container);
|
|
encodeObject(_sourceTracks);
|
|
encodeObject(_tracks);
|
|
encodeObject(_defaults);
|
|
}
|
|
|
|
- (instancetype)initWithCoder:(NSCoder *)decoder
|
|
{
|
|
self = [super init];
|
|
|
|
decodeInt(_container); if (_container != HB_MUX_MP4 && _container != HB_MUX_MKV && _container != HB_MUX_WEBM) { goto fail; }
|
|
decodeCollectionOfObjectsOrFail(_sourceTracks, NSArray, HBTitleAudioTrack);
|
|
if (_sourceTracks.count < 1) { goto fail; }
|
|
decodeCollectionOfObjectsOrFail(_tracks, NSMutableArray, HBAudioTrack);
|
|
|
|
for (HBAudioTrack *track in _tracks)
|
|
{
|
|
track.dataSource = self;
|
|
track.delegate = self;
|
|
}
|
|
|
|
decodeObjectOrFail(_defaults, HBAudioDefaults);
|
|
|
|
return self;
|
|
|
|
fail:
|
|
return nil;
|
|
}
|
|
|
|
#pragma mark - Presets
|
|
|
|
- (void)writeToPreset:(HBMutablePreset *)preset
|
|
{
|
|
[self.defaults writeToPreset:preset];
|
|
}
|
|
|
|
- (void)applyPreset:(HBPreset *)preset jobSettings:(NSDictionary *)settings
|
|
{
|
|
[self.defaults applyPreset:preset jobSettings:settings];
|
|
[self addDefaultTracksFromJobSettings:settings];
|
|
}
|
|
|
|
#pragma mark -
|
|
#pragma mark KVC
|
|
|
|
- (NSUInteger)countOfTracks
|
|
{
|
|
return self.tracks.count;
|
|
}
|
|
|
|
- (HBAudioTrack *)objectInTracksAtIndex:(NSUInteger)index
|
|
{
|
|
return self.tracks[index];
|
|
}
|
|
|
|
- (void)insertObject:(HBAudioTrack *)track inTracksAtIndex:(NSUInteger)index
|
|
{
|
|
[[self.undo prepareWithInvocationTarget:self] removeObjectFromTracksAtIndex:index];
|
|
[self.tracks insertObject:track atIndex:index];
|
|
}
|
|
|
|
- (void)insertTracks:(NSArray<HBAudioTrack *> *)array atIndexes:(NSIndexSet *)indexes
|
|
{
|
|
[[self.undo prepareWithInvocationTarget:self] removeTracksAtIndexes:indexes];
|
|
[self.tracks insertObjects:array atIndexes:indexes];
|
|
}
|
|
|
|
- (void)removeObjectFromTracksAtIndex:(NSUInteger)index
|
|
{
|
|
HBAudioTrack *track = self.tracks[index];
|
|
[[self.undo prepareWithInvocationTarget:self] insertObject:track inTracksAtIndex:index];
|
|
[self.tracks removeObjectAtIndex:index];
|
|
}
|
|
|
|
- (void)removeTracksAtIndexes:(NSIndexSet *)indexes
|
|
{
|
|
NSArray<HBAudioTrack *> *tracks = [self.tracks objectsAtIndexes:indexes];
|
|
[[self.undo prepareWithInvocationTarget:self] insertTracks:tracks atIndexes:indexes];
|
|
[self.tracks removeObjectsAtIndexes:indexes];
|
|
}
|
|
|
|
@end
|