OSDN Git Service

d21c6fbbf0f56bce7545d0d6a8283f7664e4e645
[handbrake-jp/handbrake-jp-git.git] / macosx / HBAudio.m
1 //
2 //  HBAudio.m
3 //  HandBrake
4 //
5 //  Created on 2010-08-30.
6 //
7
8 #import "HBAudio.h"
9 #import "HBAudioController.h"
10 #import "hb.h"
11
12 NSString *keyAudioCodecName = @"keyAudioCodecName";
13 NSString *keyAudioMP4 = @"keyAudioMP4";
14 NSString *keyAudioMKV = @"keyAudioMKV";
15 NSString *keyAudioSampleRateName = @"keyAudioSampleRateName";
16 NSString *keyAudioBitrateName = @"keyAudioBitrateName";
17 NSString *keyAudioMustMatchTrack = @"keyAudioMustMatchTrack";
18 NSString *keyAudioMixdownName = @"keyAudioMixdownName";
19 NSString *keyAudioMixdownLimitsToTrackBitRate = @"keyAudioMixdownLimitsToTrackBitRate";
20 NSString *keyAudioMixdownCanBeDefault = @"keyAudioMixdownCanBeDefault";
21
22 NSString *keyAudioCodec = @"codec";
23 NSString *keyAudioMixdown = @"mixdown";
24 NSString *keyAudioSamplerate = @"samplerate";
25 NSString *keyAudioBitrate = @"bitrate";
26
27 static NSMutableArray *masterCodecArray = nil;
28 static NSMutableArray *masterSampleRateArray = nil;
29 static NSMutableArray *masterBitRateArray = nil;
30
31 @interface NSArray (HBAudioSupport)
32 - (NSDictionary *) dictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey;
33 - (NSDictionary *) lastDictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey;
34 @end
35 @implementation NSArray (HBAudioSupport)
36 - (NSDictionary *) dictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey reverse: (BOOL) reverse
37
38 {
39         NSDictionary *retval = nil;
40         NSEnumerator *enumerator = reverse ? [self reverseObjectEnumerator] : [self objectEnumerator];
41         NSDictionary *dict;
42         id aValue;
43         
44         while (nil != (dict = [enumerator nextObject]) && nil == retval) {
45                 if (nil != (aValue = [dict objectForKey: aKey]) && YES == [aValue isEqual: anObject]) {
46                         retval = dict;
47                 }
48         }
49         return retval;
50 }
51 - (NSDictionary *) dictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey
52 {       return [self dictionaryWithObject: anObject matchingKey: aKey reverse: NO];     }
53 - (NSDictionary *) lastDictionaryWithObject: (id) anObject matchingKey: (NSString *) aKey
54 {       return [self dictionaryWithObject: anObject matchingKey: aKey reverse: YES];    }
55
56 @end
57
58 @implementation HBAudio
59
60 #pragma mark -
61 #pragma mark Object Setup
62
63 + (void) load
64
65 {
66         if ([HBAudio class] == self) {
67                 int i;
68                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
69                 NSDictionary *dict;
70                 
71                 masterCodecArray = [[NSMutableArray alloc] init];       //      knowingly leaked
72                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
73                                                                           NSLocalizedString(@"AAC (CoreAudio)", @"AAC (CoreAudio)"), keyAudioCodecName,
74                                                                           [NSNumber numberWithInt: HB_ACODEC_CA_AAC], keyAudioCodec,
75                                                                           [NSNumber numberWithBool: YES], keyAudioMP4,
76                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
77                                                                           [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
78                                                                           nil]];
79                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
80                                                                           NSLocalizedString(@"AAC (faac)", @"AAC (faac)"), keyAudioCodecName,
81                                                                           [NSNumber numberWithInt: HB_ACODEC_FAAC], keyAudioCodec,
82                                                                           [NSNumber numberWithBool: YES], keyAudioMP4,
83                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
84                                                                           [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
85                                                                           nil]];
86                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
87                                                                           NSLocalizedString(@"MP3 (lame)", @"MP3 (lame)"), keyAudioCodecName,
88                                                                           [NSNumber numberWithInt: HB_ACODEC_LAME], keyAudioCodec,
89                                                                           [NSNumber numberWithBool: YES], keyAudioMP4,
90                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
91                                                                           [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
92                                                                           nil]];
93                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
94                                                                           NSLocalizedString(@"AC3 Passthru", @"AC3 Passthru"), keyAudioCodecName,
95                                                                           [NSNumber numberWithInt: HB_ACODEC_AC3_PASS], keyAudioCodec,
96                                                                           [NSNumber numberWithBool: YES], keyAudioMP4,
97                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
98                                                                           [NSNumber numberWithInt: HB_ACODEC_AC3], keyAudioMustMatchTrack,
99                                                                           nil]];
100                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
101                                                                           NSLocalizedString(@"AC3", @"AC3"), keyAudioCodecName,
102                                                                           [NSNumber numberWithInt: HB_ACODEC_AC3], keyAudioCodec,
103                                                                           [NSNumber numberWithBool: YES], keyAudioMP4,
104                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
105                                                                           [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
106                                                                           nil]];
107                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
108                                                                           NSLocalizedString(@"DTS Passthru", @"DTS Passthru"), keyAudioCodecName,
109                                                                           [NSNumber numberWithInt: HB_ACODEC_DCA_PASS], keyAudioCodec,
110                                                                           [NSNumber numberWithBool: NO], keyAudioMP4,
111                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
112                                                                           [NSNumber numberWithInt: HB_ACODEC_DCA], keyAudioMustMatchTrack,
113                                                                           nil]];
114                 [masterCodecArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
115                                                                           NSLocalizedString(@"Vorbis (vorbis)", @"Vorbis (vorbis)"), keyAudioCodecName,
116                                                                           [NSNumber numberWithInt: HB_ACODEC_VORBIS], keyAudioCodec,
117                                                                           [NSNumber numberWithBool: NO], keyAudioMP4,
118                                                                           [NSNumber numberWithBool: YES], keyAudioMKV,
119                                                                           [NSNumber numberWithBool: NO], keyAudioMustMatchTrack,
120                                                                           nil]];
121                 
122                 //      Note that for the Auto value we use 0 for the sample rate because our controller will give back the track's
123                 //      input sample rate when it finds this 0 value as the selected sample rate.  We do this because the input
124                 //      sample rate depends on the track, which means it depends on the title, so cannot be nicely set up here.
125                 masterSampleRateArray = [[NSMutableArray alloc] init];  //      knowingly leaked
126                 [masterSampleRateArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
127                                                                                    NSLocalizedString(@"Auto", @"Auto"), keyAudioSampleRateName,
128                                                                                    [NSNumber numberWithInt: 0], keyAudioSamplerate,
129                                                                                    nil]];
130                 for (i = 0; i < hb_audio_rates_count; i++) {
131                         [masterSampleRateArray addObject: [NSDictionary dictionaryWithObjectsAndKeys:
132                                                                                            [NSString stringWithUTF8String: hb_audio_rates[i].string], keyAudioSampleRateName,
133                                                                                            [NSNumber numberWithInt: hb_audio_rates[i].rate], keyAudioSamplerate,
134                                                                                            nil]];
135                 }
136                 
137                 masterBitRateArray = [[NSMutableArray alloc] init];     // knowingly leaked
138                 for (i = 0; i < hb_audio_bitrates_count; i++) {
139                         int rate = hb_audio_bitrates[i].rate;
140                         dict = [NSDictionary dictionaryWithObjectsAndKeys:
141                                         [NSString stringWithUTF8String: hb_audio_bitrates[i].string], keyAudioBitrateName,
142                                         [NSNumber numberWithInt: rate], keyAudioBitrate,
143                                         nil];
144                         [masterBitRateArray addObject: dict];
145                 }
146                 
147                 [pool release];
148         }
149         return;
150 }
151
152 //      Ensure the list of codecs is accurate
153 //      Update the current value of codec based on the revised list
154 - (void) updateCodecs
155
156 {
157         NSMutableArray *permittedCodecs = [NSMutableArray array];
158         unsigned int count = [masterCodecArray count];
159         NSDictionary *dict;
160         NSString *keyThatAllows = nil;
161
162         //      Determine which key we use to see which codecs are permitted
163         switch ([videoContainerTag intValue]) {
164                 case HB_MUX_MP4:
165                         keyThatAllows = keyAudioMP4;
166                         break;
167                 case HB_MUX_MKV:
168                         keyThatAllows = keyAudioMKV;
169                         break;
170                 default:
171                         keyThatAllows = @"error condition";
172                         break;
173         }
174
175         //      First get a list of the permitted codecs based on the internal rules
176         if (nil != track && YES == [self enabled]) {
177                 BOOL goodToAdd;
178
179                 for (unsigned int i = 0; i < count; i++) {
180                         dict = [masterCodecArray objectAtIndex: i];
181                         
182                         //      First make sure only codecs permitted by the container are here
183                         goodToAdd = [[dict objectForKey: keyThatAllows] boolValue];
184                         
185                         //      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
186                         if (YES == [[dict objectForKey: keyAudioMustMatchTrack] boolValue]) {
187                                 if ([[dict objectForKey: keyAudioMustMatchTrack] intValue] != [[[self track] objectForKey: keyAudioInputCodec] intValue]) {
188                                         goodToAdd = NO;
189                                 }
190                         }
191                         
192                         if (YES == goodToAdd) {
193                                 [permittedCodecs addObject: dict];
194                         }
195                 }
196         }
197         
198         //      Now make sure the permitted list and the actual ones matches
199         [self setCodecs: permittedCodecs];
200
201         //      Ensure our codec is on the list of permitted codecs
202         if (nil == [self codec] || NO == [permittedCodecs containsObject: [self codec]]) {
203                 if (0 < [permittedCodecs count]) {
204                         [self setCodec: [permittedCodecs objectAtIndex: 0]];    //      This should be defaulting to Core Audio
205                 }
206                 else {
207                         [self setCodec: nil];
208                 }
209         }
210
211         return;
212 }
213
214 //      The rules here are provided as-is from the original -[Controller audioTrackPopUpChanged:mixdownToUse:] routine
215 //      with the comments taken from there as well.
216 - (void) updateMixdowns
217
218 {
219         NSMutableArray *retval = [NSMutableArray array];
220         int trackCodec = [[track objectForKey: keyAudioInputCodec] intValue];
221         int codecCodec = [[codec objectForKey: keyAudioCodec] intValue];
222
223         if (HB_ACODEC_AC3 == trackCodec && HB_ACODEC_AC3_PASS == codecCodec) {
224                 [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
225                                                         NSLocalizedString(@"AC3 Passthru", @"AC3 Passthru"), keyAudioMixdownName,
226                                                         [NSNumber numberWithInt: HB_ACODEC_AC3_PASS], keyAudioMixdown,
227                                                         [NSNumber numberWithBool: YES], keyAudioMixdownLimitsToTrackBitRate,
228                                                         [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
229                                                         nil]];
230         }
231         else if (HB_ACODEC_DCA == trackCodec && HB_ACODEC_DCA_PASS == codecCodec) {
232                 [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
233                                                         NSLocalizedString(@"DTS Passthru", @"DTS Passthru"), keyAudioMixdownName,
234                                                         [NSNumber numberWithInt: HB_ACODEC_DCA_PASS], keyAudioMixdown,
235                                                         [NSNumber numberWithBool: YES], keyAudioMixdownLimitsToTrackBitRate,
236                                                         [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
237                                                         nil]];
238         }
239         else {
240                 int audioCodecsSupport6Ch = (trackCodec && HB_ACODEC_LAME != codecCodec);
241                 int channelLayout = [[track objectForKey: keyAudioInputChannelLayout] intValue];
242                 int layout = channelLayout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
243                 
244                 /* add a mono option? */
245                 [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
246                                                         [NSString stringWithUTF8String: hb_audio_mixdowns[0].human_readable_name], keyAudioMixdownName,
247                                                         [NSNumber numberWithInt: hb_audio_mixdowns[0].amixdown], keyAudioMixdown,
248                                                         [NSNumber numberWithBool: NO], keyAudioMixdownLimitsToTrackBitRate,
249                                                         [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
250                                                         nil]];
251                 
252                 /* offer stereo if we have a stereo-or-better source */
253                 if (layout >= HB_INPUT_CH_LAYOUT_STEREO) {
254                         [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
255                                                                 [NSString stringWithUTF8String: hb_audio_mixdowns[1].human_readable_name], keyAudioMixdownName,
256                                                                 [NSNumber numberWithInt: hb_audio_mixdowns[1].amixdown], keyAudioMixdown,
257                                                                 [NSNumber numberWithBool: NO], keyAudioMixdownLimitsToTrackBitRate,
258                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
259                                                                 nil]];
260                 }
261         
262                 /* do we want to add a dolby surround (DPL1) option? */
263                 if (HB_INPUT_CH_LAYOUT_3F1R == layout || HB_INPUT_CH_LAYOUT_3F2R == layout || HB_INPUT_CH_LAYOUT_DOLBY == layout) {
264                         [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
265                                                                 [NSString stringWithUTF8String: hb_audio_mixdowns[2].human_readable_name], keyAudioMixdownName,
266                                                                 [NSNumber numberWithInt: hb_audio_mixdowns[2].amixdown], keyAudioMixdown,
267                                                                 [NSNumber numberWithBool: NO], keyAudioMixdownLimitsToTrackBitRate,
268                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
269                                                                 nil]];
270                 }
271                 
272                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
273                 if (HB_INPUT_CH_LAYOUT_3F2R == layout) {
274                         [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
275                                                                 [NSString stringWithUTF8String: hb_audio_mixdowns[3].human_readable_name], keyAudioMixdownName,
276                                                                 [NSNumber numberWithInt: hb_audio_mixdowns[3].amixdown], keyAudioMixdown,
277                                                                 [NSNumber numberWithBool: NO], keyAudioMixdownLimitsToTrackBitRate,
278                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
279                                                                 nil]];
280                 }
281                 
282                 /* do we want to add a 6-channel discrete option? */
283                 if (1 == audioCodecsSupport6Ch && HB_INPUT_CH_LAYOUT_3F2R == layout && (channelLayout & HB_INPUT_CH_LAYOUT_HAS_LFE)) {
284                         [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
285                                                                 [NSString stringWithUTF8String: hb_audio_mixdowns[4].human_readable_name], keyAudioMixdownName,
286                                                                 [NSNumber numberWithInt: hb_audio_mixdowns[4].amixdown], keyAudioMixdown,
287                                                                 [NSNumber numberWithBool: NO], keyAudioMixdownLimitsToTrackBitRate,
288                                                                 [NSNumber numberWithBool: (HB_ACODEC_AC3 == codecCodec) ? YES : NO], keyAudioMixdownCanBeDefault,
289                                                                 nil]];
290                 }
291                 
292                 //      based on the fact that we are in an else section where the ifs before hand would have detected the following two
293                 //      situations, the following code will never add anything to the returned array.  I am leaving this in place for
294                 //      historical reasons.
295                 /* do we want to add an AC-3 passthrough option? */
296                 if (HB_ACODEC_AC3 == trackCodec && HB_ACODEC_AC3_PASS == codecCodec) {
297                         [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
298                                                                 [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name], keyAudioMixdownName,
299                                                                 [NSNumber numberWithInt: HB_ACODEC_AC3_PASS], keyAudioMixdown,
300                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownLimitsToTrackBitRate,
301                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
302                                                                 nil]];
303                 }
304                         
305                 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
306                 if (HB_ACODEC_DCA == trackCodec && HB_ACODEC_DCA_PASS == codecCodec) {
307                         [retval addObject: [NSDictionary dictionaryWithObjectsAndKeys:
308                                                                 [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name], keyAudioMixdownName,
309                                                                 [NSNumber numberWithInt: HB_ACODEC_DCA_PASS], keyAudioMixdown,
310                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownLimitsToTrackBitRate,
311                                                                 [NSNumber numberWithBool: YES], keyAudioMixdownCanBeDefault,
312                                                                 nil]];
313                 }
314         }
315
316         if (NO == [self enabled]) {
317                 retval = nil;
318         }
319
320         //      Now make sure the permitted list and the actual ones matches
321         [self setMixdowns: retval];
322         
323         //      Ensure our mixdown is on the list of permitted ones
324         if (YES == [[NSUserDefaults standardUserDefaults] boolForKey: @"CodecDefaultsMixdown"] ||
325                 nil == [self mixdown] || NO == [retval containsObject: [self mixdown]]) {
326                 [self setMixdown: [retval lastDictionaryWithObject: [NSNumber numberWithBool: YES] matchingKey: keyAudioMixdownCanBeDefault]];
327         }
328         
329         return;
330 }
331
332 - (void) updateBitRates: (BOOL) shouldSetDefault
333
334 {
335         NSMutableArray *permittedBitRates = [NSMutableArray array];
336         int count;
337         NSDictionary *dict;
338         
339         count = [masterBitRateArray count];
340         int minBitRate;
341         int maxBitRate;
342         NSString *defaultBitRate;
343         int currentBitRate;
344         int trackInputBitRate = [[[self track] objectForKey: keyAudioInputBitrate] intValue];
345         BOOL limitsToTrackInputBitRate = [[[self mixdown] objectForKey: keyAudioMixdownLimitsToTrackBitRate] boolValue];
346         BOOL shouldAdd;
347         int theSampleRate = [[[self sampleRate] objectForKey: keyAudioSamplerate] intValue];
348                 
349         if (0 == theSampleRate) {       //      this means Auto
350                 theSampleRate = [[[self track] objectForKey: keyAudioInputSampleRate] intValue];
351                 }
352
353         int ourCodec = [[codec objectForKey: keyAudioCodec] intValue];
354         int ourMixdown = [[[self mixdown] objectForKey: keyAudioMixdown] intValue];
355         hb_get_audio_bitrate_limits(ourCodec, theSampleRate, ourMixdown, &minBitRate, &maxBitRate);
356         int theDefaultBitRate = hb_get_default_audio_bitrate(ourCodec, theSampleRate, ourMixdown);
357         defaultBitRate = [NSString stringWithFormat: @"%d", theDefaultBitRate];
358
359         for (unsigned int i = 0; i < count; i++) {
360                 dict = [masterBitRateArray objectAtIndex: i];
361                 currentBitRate = [[dict objectForKey: keyAudioBitrate] intValue];
362                 
363                 //      First ensure the bitrate falls within range of the codec
364                 shouldAdd = (currentBitRate >= minBitRate && currentBitRate <= maxBitRate);
365                 
366                 //      Now make sure the mixdown is not limiting us to the track input bitrate
367                 if (YES == shouldAdd && YES == limitsToTrackInputBitRate) {
368                         if (currentBitRate != trackInputBitRate) {
369                                 shouldAdd = NO;
370                         }
371                 }
372                                 
373                 if (YES == shouldAdd) {
374                         [permittedBitRates addObject: dict];
375                 }
376         }
377         
378         //      There is a situation where we have a mixdown requirement to match the track input bit rate,
379         //      but it does not fall into the range the codec supports.  Therefore, we force it here.
380         if (YES == limitsToTrackInputBitRate && 0 == [permittedBitRates count]) {
381                 NSDictionary *missingBitRate = [masterBitRateArray dictionaryWithObject: [NSNumber numberWithInt: trackInputBitRate] matchingKey: keyAudioBitrate];
382                 if (nil == missingBitRate) {
383                         //      We are in an even worse situation where the requested bit rate does not even exist in the underlying
384                         //      library of supported bitrates.  Of course since this value is ignored we can freely make a bogus one
385                         //      for the UI just to make the user a little more aware.
386                         missingBitRate = [NSDictionary dictionaryWithObjectsAndKeys:
387                                                           [NSString stringWithFormat: @"%d", trackInputBitRate], keyAudioBitrateName,
388                                                           [NSNumber numberWithInt: trackInputBitRate], keyAudioBitrate,
389                                                           nil];
390                         }
391                 [permittedBitRates addObject: missingBitRate];
392         }
393
394         if (NO == [self enabled]) {
395                 permittedBitRates = nil;
396         }
397
398         //      Make sure we are updated with the permitted list
399         [self setBitRates: permittedBitRates];
400
401         //      Select the proper one
402         if (YES == shouldSetDefault) {
403                 [self setBitRateFromName: defaultBitRate];
404                 }
405
406         if (nil == [self bitRate] || NO == [permittedBitRates containsObject: [self bitRate]]) {
407                 [self setBitRate: [permittedBitRates lastObject]];
408         }
409         
410         return;
411 }
412
413 - (id) init
414
415 {
416         if (self = [super init]) {
417                 [self addObserver: self forKeyPath: @"videoContainerTag" options: 0 context: NULL];
418                 [self addObserver: self forKeyPath: @"track" options: NSKeyValueObservingOptionOld context: NULL];
419                 [self addObserver: self forKeyPath: @"codec" options: 0 context: NULL];
420                 [self addObserver: self forKeyPath: @"mixdown" options: 0 context: NULL];
421                 [self addObserver: self forKeyPath: @"sampleRate" options: 0 context: NULL];
422                 }
423         return self;
424 }
425
426 #pragma mark -
427 #pragma mark Accessors
428
429 @synthesize track;
430 @synthesize codec;
431 @synthesize mixdown;
432 @synthesize sampleRate;
433 @synthesize bitRate;
434 @synthesize drc;
435 @synthesize videoContainerTag;
436 @synthesize controller;
437
438 @synthesize codecs;
439 @synthesize mixdowns;
440 @synthesize bitRates;
441
442 - (NSArray *) tracks    {       return [controller masterTrackArray];   }
443
444 - (NSArray *) sampleRates       {       return masterSampleRateArray;   }
445
446 - (void) dealloc
447
448 {
449         [self removeObserver: self forKeyPath: @"videoContainerTag"];
450         [self removeObserver: self forKeyPath: @"track"];
451         [self removeObserver: self forKeyPath: @"codec"];
452         [self removeObserver: self forKeyPath: @"mixdown"];
453         [self removeObserver: self forKeyPath: @"sampleRate"];
454         [self setTrack: nil];
455         [self setCodec: nil];
456         [self setMixdown: nil];
457         [self setSampleRate: nil];
458         [self setBitRate: nil];
459         [self setDrc: nil];
460         [self setVideoContainerTag: nil];
461         [self setCodecs: nil];
462         [self setMixdowns: nil];
463         [self setBitRates: nil];
464         [super dealloc];
465         return;
466 }
467
468 #pragma mark -
469 #pragma mark KVO
470
471 - (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context
472
473 {
474         if (YES == [keyPath isEqualToString: @"videoContainerTag"]) {
475                 [self updateCodecs];
476                 }
477         else if (YES == [keyPath isEqualToString: @"track"]) {
478                 if (nil != [self track]) {
479                         [self updateCodecs];
480                         if (YES == [self enabled]) {
481                                 [self setSampleRate: [[self sampleRates] objectAtIndex: 0]];    // default to Auto
482                                 }
483                         if (YES == [[controller noneTrack] isEqual: [change objectForKey: NSKeyValueChangeOldKey]]) {
484                                 [controller switchingTrackFromNone: self];
485                                 }
486                         if (YES == [[controller noneTrack] isEqual: [self track]]) {
487                                 [controller settingTrackToNone: self];
488                                 }
489                         }
490                 }
491         else if (YES == [keyPath isEqualToString: @"codec"]) {
492                 [self updateMixdowns];
493                 [self updateBitRates: YES];
494                 }
495         else if (YES == [keyPath isEqualToString: @"mixdown"]) {
496                 [self updateBitRates: YES];
497                 [[NSNotificationCenter defaultCenter] postNotificationName: HBMixdownChangedNotification object: self];
498                 }
499         else if (YES == [keyPath isEqualToString: @"sampleRate"]) {
500                 [self updateBitRates: NO];
501                 }
502         return;
503 }
504
505 #pragma mark -
506 #pragma mark Special Setters
507
508 - (void) setTrackFromIndex: (int) aValue
509
510 {
511         [self setTrack: [[self tracks] dictionaryWithObject: [NSNumber numberWithInt: aValue] matchingKey: keyAudioTrackIndex]];
512         return;
513 }
514
515 //      This returns whether it is able to set the actual codec desired.
516 - (BOOL) setCodecFromName: (NSString *) aValue
517
518 {
519         NSDictionary *dict = [[self codecs] dictionaryWithObject: aValue matchingKey: keyAudioCodecName];
520         
521         if (nil != dict) {
522                 [self setCodec: dict];
523         }
524         return (nil != dict);
525 }
526
527 - (void) setMixdownFromName: (NSString *) aValue
528
529 {
530         NSDictionary *dict = [[self mixdowns] dictionaryWithObject: aValue matchingKey: keyAudioMixdownName];
531
532         if (nil != dict) {
533                 [self setMixdown: dict];
534         }
535         return;
536 }
537
538 - (void) setSampleRateFromName: (NSString *) aValue
539
540 {
541         NSDictionary *dict = [[self sampleRates] dictionaryWithObject: aValue matchingKey: keyAudioSampleRateName];
542         
543         if (nil != dict) {
544                 [self setSampleRate: dict];
545         }
546         return;
547 }
548
549 - (void) setBitRateFromName: (NSString *) aValue
550
551 {
552         NSDictionary *dict = [[self bitRates] dictionaryWithObject: aValue matchingKey: keyAudioBitrateName];
553         
554         if (nil != dict) {
555                 [self setBitRate: dict];
556         }
557         return;
558 }
559
560
561 #pragma mark -
562 #pragma mark Validation
563
564 //      Because we have indicated that the binding for the drc validates immediately we can implement the
565 //      key value binding method to ensure the drc stays in our accepted range.
566 - (BOOL) validateDrc: (id *) ioValue error: (NSError *) outError
567
568 {
569         BOOL retval = YES;
570         
571         if (nil != *ioValue) {
572                 if (0.0 < [*ioValue floatValue] && 1.0 > [*ioValue floatValue]) {
573                         *ioValue = [NSNumber numberWithFloat: 1.0];
574                 }
575         }
576         
577         return retval;
578 }
579
580 #pragma mark -
581 #pragma mark Bindings Support
582
583 - (BOOL) enabled
584
585 {
586         return (nil != track) ? (NO == [track isEqual: [controller noneTrack]]) : NO;
587 }
588
589 - (BOOL) mixdownEnabled
590
591 {
592         BOOL retval = [self enabled];
593
594         if (YES == retval) {
595                 int myMixdown = [[[self mixdown] objectForKey: keyAudioMixdown] intValue];
596                 if (HB_ACODEC_AC3_PASS == myMixdown || HB_ACODEC_DCA_PASS == myMixdown) {
597                         retval = NO;
598                 }
599         }
600         return retval;
601 }
602
603 - (BOOL) AC3Enabled
604
605 {
606         BOOL retval = [self enabled];
607         
608         if (YES == retval) {
609                 int myTrackCodec = [[[self track] objectForKey: keyAudioInputCodec] intValue];
610                 int myCodecCodec = [[[self codec] objectForKey: keyAudioCodec] intValue];
611                 if (HB_ACODEC_AC3 != myTrackCodec || HB_ACODEC_AC3_PASS == myCodecCodec) {
612                         retval = NO;
613                 }
614         }
615         return retval;
616 }
617
618 + (NSSet *) keyPathsForValuesAffectingValueForKey: (NSString *) key
619
620 {
621         NSSet *retval = nil;
622
623         if (YES == [key isEqualToString: @"enabled"]) {
624                 retval = [NSSet setWithObjects: @"track", nil];
625                 }
626         else if (YES == [key isEqualToString: @"AC3Enabled"]) {
627                 retval = [NSSet setWithObjects: @"track", @"codec", nil];
628         }
629         else if (YES == [key isEqualToString: @"mixdownEnabled"]) {
630                 retval = [NSSet setWithObjects: @"track", @"mixdown", nil];
631                 }
632         return retval;
633 }
634
635 @end