OSDN Git Service

ccd6363c71ff1269f7792e88d79fe51f05dd97b4
[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 //      This routine takes the preset and will return the value for the key AudioList
197 //      if it exists, otherwise it creates an array from the data in the present.
198 - (NSArray *) _presetAudioArrayFromPreset: (NSMutableDictionary *) aPreset
199
200 {
201         NSArray *retval = [aPreset objectForKey: @"AudioList"];
202
203         if (nil == retval) {
204                 int maximumNumberOfAllowedAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
205                 NSString *base;
206                 NSMutableArray *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                 retval = whatToUse;
220         }
221         return retval;
222 }
223
224 //      This uses the templateAudioArray from the preset to create the audios for the specified trackIndex
225 - (void) _processPresetAudioArray: (NSArray *) templateAudioArray forTrack: (unsigned int) trackIndex andType: (int) aType
226
227 {
228         NSEnumerator *enumerator = [templateAudioArray objectEnumerator];
229         NSDictionary *dict;
230         NSString *key;
231         
232         while (nil != (dict = [enumerator nextObject])) {
233                 HBAudio *newAudio = [[HBAudio alloc] init];
234                 [newAudio setController: self];
235                 [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]];
236                 [newAudio setVideoContainerTag: [self videoContainerTag]];
237                 [newAudio setTrackFromIndex: trackIndex];
238                 key = [dict objectForKey: @"AudioEncoder"];
239                 if (0 == aType &&
240                         YES == [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] &&
241                         YES == [key isEqualToString: @"AAC (faac)"]
242                         ) {
243                         key = @"AAC (CoreAudio)";
244                 }
245                 //      If our preset wants us to support a codec that the track does not support, instead
246                 //      of changing the codec we remove the audio instead.
247                 if (YES == [newAudio setCodecFromName: key]) {
248                         [newAudio setMixdownFromName: [dict objectForKey: @"AudioMixdown"]];
249                         [newAudio setSampleRateFromName: [dict objectForKey: @"AudioSamplerate"]];
250                         [newAudio setBitRateFromName: [dict objectForKey: @"AudioBitrate"]];
251                         [newAudio setDrc: [dict objectForKey: @"AudioTrackDRCSlider"]];
252                 }
253                 else {
254                         [self removeObjectFromAudioArrayAtIndex: [self countOfAudioArray] - 1];
255                 }
256                 [newAudio release];
257         }
258         return;
259 }
260
261 - (void) addTracksFromPreset: (NSMutableDictionary *) aPreset
262
263 {
264         id whatToUse = [self _presetAudioArrayFromPreset: aPreset];
265
266         //      Reinitialize the configured list of audio tracks
267         [audioArray release];
268         audioArray = [[NSMutableArray alloc] init];
269         
270         [self _processPresetAudioArray: whatToUse forTrack: 1 andType: [[aPreset objectForKey: @"Type"] intValue]];
271
272         [self switchingTrackFromNone: nil];     // see if we need to add one to the list
273
274         return;
275 }
276
277 - (void) addAllTracksFromPreset: (NSMutableDictionary *) aPreset
278
279 {
280         id whatToUse = [self _presetAudioArrayFromPreset: aPreset];
281         
282         //      Reinitialize the configured list of audio tracks
283         [audioArray release];
284         audioArray = [[NSMutableArray alloc] init];
285         
286         for (unsigned int i = 1; i < [masterTrackArray count]; i++) {
287                 [self _processPresetAudioArray: whatToUse forTrack: i andType: [[aPreset objectForKey: @"Type"] intValue]];
288         }
289         
290         [self switchingTrackFromNone: nil];     // see if we need to add one to the list
291         
292         return;
293         return;
294 }
295
296 - (BOOL) anyCodecMatches: (int) aCodecValue
297
298 {
299         BOOL retval = NO;
300         unsigned int audioArrayCount = [self countOfAudioArray];
301         for (unsigned int i = 0; i < audioArrayCount && NO == retval; i++) {
302                 HBAudio *anAudio = [self objectInAudioArrayAtIndex: i];
303         if (YES == [anAudio enabled] && aCodecValue == [[[anAudio codec] objectForKey: keyAudioCodec] intValue]) {
304                         retval = YES;
305                 }
306         }
307         return retval;
308 }
309
310 - (void) addNewAudioTrack
311
312 {
313         HBAudio *newAudio = [[HBAudio alloc] init];
314         [newAudio setController: self];
315         [self insertObject: newAudio inAudioArrayAtIndex: [self countOfAudioArray]];
316         [newAudio setVideoContainerTag: [self videoContainerTag]];
317         [newAudio setTrack: noneTrack];
318         [newAudio setDrc: [NSNumber numberWithFloat: 0.0]];
319         [newAudio release];     
320         return;
321 }
322
323 #pragma mark -
324 #pragma mark Notification Handling
325
326 - (void) settingTrackToNone: (HBAudio *) newNoneTrack
327
328 {
329         //      If this is not the last track in the array we need to remove it.  We then need to see if a new
330         //      one needs to be added (in the case when we were at maximum count and this switching makes it
331         //      so we are no longer at maximum.
332         unsigned int index = [audioArray indexOfObject: newNoneTrack];
333
334         if (NSNotFound != index && index < [self countOfAudioArray] - 1) {
335                 [self removeObjectFromAudioArrayAtIndex: index];
336         }
337         [self switchingTrackFromNone: nil];     // see if we need to add one to the list
338         return;
339 }
340
341 - (void) switchingTrackFromNone: (HBAudio *) noLongerNoneTrack
342
343 {
344         int count = [self countOfAudioArray];
345         BOOL needToAdd = NO;
346         int maximumNumberOfAllowedAudioTracks = [HBController maximumNumberOfAllowedAudioTracks];
347
348         //      If there is no last track that is None and we are less than our maximum number of permitted tracks, we add one.
349         if (count < maximumNumberOfAllowedAudioTracks) {
350                 if (0 < count) {
351                         HBAudio *lastAudio = [self objectInAudioArrayAtIndex: count - 1];
352                         if (YES == [lastAudio enabled]) {
353                                 needToAdd = YES;
354                         }
355                 }
356                 else {
357                         needToAdd = YES;
358                 }
359         }
360
361         if (YES == needToAdd) {
362                 [self addNewAudioTrack];
363         }
364         return;
365 }
366
367 //      This gets called whenever the video container changes.
368 - (void) containerChanged: (NSNotification *) aNotification
369
370 {
371         NSDictionary *notDict = [aNotification userInfo];
372
373         [self setVideoContainerTag: [notDict objectForKey: keyContainerTag]];
374
375         //      Update each of the instances because this value influences possible settings.
376         NSEnumerator *enumerator = [audioArray objectEnumerator];
377         HBAudio *audioObject;
378
379         while (nil != (audioObject = [enumerator nextObject])) {
380                 [audioObject setVideoContainerTag: [self videoContainerTag]];
381         }
382         return;
383 }
384
385 - (void) titleChanged: (NSNotification *) aNotification
386
387 {
388         NSDictionary *notDict = [aNotification userInfo];
389         NSData *theData = [notDict objectForKey: keyTitleTag];
390         hb_title_t *title = NULL;
391
392         [theData getBytes: &title length: sizeof(title)];
393         if (title) {
394                 hb_audio_config_t *audio;
395                 hb_list_t *list = title->list_audio;
396                 int i, count = hb_list_count(list);
397
398                 //      Reinitialize the master list of available audio tracks from this title
399                 [masterTrackArray release];
400                 masterTrackArray = [[NSMutableArray alloc] init];
401                 [noneTrack release];
402                 noneTrack = [[NSDictionary dictionaryWithObjectsAndKeys:
403                                          [NSNumber numberWithInt: 0], keyAudioTrackIndex,
404                                          NSLocalizedString(@"None", @"None"), keyAudioTrackName,
405                                          [NSNumber numberWithInt: 0], keyAudioInputCodec,
406                                                          nil] retain];
407                 [masterTrackArray addObject: noneTrack];
408                 for (i = 0; i < count; i++) {
409                         audio = (hb_audio_config_t *) hb_list_audio_config_item(list, i);
410                         [masterTrackArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
411                                                                                   [NSNumber numberWithInt: i + 1], keyAudioTrackIndex,
412                                                                                   [NSString stringWithFormat: @"%d: %s", i, audio->lang.description], keyAudioTrackName,
413                                                                                   [NSNumber numberWithInt: audio->in.bitrate / 1000], keyAudioInputBitrate,
414                                                                                   [NSNumber numberWithInt: audio->in.samplerate], keyAudioInputSampleRate,
415                                                                                   [NSNumber numberWithInt: audio->in.codec], keyAudioInputCodec,
416                                                                                   [NSNumber numberWithInt: audio->in.channel_layout], keyAudioInputChannelLayout,
417                                                                                   nil]];
418                 }
419         }
420
421         //      Reinitialize the configured list of audio tracks
422         [audioArray release];
423         audioArray = [[NSMutableArray alloc] init];
424
425         return;
426 }
427
428 #pragma mark -
429 #pragma mark KVC
430
431 - (unsigned int) countOfAudioArray
432
433 {
434         return [audioArray count];
435 }
436
437 - (HBAudio *) objectInAudioArrayAtIndex: (unsigned int) index
438
439 {
440         return [audioArray objectAtIndex: index];
441 }
442
443 - (void) insertObject: (HBAudio *) audioObject inAudioArrayAtIndex: (unsigned int) index;
444
445 {
446         [audioArray insertObject: audioObject atIndex: index];
447         return;
448 }
449
450 - (void) removeObjectFromAudioArrayAtIndex: (unsigned int) index
451
452 {
453         [audioArray removeObjectAtIndex: index];
454         return;
455 }
456
457 @end