Develop and Download Open Source Software

Browse Subversion Repository

Contents of /trunk/Classes/AudioEffectPanelController.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 169 - (show annotations) (download)
Sun Oct 27 01:45:31 2019 UTC (4 years, 7 months ago) by toshinagata1964
File size: 17781 byte(s)
Behavior of the split view of the graphic window is fixed
1 //
2 // AudioEffectPanelController.m
3 // Alchemusica
4 //
5 // Created by Toshi Nagata on 2017/10/09.
6 // Copyright 2010-2017 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 "AudioEffectPanelController.h"
20 #import "AudioEffectLayoutView.h"
21 #import "AUViewWindowController.h"
22 #import "MyPopUpButton.h"
23 #import "NSWindowControllerAdditions.h"
24 #import "NSMenuAdditions.h"
25 #import "MDHeaders.h"
26
27 enum {
28 kAudioEffectPanelStereoInTextTag = 0,
29 kAudioEffectPanelChannelMinusButtonTag = 1,
30 kAudioEffectPanelChannelPlusButtonTag = 2,
31 kAudioEffectPanelChannelTextTag = 3,
32 kAudioEffectPanelStereoOutTextTag = 4,
33 kAudioEffectPanelEffectBaseTag = 10
34 };
35
36 static NSString *sAudioEffectPanelShouldUpdate = @"Audio effect panel should update";
37
38 @implementation AudioEffectPanelController
39
40 - (id)initWithBusIndex:(int)idx
41 {
42 self = [super initWithWindowNibName:@"AudioEffectPanel"];
43 if (self) {
44 busIndex = idx;
45 }
46 return self;
47 }
48
49 - (void)appendChain
50 {
51
52 }
53
54 - (void)removeLastChain
55 {
56 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
57 if (ip->nchains <= 1)
58 return;
59 if (ip->chains[ip->nchains - 1].neffects > 0)
60 return;
61
62 }
63
64 - (void)insertEffectWithName:(NSString *)name atIndex:(int)effectIndex inChain:(int)chainIndex
65 {
66
67 }
68
69 - (void)replaceEffectName:(NSString *)name atIndex:(int)effectIndex inChain:(int)chainIndex
70 {
71
72 }
73
74 - (void)removeEffectAtIndex:(int)effectIndex inChain:(int)chainIndex
75 {
76
77 }
78
79 - (NSMenu *)menuWithEffectNames
80 {
81 int k;
82 id item;
83 NSMenu *menu;
84 MDAudioMusicDeviceInfo *ep;
85 menu = [[[NSMenu alloc] init] autorelease];
86 for (k = 0; (ep = MDAudioEffectDeviceInfoAtIndex(k)) != NULL; k++) {
87 NSString *str2;
88 str2 = [NSString stringWithUTF8String:ep->name];
89 item = [menu addItemWithTitle:str2 action:nil keyEquivalent:@""];
90 [item setTag:k];
91 [item setTarget:self];
92 }
93 return menu;
94 }
95
96 - (NSBox *)enclosingBoxForView:(NSView *)view
97 {
98 NSView *sview;
99 for (sview = [view superview]; sview != nil; sview = [sview superview]) {
100 if ([sview isKindOfClass:[NSBox class]])
101 return (NSBox *)sview;
102 }
103 return nil;
104 }
105
106 - (void)updateWindow
107 {
108 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
109 NSView *view;
110 int n, height, i, j;
111 NSRect r, r1;
112 NSRect b = [layoutView frame];
113 NSFont *font = [NSFont labelFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
114 NSDictionary *glyphAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
115 font, NSFontAttributeName, nil];
116 NSMutableParagraphStyle *style = [[[NSMutableParagraphStyle alloc] init] autorelease];
117 NSDictionary *redTextAttributes;
118
119 [style setAlignment:NSCenterTextAlignment];
120 redTextAttributes = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, [NSColor redColor], NSForegroundColorAttributeName, style, NSParagraphStyleAttributeName, nil];
121
122 // New height of the layout view (width will be updated later)
123 n = ip->nchains;
124 height = (n <= 1 ? 52 : 52 + (n - 1) * 25);
125 b.size.height = height;
126 [layoutView setFrame:b];
127
128 // Resize 'Stereo In' box
129 view = [self enclosingBoxForView:[self viewWithTag:kAudioEffectPanelStereoInTextTag]];
130 r = [view frame];
131 height = (n <= 1 ? 17 : 17 + (n - 1) * 25);
132 r.origin.y = b.size.height - (height + 12);
133 r.size.height = height;
134 [view setFrame:r];
135
136 // Relocate channel+, channel- buttons
137 view = [self viewWithTag:kAudioEffectPanelChannelPlusButtonTag];
138 r1 = [view frame];
139 r1.origin.y = r.origin.y - r1.size.height + 2;
140 [view setFrame:r1];
141 view = [self viewWithTag:kAudioEffectPanelChannelMinusButtonTag];
142 r1 = [view frame];
143 r1.origin.y = r.origin.y - r1.size.height + 2;
144 [view setFrame:r1];
145 if (ip->nchains > 1 && ip->chains[ip->nchains - 1].neffects == 0)
146 [(NSButton *)view setEnabled:YES];
147 else [(NSButton *)view setEnabled:NO];
148
149 xpos_output = 0;
150 // Relocate view for each chain
151 for (i = 0; i < n; i++) {
152 int base = i * 1000;
153 int ybase = b.size.height - 29 - 25 * i;
154 int xbase = r.origin.x + r.size.width - 1;
155
156 // Channel "N-N+1" text
157 view = [self viewWithTag:base + kAudioEffectPanelChannelTextTag];
158 if (view == nil) {
159 // We need to create a NSBox and its enclosing text
160 NSString *s;
161 view = [self enclosingBoxForView:[self viewWithTag:kAudioEffectPanelChannelTextTag]];
162 // Copy the NSBox
163 view = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:view]];
164 [layoutView addSubview:view];
165 view = [view viewWithTag:kAudioEffectPanelChannelTextTag];
166 [(NSTextField *)view setTag:base + kAudioEffectPanelChannelTextTag];
167 s = [NSString stringWithFormat:@"%d-%d", i * 2, i * 2 + 1];
168 if (ip->chains[i].alive) {
169 [(NSTextField *)view setStringValue:s];
170 } else {
171 [(NSTextField *)view setAttributedStringValue:[[[NSAttributedString alloc] initWithString:s attributes:redTextAttributes] autorelease]];
172 }
173 }
174 view = [self enclosingBoxForView:view];
175 r1 = [view frame];
176 r1.origin.x = xbase;
177 r1.origin.y = ybase;
178 [view setFrame:r1];
179 xbase += r1.size.width - 1;
180
181 // Effect buttons
182 for (j = 0; j <= ip->chains[i].neffects; j++) {
183 NSButton *bplus, *bname, *bmenu;
184 NSString *str;
185 NSSize size;
186 int buttonTag = base + kAudioEffectPanelEffectBaseTag + j * 3;
187
188 // effect+ button
189 bplus = (NSButton *)[self viewWithTag:buttonTag];
190 if (bplus == nil) {
191 // Copy the button for chain 0, effect 0
192 bplus = (NSButton *)[self viewWithTag:kAudioEffectPanelEffectBaseTag];
193 bplus = (NSButton *)[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:bplus]];
194 [layoutView addSubview:bplus];
195 [bplus setTag:buttonTag];
196 [bplus highlight:NO];
197 }
198 r1 = [bplus frame];
199 r1.origin.x = xbase;
200 r1.origin.y = ybase + 1;
201 [bplus setFrame:r1];
202 xbase += r1.size.width - 1;
203 if (j == ip->chains[i].neffects)
204 break;
205
206 // Effect button
207 str = [NSString stringWithUTF8String:ip->chains[i].effects[j].name];
208 size = [str sizeWithAttributes:glyphAttributes];
209 r1.origin = NSMakePoint(xbase + 16, ybase);
210 r1.size = NSMakeSize((float)ceil(size.width + 10), 17);
211 bname = (NSButton *)[self viewWithTag:buttonTag + 1];
212 bmenu = (NSButton *)[self viewWithTag:buttonTag + 2];
213 if (bname == nil) {
214 // We need to create new buttons
215 bname = [[[NSButton alloc] initWithFrame:r1] autorelease];
216 [bname setBezelStyle:NSShadowlessSquareBezelStyle];
217 [bname setTag:buttonTag + 1];
218 [[bname cell] setControlSize:NSSmallControlSize];
219 [bname setTarget:self];
220 [bname setAction:@selector(effectButtonPressed:)];
221 bmenu = [[[NSButton alloc] initWithFrame:r1] autorelease];
222 [bmenu setBezelStyle:NSShadowlessSquareBezelStyle];
223 [bmenu setTag:buttonTag + 2];
224 [bmenu setTarget:self];
225 [bmenu setAction:@selector(addEffect:)];
226 [bmenu setImage:[MyPopUpButton doubleTriangleImage]];
227 [layoutView addSubview:bname];
228 [layoutView addSubview:bmenu];
229 }
230 [bname setAttributedTitle:[[[NSAttributedString alloc] initWithString:str attributes:glyphAttributes] autorelease]];
231 [bname setFrame:r1];
232 r1.origin.x += r1.size.width - 1;
233 r1.size.width = 14;
234 [bmenu setFrame:r1];
235 if (selectedEffect >= 0 && selectedEffect / 1000 == i && selectedEffect % 1000 == j)
236 [bname highlight:YES];
237 else [bname highlight:NO];
238 xbase = r1.origin.x + r1.size.width - 1;
239 }
240
241 xbase = r1.origin.x + r1.size.width + 32;
242 if (xpos_output < xbase)
243 xpos_output = xbase;
244 }
245
246 // Relocate 'Stereo Out' box
247 view = [self enclosingBoxForView:[self viewWithTag:kAudioEffectPanelStereoOutTextTag]];
248 r = [view frame];
249 height = 17;
250 r.origin.y = b.size.height - (height + 12);
251 r.size.height = height;
252 r.origin.x = xpos_output;
253 [view setFrame:r];
254
255 // Update the width of the layout view
256 b.size.width = r.origin.x + r.size.width + 12;
257 [layoutView setFrame:b];
258
259 [layoutView setNeedsDisplay:YES];
260
261 // Show custom view if requested
262 if (needsRedrawCustomView) {
263 int chainIndex, effectIndex;
264 if (selectedEffect < 0) {
265 chainIndex = -1;
266 effectIndex = 0;
267 } else {
268 chainIndex = selectedEffect / 1000;
269 effectIndex = selectedEffect % 1000;
270 }
271 [self showCustomViewForEffectInChain:chainIndex atIndex:effectIndex];
272 needsRedrawCustomView = NO;
273 }
274 }
275
276 - (void)updateRequested:(NSNotification *)aNotification
277 {
278 [self updateWindow];
279 }
280
281 - (void)windowDidLoad
282 {
283 [super windowDidLoad];
284 [[self window] setTitle:[NSString stringWithFormat:@"Audio Effects: Bus %d", busIndex + 1]];
285 [layoutView setDataSource:self];
286 selectedEffect = -1;
287 [[NSNotificationCenter defaultCenter]
288 addObserver: self
289 selector: @selector(updateRequested:)
290 name: sAudioEffectPanelShouldUpdate
291 object:nil];
292 [effectLayoutScrollView setDrawsBackground:YES];
293 [effectLayoutScrollView setBackgroundColor:[NSColor windowBackgroundColor]];
294 [effectContentScrollView setDrawsBackground:YES];
295 [effectContentScrollView setBackgroundColor:[NSColor windowBackgroundColor]];
296 [self updateWindow];
297 }
298
299 - (CGFloat)xpos_output
300 {
301 return xpos_output;
302 }
303
304 - (int)numberOfChains
305 {
306 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
307 return (ip->nchains);
308 }
309
310 - (void)showCustomViewForEffectInChain:(int)chainIndex atIndex:(int)effectIndex
311 {
312 NSView *view;
313 MDAudioMusicDeviceInfo *mp;
314 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
315 MDAudioEffect *ep;
316 if (chainIndex < 0 || chainIndex >= ip->nchains)
317 goto hide;
318 if (effectIndex < 0 || effectIndex > ip->chains[chainIndex].neffects)
319 return; // This cannot happen
320 ep = ip->chains[chainIndex].effects + effectIndex;
321 mp = MDAudioEffectDeviceInfoAtIndex(ep->effectDeviceIndex);
322 view = [AUViewWindowController getCocoaViewForAudioUnit:ep->unit defaultViewSize:NSMakeSize(100, 100)];
323 if (view != nil) {
324 NSRect b = [view bounds];
325 if (customView != nil)
326 [customView removeFromSuperview];
327 customView = nil;
328 [customContainerView setFrame:b];
329 [customContainerView addSubview:view];
330 customView = view;
331 return;
332 }
333 hide:
334 /* Hide the view */
335 if (customView != nil) {
336 [customView removeFromSuperview];
337 customView = nil;
338 }
339 }
340
341 #if 0
342 #pragma mark ====== Actions ======
343 #endif
344
345 - (IBAction)effectButtonPressed:(id)sender
346 {
347 int tag = (int)[sender tag];
348 int chainIndex = tag / 1000;
349 int itemOffset = tag % 1000 - kAudioEffectPanelEffectBaseTag;
350 int effectIndex = itemOffset / 3;
351 int newSelectedEffect = chainIndex * 1000 + effectIndex;
352 if (newSelectedEffect != selectedEffect) {
353 needsRedrawCustomView = YES;
354 selectedEffect = newSelectedEffect;
355 }
356 [[NSNotificationQueue defaultQueue] enqueueNotification: [NSNotification notificationWithName: sAudioEffectPanelShouldUpdate object: self] postingStyle: NSPostWhenIdle];
357 }
358
359 - (IBAction)addEffect:(id)sender
360 {
361 NSMenu *menu;
362 id item;
363 int i;
364 int tag = (int)[sender tag];
365 int chainIndex = tag / 1000;
366 int itemOffset = tag % 1000 - kAudioEffectPanelEffectBaseTag;
367 int effectIndex = itemOffset / 3;
368 int insert = (itemOffset % 3 == 0);
369 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
370 if (chainIndex < 0 || chainIndex >= ip->nchains)
371 return; // This cannot happen
372 if (effectIndex < 0 || effectIndex > ip->chains[chainIndex].neffects)
373 return; // This cannot happen
374 menu = [self menuWithEffectNames];
375 for (i = (int)[menu numberOfItems] - 1; i >= 0; i--) {
376 item = [menu itemAtIndex:i];
377 [item setAction:(insert ? @selector(insertEffect:) : @selector(changeEffect:))];
378 [item setTag:chainIndex * 1000000 + effectIndex * 1000 + i];
379 }
380 if (!insert) {
381 [menu addItem:[NSMenuItem separatorItem]];
382 item = [menu addItemWithTitle:@"Remove this effect" action:@selector(removeEffect:) keyEquivalent:@""];
383 [item setTag:chainIndex * 1000000 + effectIndex * 1000 + 999];
384 }
385 [menu changeMenuTitleAttributes:[NSDictionary dictionaryWithObject:[NSFont labelFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]] forKey:NSFontAttributeName]];
386 [NSMenu popUpContextMenu:menu withEvent:[NSApp currentEvent] forView:sender];
387 }
388
389 - (IBAction)addEffectChain:(id)sender
390 {
391 MDAudioAppendEffectChain(busIndex);
392 [self updateWindow];
393 }
394
395 - (IBAction)removeEffectChain:(id)sender
396 {
397 NSView *view;
398 int tagbase;
399 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
400 if (ip->nchains <= 1 || ip->chains[ip->nchains - 1].neffects != 0)
401 return; /* Do nothing */
402 MDAudioRemoveLastEffectChain(busIndex);
403 /* Remove 'N-N+1' box and effect+ button */
404 tagbase = ip->nchains * 1000;
405 view = [layoutView viewWithTag:tagbase + kAudioEffectPanelChannelTextTag];
406 if (view != nil && (view = [self enclosingBoxForView:view]) != nil)
407 [view removeFromSuperview];
408 view = [layoutView viewWithTag:tagbase + kAudioEffectPanelEffectBaseTag];
409 if (view != nil)
410 [view removeFromSuperview];
411 [self updateWindow];
412 }
413
414 - (void)changeEffect:(id)sender
415 {
416 int tag = (int)[sender tag];
417 int chainIndex = tag / 1000000;
418 int effectIndex = (tag / 1000) % 1000;
419 int effectID = tag % 1000;
420 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
421 if (chainIndex < 0 || chainIndex >= ip->nchains)
422 return; // This cannot happen
423 MDAudioChangeEffect(busIndex, chainIndex, effectIndex, effectID, 0);
424 selectedEffect = chainIndex * 1000 + effectIndex;
425 needsRedrawCustomView = YES;
426 [self updateWindow];
427 }
428
429 - (void)insertEffect:(id)sender
430 {
431 int tag = (int)[sender tag];
432 int chainIndex = tag / 1000000;
433 int effectIndex = (tag / 1000) % 1000;
434 int effectID = tag % 1000;
435 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
436 if (chainIndex < 0 || chainIndex > ip->nchains)
437 return; // This cannot happen
438 MDAudioChangeEffect(busIndex, chainIndex, effectIndex, effectID, 1);
439 selectedEffect = chainIndex * 1000 + effectIndex;
440 needsRedrawCustomView = YES;
441 [self updateWindow];
442 }
443
444 - (void)removeEffect:(id)sender
445 {
446 int i, n;
447 int tag = (int)[sender tag];
448 int chainIndex = tag / 1000000;
449 int effectIndex = (tag / 1000) % 1000;
450 MDAudioIOStreamInfo *ip = MDAudioGetIOStreamInfoAtIndex(busIndex);
451 if (chainIndex < 0 || chainIndex > ip->nchains)
452 return; // This cannot happen
453 MDAudioRemoveEffect(busIndex, chainIndex, effectIndex);
454 if (selectedEffect / 1000 == chainIndex) {
455 if (effectIndex == selectedEffect % 1000) {
456 /* Select the nearest effect on the same chain */
457 if (effectIndex == ip->chains[chainIndex].neffects) {
458 /* The view should be updated */
459 if (effectIndex == 0)
460 selectedEffect = -1; /* No selection */
461 /* TODO: update the view */
462 }
463 } else if (effectIndex < selectedEffect % 1000) {
464 /* Decrement the selected effect index (pointing to the same effect) */
465 selectedEffect--;
466 }
467 }
468 n = ip->chains[chainIndex].neffects;
469 for (i = 0; i < 3; i++) {
470 id view = [self viewWithTag:chainIndex * 1000 + kAudioEffectPanelEffectBaseTag + 1 + n * 3 + i];
471 [view removeFromSuperview];
472 }
473 [self updateWindow];
474 if (selectedEffect < 0) {
475 chainIndex = -1;
476 effectIndex = 0;
477 } else {
478 chainIndex = selectedEffect / 1000;
479 effectIndex = selectedEffect % 1000;
480 }
481 [self showCustomViewForEffectInChain:chainIndex atIndex:effectIndex];
482 }
483
484 #if 0
485 #pragma mark ====== Delegate methods ======
486 #endif
487
488 - (CGFloat)splitView:(NSSplitView *)splitView constrainSplitPosition:(CGFloat)proposedPosition ofSubviewAt:(NSInteger)dividerIndex
489 {
490 NSSize size = [[[self window] contentView] bounds].size;
491 if (proposedPosition < 42.0f)
492 return 42.0f;
493 else if (proposedPosition > size.height - 48.0f)
494 return size.height - 48.0f;
495 else return proposedPosition;
496 }
497
498 @end

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