OSDN Git Service

MacGui: Audio ... Allow more than 4 audio tracks.
[handbrake-jp/handbrake-jp-git.git] / macosx / HBAudioController.m
1 //
2 //  HBAudioController.m
3 //  HandBrake
4 //
5 //  Created on 2010-08-24.
6 //
7
8 #import "HBAudioController.h"
9 #import "Controller.h"
10 #import "HBAudio.h"
11 #import "hb.h"
12
13 NSString *keyAudioTrackIndex = @"keyAudioTrackIndex";
14 NSString *keyAudioTrackName = @"keyAudioTrackName";
15 NSString *keyAudioInputBitrate = @"keyAudioInputBitrate";
16 NSString *keyAudioInputSampleRate = @"keyAudioInputSampleRate";
17 NSString *keyAudioInputCodec = @"keyAudioInputCodec";
18 NSString *keyAudioInputChannelLayout = @"keyAudioInputChannelLayout";
19 NSString *HBMixdownChangedNotification = @"HBMixdownChangedNotification";
20
21 @implementation HBAudioController
22
23 #pragma mark -
24 #pragma mark Accessors
25
26 @synthesize masterTrackArray;
27 @synthesize noneTrack;
28 @synthesize videoContainerTag;
29
30 - (id) init
31
32 {
33         if (self = [super init]) {
34                 [self setVideoContainerTag: [NSNumber numberWithInt: HB_MUX_MP4]];
35         }
36         return self;
37 }
38
39 - (void) dealloc
40
41 {
42         [[NSNotificationCenter defaultCenter] removeObserver: self];
43         [masterTrackArray release];
44         [noneTrack release];
45         [audioArray release];
46         [self setVideoContainerTag: nil];
47         [super dealloc];
48         return;
49 }
50
51 - (void) setHBController: (id) aController
52
53 {
54         NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
55         myController = aController;
56
57         /* register that we are interested in changes made to the video container */
58         [center addObserver: self selector: @selector(containerChanged:) name: HBContainerChangedNotification object: aController];
59         [center addObserver: self selector: @selector(titleChanged:) name: HBTitleChangedNotification object: aController];
60         return;
61 }
62
63 #pragma mark -
64 #pragma mark HBController Support
65
66 - (void) prepareAudioForQueueFileJob: (NSMutableDictionary *) aDict
67
68 {
69         unsigned int audioArrayCount = [self countOfAudioArray];
70         for (unsigned int counter = 0; counter < audioArrayCount; counter++) {
71                 HBAudio *anAudio = [self objectInAudioArrayAtIndex: counter];
72                 if (YES == [anAudio enabled]) {
73                         NSString *prefix = [NSString stringWithFormat: @"Audio%d", counter + 1];
74                         NSNumber *sampleRateToUse = (0 == [[[anAudio sampleRate] objectForKey: keyAudioSamplerate] intValue]) ?
75                                                                 [[anAudio track] objectForKey: keyAudioInputSampleRate] :
76                                                                 [[anAudio sampleRate] objectForKey: keyAudioSamplerate];
77                 
78                         [aDict setObject: [[anAudio track] objectForKey: keyAudioTrackIndex] forKey: [prefix stringByAppendingString: @"Track"]];
79                         [aDict setObject: [[anAudio track] objectForKey: keyAudioTrackName] forKey: [prefix stringByAppendingString: @"TrackDescription"]];
80                         [aDict setObject: [[anAudio codec] objectForKey: keyAudioCodecName] forKey: [prefix stringByAppendingString: @"Encoder"]];
81                         [aDict setObject: [[anAudio mixdown] objectForKey: keyAudioMixdownName] forKey: [prefix stringByAppendingString: @"Mixdown"]];
82                         [aDict setObject: [[anAudio sampleRate] objectForKey: keyAudioSampleRateName] forKey: [prefix stringByAppendingString: @"Samplerate"]];
83                         [aDict setObject: [[anAudio bitRate] objectForKey: keyAudioBitrateName] forKey: [prefix stringByAppendingString: @"Bitrate"]];
84                         [aDict setObject: [anAudio drc] forKey: [prefix stringByAppendingString: @"TrackDRCSlider"]];
85                 
86                         prefix = [NSString stringWithFormat: @"JobAudio%d", counter + 1];
87                         [aDict setObject: [[anAudio codec] objectForKey: keyAudioCodec] forKey: [prefix stringByAppendingString: @"Encoder"]];
88                         [aDict setObject: [[anAudio mixdown] objectForKey: keyAudioMixdown] forKey: [prefix stringByAppendingString: @"Mixdown"]];
89                         [aDict setObject: sampleRateToUse forKey: [prefix stringByAppendingString: @"Samplerate"]];
90                         [aDict setObject: [[anAudio bitRate] objectForKey: keyAudioBitrate] forKey: [prefix stringByAppendingString: @"Bitrate"]];
91                 }
92         }
93         return;
94 }
95
96 - (void) prepareAudioForJob: (hb_job_t *) aJob
97
98 {
99         unsigned int i;
100         
101         //      First clear out any audio tracks in the job currently
102     int audiotrack_count = hb_list_count(aJob->list_audio);
103     for(i = 0; i < audiotrack_count; i++)
104     {
105         hb_audio_t *temp_audio = (hb_audio_t *) hb_list_item(aJob->list_audio, 0);
106         hb_list_rem(aJob->list_audio, temp_audio);
107     }
108
109         //      Now add audio tracks based on the current settings
110         unsigned int audioArrayCount = [self countOfAudioArray];
111         for (i = 0; i < audioArrayCount; i++) {
112                 HBAudio *anAudio = [self objectInAudioArrayAtIndex: i];
113                 if (YES == [anAudio enabled]) {
114                         NSNumber *sampleRateToUse = (0 == [[[anAudio sampleRate] objectForKey: keyAudioSamplerate] intValue]) ?
115                                                                                 [[anAudio track] objectForKey: keyAudioInputSampleRate] :
116                                                                                 [[anAudio sampleRate] objectForKey: keyAudioSamplerate];
117                         
118                         hb_audio_config_t *audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
119                         hb_audio_config_init(audio);
120                         audio->in.track = [[[anAudio track] objectForKey: keyAudioTrackIndex] intValue] - 1;
121                         /* We go ahead and assign values to our audio->out.<properties> */
122                         audio->out.track = audio->in.track;
123                         audio->out.codec = [[[anAudio codec] objectForKey: keyAudioCodec] intValue];
124                         audio->out.mixdown = [[[anAudio mixdown] objectForKey: keyAudioMixdown] intValue];
125                         audio->out.bitrate = [[[anAudio bitRate] objectForKey: keyAudioBitrate] intValue];
126                         audio->out.samplerate = [sampleRateToUse intValue];
127                         audio->out.dynamic_range_compression = [[anAudio drc] floatValue];
128         
129                         hb_audio_add(aJob, audio);
130                         free(audio);
131                 }
132         }
133         return;
134 }
135
136 - (void) prepareAudioForPreset: (NSMutableArray *) anArray
137
138 {
139         unsigned int audioArrayCount = [self countOfAudioArray];
140         unsigned int i;
141
142         for (i = 0; i < audioArrayCount; i++) {
143                 HBAudio *anAudio = [self objectInAudioArrayAtIndex: i];
144                 if (YES == [anAudio enabled]) {
145                         NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithCapacity: 7];
146                         [dict setObject: [[anAudio track] objectForKey: keyAudioTrackIndex] forKey: @"AudioTrack"];
147                         [dict setObject: [[anAudio track] objectForKey: keyAudioTrackName] forKey: @"AudioTrackDescription"];
148                         [dict setObject: [[anAudio codec] objectForKey: keyAudioCodecName] forKey: @"AudioEncoder"];
149                         [dict setObject: [[anAudio mixdown] objectForKey: keyAudioMixdownName] forKey: @"AudioMixdown"];
150                         [dict setObject: [[anAudio sampleRate] objectForKey: keyAudioSampleRateName] forKey: @"AudioSamplerate"];
151                         [dict setObject: [[anAudio bitRate] objectForKey: keyAudioBitrateName] forKey: @"AudioBitrate"];
152                         [dict setObject: [anAudio drc] forKey: @"AudioTrackDRCSlider"];
153                         [anArray addObject: dict];
154                         [dict release];
155                 }
156         }
157         return;
158 }
159
160 - (void) addTracksFromQueue: (NSMutableDictionary *) aQueue
161
162 {
163         NSString *base;
164         int value;
165         int maximumNumberOfAllowedAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
166
167         //      Reinitialize the configured list of audio tracks
168         [audioArray release];
169         audioArray = [[NSMutableArray alloc] init];
170         
171         //      The following is the pattern to follow, but with Audio%dTrack being the key to seek...
172         //      Can we assume that there will be no skip in the data?
173         for (unsigned int i = 1; i <= maximumNumberOfAllowedAudioTracks; i++) {
174                 base = [NSString stringWithFormat: @"Audio%d", i];
175                 value = [[aQueue objectForKey: [base stringByAppendingString: @"Track"]] intValue];
176                 if (0 < value) {
177                         HBAudio *newAudio = [[HBAudio alloc] init];
178                         [newAudio setController: self];
179                         [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]];
180                         [newAudio setVideoContainerTag: [self videoContainerTag]];
181                         [newAudio setTrackFromIndex: value];
182                         [newAudio setCodecFromName: [aQueue objectForKey: [base stringByAppendingString: @"Encoder"]]];
183                         [newAudio setMixdownFromName: [aQueue objectForKey: [base stringByAppendingString: @"Mixdown"]]];
184                         [newAudio setSampleRateFromName: [aQueue objectForKey: [base stringByAppendingString: @"Samplerate"]]];
185                         [newAudio setBitRateFromName: [aQueue objectForKey: [base stringByAppendingString: @"Bitrate"]]];
186                         [newAudio setDrc: [aQueue objectForKey: [base stringByAppendingString: @"TrackDRCSlider"]]];
187                         [newAudio release];
188                 }
189         }
190
191         [self switchingTrackFromNone: nil];     // see if we need to add one to the list
192         
193         return;
194 }
195
196 - (void) addTracksFromPreset: (NSMutableDictionary *) aPreset
197
198 {
199         id whatToUse = nil;
200
201         //      If we do not have an AudioList we need to make one from the data we have
202         if (nil == (whatToUse = [aPreset objectForKey: @"AudioList"])) {
203                 int maximumNumberOfAllowedAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
204                 NSString *base;
205
206                 whatToUse = [NSMutableArray array];
207                 for (unsigned int i = 1; i <= maximumNumberOfAllowedAudioTracks; i++) {
208                         base = [NSString stringWithFormat: @"Audio%d", i];
209                         if (nil != [aPreset objectForKey: [base stringByAppendingString: @"Track"]]) {
210                                 [whatToUse addObject: [NSDictionary dictionaryWithObjectsAndKeys:
211                                                                            [aPreset objectForKey: [base stringByAppendingString: @"Encoder"]], @"AudioEncoder",
212                                                                            [aPreset objectForKey: [base stringByAppendingString: @"Mixdown"]], @"AudioMixdown",
213                                                                            [aPreset objectForKey: [base stringByAppendingString: @"Samplerate"]], @"AudioSamplerate",
214                                                                            [aPreset objectForKey: [base stringByAppendingString: @"Bitrate"]], @"AudioBitrate",
215                                                                            [aPreset objectForKey: [base stringByAppendingString: @"TrackDRCSlider"]], @"AudioTrackDRCSlider",
216                                                                            nil]];
217                         }
218                 }
219         }
220
221         //      Reinitialize the configured list of audio tracks
222         [audioArray release];
223         audioArray = [[NSMutableArray alloc] init];
224         
225         //      Now to process the list
226         NSEnumerator *enumerator = [whatToUse objectEnumerator];
227         NSDictionary *dict;
228         NSString *key;
229         
230         while (nil != (dict = [enumerator nextObject])) {
231                 HBAudio *newAudio = [[HBAudio alloc] init];
232                 [newAudio setController: self];
233                 [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]];
234                 [newAudio setVideoContainerTag: [self videoContainerTag]];
235                 [newAudio setTrackFromIndex: 1];
236                 key = [dict objectForKey: @"AudioEncoder"];
237                 if (0 == [[aPreset objectForKey: @"Type"] intValue] &&
238                         YES == [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] &&
239                         YES == [key isEqualToString: @"AAC (faac)"]
240                         ) {
241                         key = @"AAC (CoreAudio)";
242                 }
243                 //      If our preset wants us to support a codec that the track does not support, instead
244                 //      of changing the codec we remove the audio instead.
245                 if (YES == [newAudio setCodecFromName: key]) {
246                         [newAudio setMixdownFromName: [dict objectForKey: @"AudioMixdown"]];
247                         [newAudio setSampleRateFromName: [dict objectForKey: @"AudioSamplerate"]];
248                         [newAudio setBitRateFromName: [dict objectForKey: @"AudioBitrate"]];
249                         [newAudio setDrc: [dict objectForKey: @"AudioTrackDRCSlider"]];
250                 }
251                 else {
252                         [self removeObjectFromAudioArrayAtIndex: [self countOfAudioArray] - 1];
253                 }
254                 [newAudio release];
255         }
256
257         [self switchingTrackFromNone: nil];     // see if we need to add one to the list
258
259         return;
260 }
261
262 - (BOOL) anyCodecMatches: (int) aCodecValue
263
264 {
265         BOOL retval = NO;
266         unsigned int audioArrayCount = [self countOfAudioArray];
267         for (unsigned int i = 0; i < audioArrayCount && NO == retval; i++) {
268                 HBAudio *anAudio = [self objectInAudioArrayAtIndex: i];
269         if (YES == [anAudio enabled] && aCodecValue == [[[anAudio codec] objectForKey: keyAudioCodec] intValue]) {
270                         retval = YES;
271                 }
272         }
273         return retval;
274 }
275
276 - (void) addNewAudioTrack
277
278 {
279         HBAudio *newAudio = [[HBAudio alloc] init];
280         [newAudio setController: self];
281         [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]];
282         [newAudio setVideoContainerTag: [self videoContainerTag]];
283         [newAudio setTrack: noneTrack];
284         [newAudio setDrc: [NSNumber numberWithFloat: 0.0]];
285         [newAudio release];     
286         return;
287 }
288
289 #pragma mark -
290 #pragma mark Notification Handling
291
292 - (void) settingTrackToNone: (HBAudio *) newNoneTrack
293
294 {
295         //      If this is not the last track in the array we need to remove it.  We then need to see if a new
296         //      one needs to be added (in the case when we were at maximum count and this switching makes it
297         //      so we are no longer at maximum.
298         unsigned int index = [audioArray indexOfObject: newNoneTrack];
299
300         if (NSNotFound != index && index < [self countOfAudioArray] - 1) {
301                 [self removeObjectFromAudioArrayAtIndex: index];
302         }
303         [self switchingTrackFromNone: nil];     // see if we need to add one to the list
304         return;
305 }
306
307 - (void) switchingTrackFromNone: (HBAudio *) noLongerNoneTrack
308
309 {
310         int count = [self countOfAudioArray];
311         BOOL needToAdd = NO;
312         int maximumNumberOfAllowedAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
313
314         //      If there is no last track that is None and we are less than our maximum number of permitted tracks, we add one.
315         if (count < maximumNumberOfAllowedAudioTracks) {
316                 if (0 < count) {
317                         HBAudio *lastAudio = [self objectInAudioArrayAtIndex: count - 1];
318                         if (YES == [lastAudio enabled]) {
319                                 needToAdd = YES;
320                         }
321                 }
322                 else {
323                         needToAdd = YES;
324                 }
325         }
326
327         if (YES == needToAdd) {
328                 [self addNewAudioTrack];
329         }
330         return;
331 }
332
333 //      This gets called whenever the video container changes.
334 - (void) containerChanged: (NSNotification *) aNotification
335
336 {
337         NSDictionary *notDict = [aNotification userInfo];
338
339         [self setVideoContainerTag: [notDict objectForKey: keyContainerTag]];
340
341         //      Update each of the instances because this value influences possible settings.
342         NSEnumerator *enumerator = [audioArray objectEnumerator];
343         HBAudio *audioObject;
344
345         while (nil != (audioObject = [enumerator nextObject])) {
346                 [audioObject setVideoContainerTag: [self videoContainerTag]];
347         }
348         return;
349 }
350
351 - (void) titleChanged: (NSNotification *) aNotification
352
353 {
354         NSDictionary *notDict = [aNotification userInfo];
355         NSData *theData = [notDict objectForKey: keyTitleTag];
356         hb_title_t *title = NULL;
357
358         [theData getBytes: &title length: sizeof(title)];
359         if (title) {
360                 hb_audio_config_t *audio;
361                 hb_list_t *list = title->list_audio;
362                 int i, count = hb_list_count(list);
363
364                 //      Reinitialize the master list of available audio tracks from this title
365                 [masterTrackArray release];
366                 masterTrackArray = [[NSMutableArray alloc] init];
367                 [noneTrack release];
368                 noneTrack = [[NSDictionary dictionaryWithObjectsAndKeys:
369                                          [NSNumber numberWithInt: 0], keyAudioTrackIndex,
370                                          NSLocalizedString(@"None", @"None"), keyAudioTrackName,
371                                          [NSNumber numberWithInt: 0], keyAudioInputCodec,
372                                                          nil] retain];
373                 [masterTrackArray addObject: noneTrack];
374                 for (i = 0; i < count; i++) {
375                         audio = (hb_audio_config_t *) hb_list_audio_config_item(list, i);
376                         [masterTrackArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
377                                                                                   [NSNumber numberWithInt: i + 1], keyAudioTrackIndex,
378                                                                                   [NSString stringWithFormat: @"%d: %s", i, audio->lang.description], keyAudioTrackName,
379                                                                                   [NSNumber numberWithInt: audio->in.bitrate / 1000], keyAudioInputBitrate,
380                                                                                   [NSNumber numberWithInt: audio->in.samplerate], keyAudioInputSampleRate,
381                                                                                   [NSNumber numberWithInt: audio->in.codec], keyAudioInputCodec,
382                                                                                   [NSNumber numberWithInt: audio->in.channel_layout], keyAudioInputChannelLayout,
383                                                                                   nil]];
384                 }
385         }
386
387         //      Reinitialize the configured list of audio tracks
388         [audioArray release];
389         audioArray = [[NSMutableArray alloc] init];
390
391         return;
392 }
393
394 #pragma mark -
395 #pragma mark KVC
396
397 - (unsigned int) countOfAudioArray
398
399 {
400         return [audioArray count];
401 }
402
403 - (HBAudio *) objectInAudioArrayAtIndex: (unsigned int) index
404
405 {
406         return [audioArray objectAtIndex: index];
407 }
408
409 - (void) insertObject: (HBAudio *) audioObject inAudioArrayAtIndex: (unsigned int) index;
410
411 {
412         [audioArray insertObject: audioObject atIndex: index];
413         return;
414 }
415
416 - (void) removeObjectFromAudioArrayAtIndex: (unsigned int) index
417
418 {
419         [audioArray removeObjectAtIndex: index];
420         return;
421 }
422
423 @end