Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Classes/AudioSettingsPanelController.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 197 - (show annotations) (download)
Sun Jul 26 10:33:50 2020 UTC (3 years, 10 months ago) by toshinagata1964
File size: 28458 byte(s)
Selecting Audio Device as an input caused crash. Fixed.
1 //
2 // AudioSettingsPanelController.m
3 // Alchemusica
4 //
5 // Created by Toshi Nagata on 10/06/13.
6 // Copyright 2010-2016 Toshi Nagata. All rights reserved.
7 //
8 /*
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation version 2 of the License.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17 */
18
19 #import "AudioSettingsPanelController.h"
20 #import "AudioSettingsPrefPanelController.h"
21 #import "NSWindowControllerAdditions.h"
22 #import "AudioEffectPanelController.h"
23 #import "AUViewWindowController.h"
24 #import "MDHeaders.h"
25
26 /* Tags for controls */
27 /* ...Base + 0: output, +1 ~ +8: bus 1 ~ 8 */
28 enum {
29 kDevicePopUpBase = 100,
30 kPanKnobBase = 200,
31 kVolumeSliderBase = 300,
32 kLeftLevelIndicatorBase = 400,
33 kRightLevelIndicatorBase = 500,
34 kCustomViewButtonBase = 600,
35 kEffectButtonBase = 700,
36 kBusIndexTextBase = 800
37 };
38 #define kOutputTagOffset 50
39
40 @implementation AudioSettingsPanelController
41
42 static AudioSettingsPanelController *sharedAudioSettingsPanelController;
43
44 + (void)openAudioSettingsPanel
45 {
46 if (sharedAudioSettingsPanelController == nil) {
47 sharedAudioSettingsPanelController = [[AudioSettingsPanelController alloc] initWithWindowNibName: @"AudioSettingsPanel"];
48 }
49 [[sharedAudioSettingsPanelController window] makeKeyAndOrderFront: nil];
50 [sharedAudioSettingsPanelController updateDisplay];
51 [sharedAudioSettingsPanelController timerCallback:nil];
52 }
53
54 + (AudioSettingsPanelController *)sharedAudioSettingsPanelController
55 {
56 if (sharedAudioSettingsPanelController == nil)
57 [AudioSettingsPanelController openAudioSettingsPanel];
58 return sharedAudioSettingsPanelController;
59 }
60
61 /* For debug */
62 static void printCFdata(CFTypeRef ref, int nestLevel)
63 {
64 int i, count, typeid;
65 const void **keys;
66 typeid = (int)CFGetTypeID(ref);
67 if (typeid == CFNumberGetTypeID()) {
68 double dval;
69 CFNumberGetValue(ref, kCFNumberDoubleType, &dval);
70 fprintf(stderr, "%f\n", dval);
71 } else if (typeid == CFStringGetTypeID()) {
72 fprintf(stderr, "%s\n", CFStringGetCStringPtr(ref, kCFStringEncodingUTF8));
73 } else if (typeid == CFDataGetTypeID()) {
74 UInt64 len;
75 len = CFDataGetLength(ref);
76 fprintf(stderr, "data (%p) length (%llu)\n", (void *)ref, len);
77 } else if (typeid == CFArrayGetTypeID()) {
78 count = (int)CFArrayGetCount(ref);
79 fprintf(stderr, "array {\n");
80 for (i = 0; i < count; i++) {
81 CFTypeRef eref = CFArrayGetValueAtIndex(ref, i);
82 fprintf(stderr, "%*s%d: ", nestLevel + 2, "", i);
83 printCFdata(eref, nestLevel + 2);
84 }
85 fprintf(stderr, "%*s}\n", nestLevel, "");
86 } else if (typeid == CFDictionaryGetTypeID()) {
87 count = (int)CFDictionaryGetCount(ref);
88 keys = (const void **)malloc(sizeof(keys[0]) * count);
89 if (keys == NULL) {
90 fprintf(stderr, "*** memory allocation error (count = %d)\n", count);
91 return;
92 }
93 CFDictionaryGetKeysAndValues(ref, keys, NULL);
94 fprintf(stderr, "dictionary {\n");
95 for (i = 0; i < count; i++) {
96 CFTypeRef vref = CFDictionaryGetValue(ref, keys[i]);
97 fprintf(stderr, "%*s[%s]: ", nestLevel + 2, "", CFStringGetCStringPtr((CFTypeRef)keys[i], kCFStringEncodingUTF8));
98 printCFdata(vref, nestLevel + 2);
99 }
100 fprintf(stderr, "%*s}\n", nestLevel, "");
101 } else {
102 fprintf(stderr, "value of type %d (%p)\n", (int)CFGetTypeID(ref), (void *)ref);
103 }
104 }
105
106 - (id)exportAudioSettingsToPropertyList
107 {
108 int idx, isInput, deviceIndex, status, shouldSaveInternal;
109 MDAudioDeviceInfo *dp;
110 MDAudioMusicDeviceInfo *mp;
111 MDAudioIOStreamInfo *iop;
112 CFPropertyListRef pref;
113 UInt32 prefSize = sizeof(pref);
114 NSMutableDictionary *dic;
115
116 dic = [NSMutableDictionary dictionary];
117 for (idx = 0; idx < kMDAudioNumberOfStreams; idx++) {
118 NSMutableDictionary *dic2;
119 const char *name;
120 char key[40];
121 if (idx >= kMDAudioFirstIndexForOutputStream)
122 isInput = 0;
123 else
124 isInput = 1;
125 if (isInput) {
126 snprintf(key, sizeof key, "bus%d", idx + 1);
127 } else {
128 snprintf(key, sizeof key, "output%d", (idx - kMDAudioFirstIndexForOutputStream) + 1);
129 }
130 iop = MDAudioGetIOStreamInfoAtIndex(idx);
131 deviceIndex = iop->deviceIndex;
132 if (deviceIndex < 0) {
133 /* No device */
134 continue;
135 } else if (deviceIndex < kMDAudioMusicDeviceIndexOffset) {
136 dp = MDAudioDeviceInfoAtIndex(deviceIndex, isInput);
137 name = dp->name;
138 } else {
139 mp = MDAudioMusicDeviceInfoAtIndex(deviceIndex - kMDAudioMusicDeviceIndexOffset);
140 name = mp->name;
141 }
142 shouldSaveInternal = [AudioSettingsPrefPanelController shouldSaveInternalForDeviceName:name];
143 dic2 = [NSMutableDictionary dictionary];
144 [dic2 setObject:[NSString stringWithUTF8String:name] forKey:@"deviceName"];
145 [dic setObject:dic2 forKey:[NSString stringWithUTF8String:key]];
146 if (shouldSaveInternal) {
147 pref = NULL;
148 status = AudioUnitGetProperty(iop->unit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, (AudioUnitElement)0, &pref, &prefSize);
149 if (status == 0) {
150 if (CFGetTypeID(pref) == CFDictionaryGetTypeID()) {
151 [dic2 setObject:pref forKey:@"classInfo"];
152 }
153 CFRelease(pref);
154 }
155 }
156 [dic2 setObject:[NSNumber numberWithFloat:iop->pan] forKey:@"pan"];
157 [dic2 setObject:[NSNumber numberWithFloat:iop->volume] forKey:@"volume"];
158 if (iop->nchains > 0) {
159 int ni;
160 NSMutableArray *ary1, *ary2;
161 ary1 = [NSMutableArray array];
162 [dic2 setObject:ary1 forKey:@"effectChains"];
163 for (ni = 0; ni < iop->nchains; ni++) {
164 MDAudioEffectChain *cp = &(iop->chains[ni]);
165 ary2 = [NSMutableArray array];
166 [ary1 addObject:ary2];
167 if (cp->neffects > 0) {
168 int ei;
169 for (ei = 0; ei < cp->neffects; ei++) {
170 CFPropertyListRef ppref;
171 NSMutableDictionary *dic3;
172 MDAudioEffect *ep = &(cp->effects[ei]);
173 dic3 = [NSMutableDictionary dictionary];
174 [ary2 addObject:dic3];
175 [dic3 setObject:[NSString stringWithUTF8String:ep->name] forKey:@"effectName"];
176 shouldSaveInternal = [AudioSettingsPrefPanelController shouldSaveInternalForDeviceName:ep->name];
177 if (shouldSaveInternal) {
178 ppref = NULL;
179 status = AudioUnitGetProperty(ep->unit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, (AudioUnitElement)0, &ppref, &prefSize);
180 if (status == 0) {
181 [dic3 setObject:ppref forKey:@"classInfo"];
182 CFRelease(ppref);
183 }
184 }
185 }
186 }
187 }
188 }
189 }
190 // printCFdata(dic, 0); /* for debug */
191 return dic;
192 }
193
194 - (void)exportAudioSettings
195 {
196 NSData *data;
197 id plist;
198 plist = [self exportAudioSettingsToPropertyList];
199 if (plist == nil)
200 return;
201 data = [NSPropertyListSerialization dataWithPropertyList:plist format:NSPropertyListXMLFormat_v1_0 options:0 error: NULL];
202 if (data) {
203 NSSavePanel *panel = [NSSavePanel savePanel];
204 NSString *filename, *foldername;
205 NSURL *url;
206 [panel setNameFieldStringValue:@"audio_settings.xml"];
207 if ([panel runModal] == NSFileHandlingPanelOKButton) {
208 foldername = [[[panel directoryURL] path] stringByAbbreviatingWithTildeInPath];
209 url = [panel URL];
210 filename = [[url path] lastPathComponent];
211 [data writeToURL:url atomically:YES];
212 }
213 }
214 }
215
216 - (void)importAudioSettingsFromPropertyList:(id)plist
217 {
218 int map_current2plist[kMDAudioNumberOfStreams];
219 int map_plist2current[kMDAudioNumberOfStreams];
220 id plist4bus[kMDAudioNumberOfStreams];
221 int i, j, k, isInput, deviceIndex, status, shouldLoadInternal;
222 UInt32 psize = sizeof(id);
223 MDAudioDeviceInfo *ap;
224 MDAudioMusicDeviceInfo *mp;
225 MDAudioIOStreamInfo *iop;
226 id classInfo, num;
227 for (i = 0; i < kMDAudioNumberOfStreams; i++) {
228 char key[40];
229 if (i >= kMDAudioFirstIndexForOutputStream)
230 snprintf(key, sizeof key, "output%d", (i - kMDAudioFirstIndexForOutputStream) + 1);
231 else
232 snprintf(key, sizeof key, "bus%d", i + 1);
233 plist4bus[i] = [plist valueForKey:[NSString stringWithUTF8String:key]];
234 map_current2plist[i] = -1;
235 map_plist2current[i] = -1;
236 }
237 for (i = 0; i < kMDAudioNumberOfStreams; i++) {
238 const char *namep;
239 if (plist4bus[i] == nil)
240 continue;
241 namep = [[plist4bus[i] valueForKey:@"deviceName"] UTF8String];
242 deviceIndex = -1;
243 if (i >= kMDAudioFirstIndexForOutputStream) {
244 /* Output stream */
245 isInput = 0;
246 for (j = 0; (ap = MDAudioDeviceInfoAtIndex(j, isInput)) != NULL; j++) {
247 if (strcmp(ap->name, namep) == 0) {
248 deviceIndex = j;
249 break;
250 }
251 }
252 if (deviceIndex >= 0) {
253 iop = MDAudioGetIOStreamInfoAtIndex(i);
254 if (iop->deviceIndex != deviceIndex) {
255 /* Change device */
256 MDAudioSelectIOStreamDevice(i, deviceIndex);
257 }
258 /* Set classInfo if present */
259 shouldLoadInternal = [AudioSettingsPrefPanelController shouldSaveInternalForDeviceName:ap->name];
260 classInfo = [plist4bus[i] valueForKey:@"classInfo"];
261 if (classInfo != nil && shouldLoadInternal) {
262 status = AudioUnitSetProperty(iop->unit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, (AudioUnitElement)0, &classInfo, psize);
263 if (status != 0) {
264 fprintf(stderr, "*** cannot set classInfo to output bus %d (device %s)\n", (i - kMDAudioFirstIndexForOutputStream + 1), namep);
265 } else {
266 [classInfo retain];
267 }
268 }
269 /* Set pan and volume */
270 num = [plist4bus[i] valueForKey:@"pan"];
271 if (num)
272 MDAudioSetMixerPan(i, [num doubleValue]);
273 num = [plist4bus[i] valueForKey:@"volume"];
274 if (num)
275 MDAudioSetMixerVolume(i, [num doubleValue]);
276 }
277 continue;
278 } else {
279 /* Input stream */
280 NSArray *ary1, *ary2;
281 isInput = 1;
282 for (j = 0; (ap = MDAudioDeviceInfoAtIndex(j, isInput)) != NULL; j++) {
283 if (strcmp(ap->name, namep) == 0) {
284 deviceIndex = j;
285 break;
286 }
287 }
288 if (deviceIndex < 0) {
289 for (j = 0; (mp = MDAudioMusicDeviceInfoAtIndex(j)) != NULL; j++) {
290 if (strcmp(mp->name, namep) == 0) {
291 deviceIndex = j + kMDAudioMusicDeviceIndexOffset;
292 break;
293 }
294 }
295 }
296 if (deviceIndex < 0) {
297 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
298 [alert setMessageText:@"Audio Settings Import Error"];
299 [alert setInformativeText:[NSString stringWithFormat:@"No device %s is available.", namep]];
300 [alert runModal];
301 continue;
302 }
303 /* Look up the existing audio/music input device */
304 k = -1;
305 for (j = 0; j < kMDAudioNumberOfInputStreams; j++) {
306 if (map_current2plist[j] != -1)
307 continue; /* This slot is already used */
308 iop = MDAudioGetIOStreamInfoAtIndex(j);
309 if (iop->deviceIndex == deviceIndex) {
310 map_current2plist[j] = i;
311 map_plist2current[i] = j;
312 break;
313 } else if (iop->deviceIndex == -1 && k == -1) {
314 k = j; /* Remember the first bus that is not used */
315 }
316 }
317 if (j >= kMDAudioNumberOfInputStreams) {
318 /* We need to open a new input device */
319 if (k == -1) {
320 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
321 [alert setMessageText:@"Audio Settings Import Error"];
322 [alert setInformativeText:[NSString stringWithFormat:@"Cannot open %s: no empty input bus is available.", namep]];
323 [alert runModal];
324 return;
325 }
326 map_current2plist[k] = i;
327 map_plist2current[i] = k;
328 /* Open the music device */
329 MDAudioSelectIOStreamDevice(k, deviceIndex);
330 }
331 /* Set classInfo if present */
332 shouldLoadInternal = [AudioSettingsPrefPanelController shouldSaveInternalForDeviceName:namep];
333 classInfo = [plist4bus[i] valueForKey:@"classInfo"];
334 iop = MDAudioGetIOStreamInfoAtIndex(map_plist2current[i]);
335 if (classInfo != nil && shouldLoadInternal) {
336 [classInfo retain];
337 status = AudioUnitSetProperty(iop->unit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, (AudioUnitElement)0, &classInfo, psize);
338 if (status != 0) {
339 fprintf(stderr, "*** cannot set classInfo to input bus %d (device %s)\n", i + 1, namep);
340 }
341 }
342 /* Set pan and volume */
343 num = [plist4bus[i] valueForKey:@"pan"];
344 if (num)
345 MDAudioSetMixerPan(map_plist2current[i], [num doubleValue]);
346 num = [plist4bus[i] valueForKey:@"volume"];
347 if (num)
348 MDAudioSetMixerVolume(map_plist2current[i], [num doubleValue]);
349 /* Rebuild the effect chain */
350 /* Dispose the existing effects */
351 while (iop->nchains > 0) {
352 MDAudioEffectChain *cp = &(iop->chains[iop->nchains - 1]);
353 while (cp->neffects > 0) {
354 MDAudioRemoveEffect(map_plist2current[i], iop->nchains - 1, cp->neffects - 1);
355 }
356 if (iop->nchains == 1)
357 break;
358 MDAudioRemoveLastEffectChain(map_plist2current[i]);
359 }
360 /* Creating the effect chains */
361 ary1 = [plist4bus[i] valueForKey:@"effectChains"];
362 for (j = 0; j < [ary1 count]; j++) {
363 ary2 = [ary1 objectAtIndex:j];
364 if ([ary2 count] > 0 && j >= iop->nchains) {
365 /* Append a new effect chain */
366 MDAudioAppendEffectChain(map_plist2current[i]);
367 }
368 for (k = 0; k < [ary2 count]; k++) {
369 NSDictionary *dic = [ary2 objectAtIndex:k];
370 const char *enamep = [[dic valueForKey:@"effectName"] UTF8String];
371 int ei;
372 MDAudioEffect *ep;
373 ep = NULL;
374 for (ei = 0; (mp = MDAudioEffectDeviceInfoAtIndex(ei)) != NULL; ei++) {
375 if (strcmp(mp->name, enamep) == 0) {
376 status = MDAudioChangeEffect(map_plist2current[i], j, k, ei, 1);
377 if (status != 0) {
378 fprintf(stderr, "*** cannot create effect %s in effect chain %d-%d\n", enamep, j, k);
379 } else {
380 ep = &(iop->chains[j].effects[k]);
381 /* Set classInfo if present */
382 shouldLoadInternal = [AudioSettingsPrefPanelController shouldSaveInternalForDeviceName:enamep];
383 classInfo = [dic valueForKey:@"classInfo"];
384 if (classInfo != nil && shouldLoadInternal) {
385 status = AudioUnitSetProperty(ep->unit, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, (AudioUnitElement)0, &classInfo, psize);
386 if (status != 0) {
387 fprintf(stderr, "*** cannot set classInfo to effect %s in effect chain %d-%d\n", enamep, j, k);
388 } else {
389 [classInfo retain];
390 }
391 }
392
393 }
394 }
395 }
396 }
397 }
398 }
399 }
400 [self updateDisplay];
401 }
402
403 - (void)importAudioSettings
404 {
405 NSOpenPanel *panel = [NSOpenPanel openPanel];
406 int result;
407 [panel setAllowsMultipleSelection:NO];
408 [panel setCanChooseDirectories:NO];
409 [panel setCanCreateDirectories:YES];
410 [panel setCanChooseFiles:YES];
411 [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"xml", nil]];
412 result = (int)[panel runModal];
413 if (result == NSFileHandlingPanelOKButton) {
414 NSURL *url = [panel URL];
415 NSData *data = [NSData dataWithContentsOfURL:url];
416 NSPropertyListFormat plistFormat;
417 id plist = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListImmutable format:&plistFormat error:NULL];
418 if (plist == NULL) {
419 NSAlert *alert = [[[NSAlert alloc] init] autorelease];
420 [alert setMessageText:@"Audio Settings Import Error"];
421 [alert setInformativeText:@"Cannot read the settings file."];
422 [alert runModal];
423 return;
424 }
425 //printCFdata(plist, 0); /* for debug */
426 [self importAudioSettingsFromPropertyList:plist];
427 }
428 }
429
430 - (void)updateDisplay
431 {
432 int idx, i, n, isInput;
433 NSMenu *menu;
434 MDAudioDeviceInfo *dp;
435 MDAudioMusicDeviceInfo *mp;
436 if (knobValues == nil) {
437 knobValues = [[NSMutableArray arrayWithCapacity:kMDAudioNumberOfStreams] retain];
438 for (idx = 0; idx < kMDAudioNumberOfStreams; idx++) {
439 [knobValues addObject:[NSNumber numberWithFloat:0.0f]];
440 }
441 }
442 for (idx = 0; idx < kMDAudioNumberOfStreams; idx++) {
443 id view;
444 int tagOffset;
445 if (idx >= kMDAudioFirstIndexForOutputStream) {
446 isInput = 0;
447 tagOffset = idx - kMDAudioFirstIndexForOutputStream + kOutputTagOffset;
448 } else {
449 isInput = 1;
450 tagOffset = idx;
451 }
452 /* Device PopUp button */
453 view = [self viewWithTag: kDevicePopUpBase + tagOffset];
454 /* Create menu */
455 menu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
456 [menu addItemWithTitle: @"(none)" action: nil keyEquivalent: @""];
457 [[menu itemAtIndex: 0] setTag: -1];
458 [menu addItem: [NSMenuItem separatorItem]];
459 for (i = 0; (dp = MDAudioDeviceInfoAtIndex(i, isInput)) != NULL; i++) {
460 [menu addItemWithTitle: [NSString stringWithUTF8String: dp->name] action: nil keyEquivalent: @""];
461 [[menu itemAtIndex: i + 2] setTag: i + 1];
462 }
463 if (isInput) {
464 [menu addItem: [NSMenuItem separatorItem]];
465 n = i + 3; /* Number of items */
466 for (i = 0; (mp = MDAudioMusicDeviceInfoAtIndex(i)) != NULL; i++) {
467 [menu addItemWithTitle: [NSString stringWithUTF8String: mp->name] action: nil keyEquivalent: @""];
468 [[menu itemAtIndex: i + n] setTag: i + kMDAudioMusicDeviceIndexOffset + 1];
469 }
470 }
471 [view setMenu: menu];
472 MDAudioGetIOStreamDevice(idx, &n);
473 if (n < 0)
474 [view selectItemAtIndex: 0];
475 else
476 [view selectItemWithTag: n + 1];
477 if (n >= 0 || !isInput) {
478 view = [self viewWithTag: kPanKnobBase + tagOffset];
479 [view setEnabled: (isInput != 0)];
480 view = [self viewWithTag: kVolumeSliderBase + tagOffset];
481 [view setEnabled: YES];
482 view = [self viewWithTag: kLeftLevelIndicatorBase + tagOffset];
483 [view setEnabled: YES];
484 view = [self viewWithTag: kRightLevelIndicatorBase + tagOffset];
485 [view setEnabled: YES];
486 if (isInput) {
487 view = [self viewWithTag: kCustomViewButtonBase + tagOffset];
488 if (n >= kMDAudioMusicDeviceIndexOffset &&
489 (mp = MDAudioMusicDeviceInfoAtIndex(n - kMDAudioMusicDeviceIndexOffset)) != NULL &&
490 (mp->hasCustomView || [AudioSettingsPrefPanelController shouldCallApplicationForDeviceName:mp->name] != nil)) {
491 [view setEnabled: YES];
492 [view setState: NSOnState];
493 } else {
494 [view setEnabled: NO];
495 [view setState: NSOffState];
496 }
497 view = [self viewWithTag: kEffectButtonBase + tagOffset];
498 [view setEnabled: YES];
499 [view setState: NSOnState];
500 }
501 } else {
502 [[self viewWithTag: kPanKnobBase + tagOffset] setEnabled: NO];
503 [[self viewWithTag: kVolumeSliderBase + tagOffset] setEnabled: NO];
504 [[self viewWithTag: kLeftLevelIndicatorBase + tagOffset] setEnabled: NO];
505 [[self viewWithTag: kRightLevelIndicatorBase + tagOffset] setEnabled: NO];
506 view = [self viewWithTag: kCustomViewButtonBase + tagOffset];
507 [view setEnabled: NO];
508 [view setState: NSOffState];
509 view = [self viewWithTag: kEffectButtonBase + tagOffset];
510 [view setEnabled: NO];
511 [view setState: NSOffState];
512 }
513 }
514 }
515
516 - (void)timerCallback: (NSTimer *)timer
517 {
518 float pan, volume, ampLeft, ampRight, peakLeft, peakRight;
519 int idx;
520 if (![[self window] isVisible])
521 return;
522 for (idx = 0; idx < kMDAudioNumberOfStreams; idx++) {
523 if (idx < kMDAudioNumberOfInputStreams) {
524 /* Skip if the device is disabled */
525 int n;
526 if (MDAudioGetIOStreamDevice(idx, &n) != kMDNoError || n < 0)
527 continue;
528 }
529 if (MDAudioGetMixerBusAttributes(idx, &pan, &volume, &ampLeft, &ampRight, &peakLeft, &peakRight) == kMDNoError) {
530 int tagOffset = (idx % kMDAudioFirstIndexForOutputStream) + (idx >= kMDAudioFirstIndexForOutputStream ? kOutputTagOffset : 0);
531 ampLeft = (ampLeft * 1.6667f) + 100.0f;
532 ampRight = (ampRight * 1.6667f) + 100.0f;
533 if (ampLeft > 100.0f)
534 ampLeft = 100.0f;
535 if (ampLeft < 0.0f)
536 ampLeft = 0.0f;
537 if (ampRight > 100.0f)
538 ampRight = 100.0f;
539 if (ampRight < 0.0f)
540 ampRight = 0.0f;
541 /* The pan slider uses 60-100 (for 0 to 0.5) and 0-40 (for 0.5 to 1.0) */
542 [[self viewWithTag: kPanKnobBase + tagOffset] setFloatValue: (pan - 0.5f) * 80 + (pan < 0.5f ? 100 : 0)];
543 [knobValues replaceObjectAtIndex:idx withObject:[NSNumber numberWithFloat:pan]];
544 [[self viewWithTag: kVolumeSliderBase + tagOffset] setFloatValue: volume * 100.0f];
545 [[self viewWithTag: kLeftLevelIndicatorBase + tagOffset] setFloatValue: ampLeft];
546 [[self viewWithTag: kRightLevelIndicatorBase + tagOffset] setFloatValue: ampRight];
547 }
548 }
549 }
550
551 - (void)stopTimer
552 {
553 if (timer != nil) {
554 [timer invalidate];
555 [timer release];
556 timer = nil;
557 }
558 }
559
560 - (IBAction)volumeSliderMoved:(id)sender
561 {
562 int idx = (int)[sender tag] - kVolumeSliderBase;
563 if (idx >= kOutputTagOffset)
564 idx += (kMDAudioFirstIndexForOutputStream - kOutputTagOffset);
565 MDAudioSetMixerVolume(idx, [sender floatValue] * 0.01f);
566 }
567
568 - (IBAction)panKnobMoved:(id)sender
569 {
570 float pan, opan;
571 int idx = (int)[sender tag] - kPanKnobBase;
572 if (idx >= kOutputTagOffset)
573 idx += (kMDAudioFirstIndexForOutputStream - kOutputTagOffset);
574 pan = [sender floatValue];
575 pan = (pan >= 50.0f ? pan - 100.0f : pan) / 80.0f + 0.5f;
576 opan = [[knobValues objectAtIndex:idx] floatValue];
577 if (pan < 0.0f || pan > 1.0f || (opan < 0.25f && pan > 0.75f) || (opan > 0.75f && pan < 0.25f)) {
578 /* Do not change value */
579 [sender setFloatValue:(opan - 0.5f) * 80 + (opan < 0.5f ? 100 : 0)];
580 return;
581 }
582 [knobValues replaceObjectAtIndex:idx withObject:[NSNumber numberWithFloat:pan]];
583 MDAudioSetMixerPan(idx, pan);
584 }
585
586 - (IBAction)myPopUpAction: (id)sender
587 {
588 int idx = (int)[sender tag] - kDevicePopUpBase;
589 int dev = (int)[[sender selectedItem] tag];
590 if (dev > 0)
591 dev--;
592 if (idx >= kOutputTagOffset)
593 idx += (kMDAudioFirstIndexForOutputStream - kOutputTagOffset);
594 MDAudioSelectIOStreamDevice(idx, dev);
595 [self updateDisplay];
596 }
597
598 - (IBAction)customViewButtonPressed: (id)sender
599 {
600 int idx = (int)[sender tag] - kCustomViewButtonBase;
601 if (idx >= 0 && idx < kMDAudioNumberOfInputStreams) {
602 int dev;
603 MDAudioGetIOStreamDevice(idx, &dev);
604 if (dev >= kMDAudioMusicDeviceIndexOffset) {
605 MDAudioIOStreamInfo *ip;
606 ip = MDAudioGetIOStreamInfoAtIndex(idx);
607 if (ip != NULL && ip->unit != NULL) {
608 char *name = NULL;
609 MDAudioMusicDeviceInfo *mp = MDAudioMusicDeviceInfoAtIndex(ip->deviceIndex - kMDAudioMusicDeviceIndexOffset);
610 id appName = [AudioSettingsPrefPanelController shouldCallApplicationForDeviceName:mp->name];
611 if (appName != nil) {
612 [[NSWorkspace sharedWorkspace] launchApplication:appName];
613 } else {
614 id cont = [AUViewWindowController windowControllerForAudioUnit: ip->unit cocoaView:(mp->hasCustomView == kMDAudioHasCocoaView) delegate: nil];
615 if (ip->midiControllerName != NULL)
616 name = ip->midiControllerName;
617 else {
618 if (mp != NULL && mp->name != NULL)
619 name = mp->name;
620 }
621 if (name != NULL)
622 [[cont window] setTitle: [NSString stringWithUTF8String: name]];
623 }
624 }
625 }
626 }
627 }
628
629 - (IBAction)effectButtonPressed: (id)sender
630 {
631 int idx = (int)[sender tag] - kEffectButtonBase;
632 if (idx >= 0 && idx < kMDAudioNumberOfInputStreams) {
633 AudioEffectPanelController *cont = (AudioEffectPanelController *)effectControllers[idx];
634 if (cont != nil) {
635 [[cont window] makeKeyAndOrderFront:self];
636 } else {
637 cont = [[AudioEffectPanelController alloc] initWithBusIndex:idx];
638 [[cont window] makeKeyAndOrderFront:self];
639 effectControllers[idx] = cont;
640 }
641 }
642 }
643
644 - (void)windowDidLoad
645 {
646 int i, count;
647
648 [super windowDidLoad];
649
650 // Create bus controller list
651 {
652 static int sTagsToCopy[] = {
653 kDevicePopUpBase, kPanKnobBase, kVolumeSliderBase,
654 kLeftLevelIndicatorBase, kRightLevelIndicatorBase, kCustomViewButtonBase,
655 kEffectButtonBase, kBusIndexTextBase,
656 0 /* This is dummy to copy the horizontal line */
657 };
658 NSPoint pt = [separatorLine frame].origin;
659 NSRect frame = [busListView frame];
660 float busHeight = frame.size.height - pt.y;
661 frame.size.height = busHeight * kMDAudioNumberOfInputStreams;
662 [busListView setFrame:frame];
663 for (count = 1; count < kMDAudioNumberOfInputStreams; count++) {
664 for (i = 0; i < sizeof(sTagsToCopy) / sizeof(sTagsToCopy[0]); i++) {
665 NSView *view, *newview;
666 NSData *data;
667 NSRect vframe;
668 int tag = sTagsToCopy[i];
669 if (tag == 0)
670 view = separatorLine;
671 else
672 view = [self viewWithTag:tag];
673 data = [NSKeyedArchiver archivedDataWithRootObject:view];
674 newview = [NSKeyedUnarchiver unarchiveObjectWithData:data];
675 if (tag != 0)
676 [(id)newview setTag:tag + count];
677 if (tag == kBusIndexTextBase)
678 [(id)newview setStringValue:[NSString stringWithFormat:@"Bus %d", count + 1]];
679 vframe = [view frame];
680 vframe.origin.y -= count * busHeight;
681 [newview setFrame:vframe];
682 [busListView addSubview:newview];
683 }
684 }
685 [busListView scrollPoint:NSMakePoint(0, frame.size.height - [[busListView superview] frame].size.height)];
686 }
687
688 if (timer == nil) {
689 timer = [[NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector:@selector(timerCallback:) userInfo:nil repeats:YES] retain];
690 }
691 if (effectControllers == NULL) {
692 effectControllers = (id *)calloc(sizeof(id), kMDAudioNumberOfInputStreams);
693 }
694 [self updateDisplay];
695 }
696
697 @end

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26