mirror of
https://github.com/holub/mame
synced 2025-05-10 08:12:13 +03:00
1077 lines
36 KiB
Plaintext
1077 lines
36 KiB
Plaintext
// license:BSD-3-Clause
|
|
// copyright-holders:Vas Crabb
|
|
#import <AvailabilityMacros.h>
|
|
#import <AudioUnit/AudioUnit.h>
|
|
#import <AudioUnit/AUCocoaUIView.h>
|
|
#import <AudioToolbox/AudioToolbox.h>
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <CoreAudio/CoreAudio.h>
|
|
#import <CoreAudioKit/CoreAudioKit.h>
|
|
#import <CoreFoundation/CoreFoundation.h>
|
|
#import <CoreServices/CoreServices.h>
|
|
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#ifdef MAC_OS_X_VERSION_MAX_ALLOWED
|
|
|
|
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060
|
|
|
|
typedef ComponentDescription AudioComponentDescription;
|
|
|
|
@protocol NSApplicationDelegate <NSObject>
|
|
@end
|
|
|
|
@protocol NSWindowDelegate <NSObject>
|
|
@end
|
|
|
|
#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060
|
|
|
|
#endif // MAC_OS_X_VERSION_MAX_ALLOWED
|
|
|
|
|
|
struct EffectInfo
|
|
{
|
|
Component component;
|
|
OSType type;
|
|
OSType subtype;
|
|
OSType manufacturer;
|
|
};
|
|
|
|
|
|
static NSString *const AUEffectUtilErrorDomain = @"AUEffectUtilErrorDomain";
|
|
|
|
static NSString *const AUEffectDocumentType = @"AUEffect";
|
|
static NSString *const AUPresetDocumentType = @"AudioUnit Preset";
|
|
|
|
static NSString *const ComponentTypeKey = @"ComponentType";
|
|
static NSString *const ComponentSubTypeKey = @"ComponentSubType";
|
|
static NSString *const ComponentManufacturerKey = @"ComponentManufacturer";
|
|
static NSString *const ClassInfoKey = @"ClassInfo";
|
|
static NSString *const ForceGenericViewKey = @"ForceGenericView";
|
|
static NSString *const WindowFrameKey = @"WindowFrame";
|
|
|
|
|
|
static void UpdateChangeCountCallback(
|
|
void *userData,
|
|
void *object,
|
|
AudioUnitEvent const *inEvent,
|
|
UInt64 inEventHostTime,
|
|
AudioUnitParameterValue inParameterValue)
|
|
{
|
|
[(NSDocument *)userData updateChangeCount:NSChangeDone];
|
|
}
|
|
|
|
|
|
@interface AUEffectDocument : NSDocument <NSWindowDelegate>
|
|
{
|
|
IBOutlet NSWindow *window;
|
|
IBOutlet NSButton *genericViewButton;
|
|
IBOutlet NSPopUpButton *presetButton;
|
|
NSView *view;
|
|
NSSize headerSize;
|
|
CFArrayRef presets;
|
|
AUParameterListenerRef listener;
|
|
BOOL forceGenericView;
|
|
NSString *restoreFrame;
|
|
|
|
AudioComponentDescription description;
|
|
AUGraph graph;
|
|
AUNode outputNode, sourceNode, effectNode;
|
|
AudioUnit outputUnit, sourceUnit, effectUnit;
|
|
}
|
|
|
|
- (void)dealloc;
|
|
|
|
- (void)makeWindowControllers;
|
|
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error;
|
|
- (NSData *)dataOfType:(NSString *)type error:(NSError **)error;
|
|
|
|
- (IBAction)toggleGenericView:(id)sender;
|
|
- (IBAction)loadPreset:(id)sender;
|
|
|
|
- (void)viewFrameDidChange:(NSNotification *)notification;
|
|
|
|
@end
|
|
|
|
@implementation AUEffectDocument
|
|
|
|
- (void)loadEffectUI {
|
|
if ((0 == effectNode) || !window)
|
|
return;
|
|
|
|
BOOL customViewValid = NO;
|
|
OSStatus status;
|
|
UInt32 uiDescSize;
|
|
AudioUnitCocoaViewInfo *viewInfo;
|
|
status = AudioUnitGetPropertyInfo(
|
|
effectUnit,
|
|
kAudioUnitProperty_CocoaUI,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
&uiDescSize,
|
|
NULL);
|
|
UInt32 const uiClassCount = 1 + ((uiDescSize - sizeof(*viewInfo)) / sizeof(viewInfo->mCocoaAUViewClass[0]));
|
|
if ((noErr == status) && (0 < uiClassCount))
|
|
{
|
|
viewInfo = (AudioUnitCocoaViewInfo *)malloc(uiDescSize);
|
|
status = AudioUnitGetProperty(effectUnit,
|
|
kAudioUnitProperty_CocoaUI,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
viewInfo,
|
|
&uiDescSize);
|
|
if (noErr == status)
|
|
{
|
|
NSBundle *const bundle = [NSBundle bundleWithPath:[(NSURL *)viewInfo->mCocoaAUViewBundleLocation path]];
|
|
Class const viewClass = [bundle classNamed:(NSString *)viewInfo->mCocoaAUViewClass[0]];
|
|
if (viewClass
|
|
&& [viewClass conformsToProtocol:@protocol(AUCocoaUIBase)]
|
|
&& [viewClass instancesRespondToSelector:@selector(uiViewForAudioUnit:withSize:)])
|
|
{
|
|
customViewValid = YES;
|
|
if (!forceGenericView)
|
|
{
|
|
id const factory = [[viewClass alloc] init];
|
|
view = [factory uiViewForAudioUnit:effectUnit
|
|
withSize:[[window contentView] bounds].size];
|
|
[factory release];
|
|
}
|
|
}
|
|
CFRelease(viewInfo->mCocoaAUViewBundleLocation);
|
|
for (UInt32 i = 0; i < uiClassCount; i++)
|
|
CFRelease(viewInfo->mCocoaAUViewClass[i]);
|
|
}
|
|
free(viewInfo);
|
|
}
|
|
if (!view)
|
|
{
|
|
view = [[[AUGenericView alloc] initWithAudioUnit:effectUnit] autorelease];
|
|
[(AUGenericView *)view setShowsExpertParameters:YES];
|
|
}
|
|
|
|
[view setAutoresizingMask:NSViewNotSizable];
|
|
[view setFrameOrigin:NSMakePoint(0, 0)];
|
|
NSRect const oldFrame = [window frame];
|
|
NSRect const desired = [window frameRectForContentRect:[view frame]];
|
|
NSRect const newFrame = NSMakeRect(oldFrame.origin.x,
|
|
oldFrame.origin.y + oldFrame.size.height - headerSize.height - desired.size.height,
|
|
desired.size.width,
|
|
headerSize.height + desired.size.height);
|
|
[window setFrame:newFrame display:YES animate:NO];
|
|
[[window contentView] addSubview:view];
|
|
[view setPostsFrameChangedNotifications:YES];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(viewFrameDidChange:)
|
|
name:NSViewFrameDidChangeNotification
|
|
object:view];
|
|
|
|
[genericViewButton setEnabled:customViewValid];
|
|
if (!customViewValid)
|
|
{
|
|
forceGenericView = YES;
|
|
[genericViewButton setState:NSOnState];
|
|
}
|
|
|
|
CFIndex const presetCount = (NULL != presets) ? CFArrayGetCount(presets) : 0;
|
|
[presetButton setEnabled:(0 < presetCount)];
|
|
while (1 < [presetButton numberOfItems])
|
|
[presetButton removeItemAtIndex:1];
|
|
for (CFIndex i = 0; i < presetCount; i++)
|
|
{
|
|
AUPreset const *preset = (AUPreset const*)CFArrayGetValueAtIndex(presets, i);
|
|
NSMenuItem const *item = [[presetButton menu] addItemWithTitle:(NSString *)preset->presetName
|
|
action:@selector(loadPreset:)
|
|
keyEquivalent:@""];
|
|
[item setTarget:self];
|
|
[item setTag:i];
|
|
}
|
|
}
|
|
|
|
- (id)init {
|
|
if (!(self = [super init]))
|
|
return nil;
|
|
|
|
window = nil;
|
|
genericViewButton = nil;
|
|
presetButton = nil;
|
|
view = nil;
|
|
presets = NULL;
|
|
listener = NULL;
|
|
forceGenericView = NO;
|
|
restoreFrame = nil;
|
|
|
|
description.componentType = description.componentSubType = description.componentManufacturer = 0;
|
|
description.componentFlags = description.componentFlagsMask = 0;
|
|
graph = NULL;
|
|
outputNode = sourceNode = effectNode = 0;
|
|
|
|
AudioComponentDescription const outputDesc = { kAudioUnitType_Output,
|
|
kAudioUnitSubType_DefaultOutput,
|
|
kAudioUnitManufacturer_Apple,
|
|
0,
|
|
0, };
|
|
AudioComponentDescription const sourceDesc = { kAudioUnitType_Generator,
|
|
kAudioUnitSubType_AudioFilePlayer,
|
|
kAudioUnitManufacturer_Apple,
|
|
0,
|
|
0, };
|
|
if ((noErr != NewAUGraph(&graph))
|
|
|| (noErr != AUGraphAddNode(graph, &outputDesc, &outputNode))
|
|
|| (noErr != AUGraphAddNode(graph, &sourceDesc, &sourceNode))
|
|
|| (noErr != AUGraphOpen(graph))
|
|
|| (noErr != AUGraphNodeInfo(graph, outputNode, NULL, &outputUnit))
|
|
|| (noErr != AUGraphNodeInfo(graph, sourceNode, NULL, &sourceUnit))
|
|
|| (noErr != AUGraphInitialize(graph)))
|
|
{
|
|
[self release];
|
|
return nil;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
if (presets)
|
|
CFRelease(presets);
|
|
|
|
if (listener)
|
|
AUListenerDispose(listener);
|
|
|
|
if (restoreFrame)
|
|
[restoreFrame release];
|
|
|
|
if (graph)
|
|
{
|
|
AUGraphClose(graph);
|
|
DisposeAUGraph(graph);
|
|
}
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)makeWindowControllers {
|
|
genericViewButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 18)];
|
|
[genericViewButton setAutoresizingMask:NSViewNotSizable];
|
|
[[genericViewButton cell] setControlSize:NSSmallControlSize];
|
|
[genericViewButton setButtonType:NSSwitchButton];
|
|
[genericViewButton setBordered:NO];
|
|
[genericViewButton setAllowsMixedState:NO];
|
|
[genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)];
|
|
[genericViewButton setTitle:@"Use generic editor view"];
|
|
[genericViewButton setAction:@selector(toggleGenericView:)];
|
|
[genericViewButton setTarget:self];
|
|
[genericViewButton sizeToFit];
|
|
|
|
presetButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 22) pullsDown:YES];
|
|
[presetButton setAutoresizingMask:NSViewNotSizable];
|
|
[[presetButton cell] setControlSize:NSSmallControlSize];
|
|
[[presetButton cell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
|
|
[presetButton setTitle:@"Load preset"];
|
|
[[[presetButton menu] addItemWithTitle:@"Load preset" action:NULL keyEquivalent:@""] setHidden:YES];
|
|
[presetButton sizeToFit];
|
|
|
|
CGFloat const controlWidth = MAX(NSWidth([genericViewButton frame]), NSWidth([presetButton frame]));
|
|
NSRect const presetFrame = NSMakeRect(
|
|
17,
|
|
8,
|
|
controlWidth,
|
|
NSHeight([presetButton frame]));
|
|
NSRect const genericViewFrame = NSMakeRect(
|
|
17,
|
|
NSMaxY(presetFrame) + 9,
|
|
controlWidth,
|
|
NSHeight([genericViewButton frame]));
|
|
[genericViewButton setFrame:genericViewFrame];
|
|
[presetButton setFrame:presetFrame];
|
|
|
|
headerSize = NSMakeSize((2 * 17) + controlWidth, 18 + NSMaxY(genericViewFrame));
|
|
NSView *const container = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, headerSize.width, headerSize.height)];
|
|
[container setAutoresizingMask:(NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin)];
|
|
[container addSubview:genericViewButton];
|
|
[genericViewButton release];
|
|
[container addSubview:presetButton];
|
|
[presetButton release];
|
|
|
|
window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, headerSize.width, headerSize.height)
|
|
styleMask:(NSTitledWindowMask |
|
|
NSClosableWindowMask |
|
|
NSMiniaturizableWindowMask)
|
|
backing:NSBackingStoreBuffered
|
|
defer:YES];
|
|
[window setReleasedWhenClosed:NO];
|
|
[window setDelegate:self];
|
|
[window setTitle:@"Effect"];
|
|
[[window contentView] addSubview:container];
|
|
[container release];
|
|
[self setWindow:window];
|
|
|
|
NSWindowController *const controller = [[NSWindowController alloc] initWithWindow:window];
|
|
[self addWindowController:controller];
|
|
[controller release];
|
|
[window release];
|
|
|
|
[self loadEffectUI];
|
|
if (restoreFrame)
|
|
{
|
|
[window setFrameFromString:restoreFrame];
|
|
}
|
|
else
|
|
{
|
|
NSRect const available = [[NSScreen mainScreen] visibleFrame];
|
|
NSRect frame = [window frame];
|
|
frame.origin.x = (NSWidth(available) - NSWidth(frame)) / 4;
|
|
frame.origin.y = (NSHeight(available) - NSHeight(frame)) * 3 / 4;
|
|
[window setFrame:frame display:YES animate:NO];
|
|
}
|
|
}
|
|
|
|
- (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error {
|
|
OSStatus status;
|
|
UInt32 propertySize;
|
|
|
|
BOOL const hasWrapper = [type isEqualToString:AUEffectDocumentType];
|
|
if (!hasWrapper && ![type isEqualToString:AUPresetDocumentType])
|
|
{
|
|
if (error)
|
|
{
|
|
NSString *const message = [NSString stringWithFormat:@"Unsupported document type %@", type];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
NSString *errDesc = nil;
|
|
id const desc = [NSPropertyListSerialization propertyListFromData:data
|
|
mutabilityOption:0
|
|
format:NULL
|
|
errorDescription:&errDesc];
|
|
if (!desc || ![desc isKindOfClass:[NSDictionary class]] || errDesc)
|
|
{
|
|
if (error)
|
|
{
|
|
NSString *const message = [NSString stringWithFormat:@"Error in file format (%@)", errDesc];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
if (errDesc)
|
|
[errDesc release];
|
|
return NO;
|
|
}
|
|
|
|
id const typeValue = [desc objectForKey:(hasWrapper ? ComponentTypeKey : (NSString *)CFSTR(kAUPresetTypeKey))];
|
|
id const subtypeValue = [desc objectForKey:(hasWrapper ? ComponentSubTypeKey : (NSString *)CFSTR(kAUPresetSubtypeKey))];
|
|
id const manufacturerValue = [desc objectForKey:(hasWrapper ? ComponentManufacturerKey : (NSString *)CFSTR(kAUPresetManufacturerKey))];
|
|
if (!typeValue || ![typeValue isKindOfClass:[NSNumber class]]
|
|
|| !subtypeValue || ![subtypeValue isKindOfClass:[NSNumber class]]
|
|
|| !manufacturerValue || ![manufacturerValue isKindOfClass:[NSNumber class]]
|
|
|| ([typeValue unsignedLongValue] != kAudioUnitType_Effect))
|
|
{
|
|
if (error)
|
|
{
|
|
NSString *const message = @"Error in effect description file format";
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
if (presets)
|
|
{
|
|
CFRelease(presets);
|
|
presets = NULL;
|
|
}
|
|
if (listener)
|
|
{
|
|
AUListenerDispose(listener);
|
|
listener = NULL;
|
|
}
|
|
if (view)
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:NSViewFrameDidChangeNotification
|
|
object:nil];
|
|
[view removeFromSuperview];
|
|
view = nil;
|
|
}
|
|
if (0 != effectNode)
|
|
{
|
|
view = nil;
|
|
AUGraphRemoveNode(graph, effectNode);
|
|
effectNode = 0;
|
|
}
|
|
|
|
description.componentType = [typeValue longValue];
|
|
description.componentSubType = [subtypeValue longValue];
|
|
description.componentManufacturer = [manufacturerValue longValue];
|
|
status = noErr;
|
|
status = AUGraphClearConnections(graph);
|
|
if (noErr == status)
|
|
status = AUGraphAddNode(graph, &description, &effectNode);
|
|
if (noErr == status)
|
|
status = AUGraphNodeInfo(graph, effectNode, NULL, &effectUnit);
|
|
if (noErr == status)
|
|
status = AUGraphConnectNodeInput(graph, sourceNode, 0, effectNode, 0);
|
|
if (noErr == status)
|
|
status = AUGraphConnectNodeInput(graph, effectNode, 0, outputNode, 0);
|
|
if (noErr == status)
|
|
status = AUGraphUpdate(graph, NULL);
|
|
if (noErr != status)
|
|
{
|
|
if (error)
|
|
{
|
|
NSString * const message = @"Error encountered while configuring AudioUnit graph";
|
|
NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
CFPropertyListRef const classInfo = (CFPropertyListRef)(hasWrapper ? [desc objectForKey:ClassInfoKey] : desc);
|
|
if (classInfo)
|
|
{
|
|
AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 };
|
|
status = AudioUnitSetProperty(
|
|
effectUnit,
|
|
kAudioUnitProperty_ClassInfo,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
&classInfo,
|
|
sizeof(classInfo));
|
|
if (noErr == status)
|
|
status = AUParameterListenerNotify(NULL, NULL, &change);
|
|
if (noErr != status)
|
|
{
|
|
if (error)
|
|
{
|
|
NSString * const message = @"Error configuring effect";
|
|
NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
propertySize = 0;
|
|
status = AudioUnitGetPropertyInfo(
|
|
effectUnit,
|
|
kAudioUnitProperty_ParameterList,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
&propertySize,
|
|
NULL);
|
|
if (noErr != status)
|
|
{
|
|
if (error)
|
|
{
|
|
NSString * const message = @"Error getting effect parameters";
|
|
NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
UInt32 const paramCount = propertySize / sizeof(AudioUnitParameterID);
|
|
if (0U < paramCount)
|
|
{
|
|
status = AUEventListenerCreate(
|
|
UpdateChangeCountCallback,
|
|
self,
|
|
CFRunLoopGetCurrent(),
|
|
kCFRunLoopDefaultMode,
|
|
0.05,
|
|
0.05,
|
|
&listener);
|
|
if (noErr != status)
|
|
{
|
|
if (error)
|
|
{
|
|
NSString * const message = @"Error creating AudioUnit event listener";
|
|
NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
AudioUnitParameterID *const params = (AudioUnitParameterID *)malloc(propertySize);
|
|
AudioUnitGetProperty(
|
|
effectUnit,
|
|
kAudioUnitProperty_ParameterList,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
params,
|
|
&propertySize);
|
|
if (noErr != status)
|
|
{
|
|
free(params);
|
|
if (error)
|
|
{
|
|
NSString * const message = @"Error getting effect parameters";
|
|
NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
for (UInt32 i = 0; (i < paramCount) && (noErr == status); i++)
|
|
{
|
|
AudioUnitEvent event;
|
|
event.mEventType = kAudioUnitEvent_ParameterValueChange;
|
|
event.mArgument.mParameter.mAudioUnit = effectUnit;
|
|
event.mArgument.mParameter.mParameterID = params[i];
|
|
event.mArgument.mParameter.mScope = kAudioUnitScope_Global;
|
|
event.mArgument.mParameter.mElement = 0;
|
|
status = AUEventListenerAddEventType(listener, self, &event);
|
|
}
|
|
free(params);
|
|
if (noErr != status)
|
|
{
|
|
free(params);
|
|
if (error)
|
|
{
|
|
NSString * const message = @"Error getting effect parameters";
|
|
NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
propertySize = sizeof(presets);
|
|
status = AudioUnitGetProperty(
|
|
effectUnit,
|
|
kAudioUnitProperty_FactoryPresets,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
&presets,
|
|
&propertySize);
|
|
if ((noErr != status) && presets)
|
|
{
|
|
CFRelease(presets);
|
|
presets = NULL;
|
|
}
|
|
|
|
if (hasWrapper)
|
|
{
|
|
if ([desc objectForKey:ForceGenericViewKey]
|
|
&& [[desc objectForKey:ForceGenericViewKey] respondsToSelector:@selector(boolValue)])
|
|
{
|
|
forceGenericView = [[desc objectForKey:ForceGenericViewKey] boolValue];
|
|
[genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)];
|
|
}
|
|
if ([desc objectForKey:WindowFrameKey]
|
|
&& [[desc objectForKey:WindowFrameKey] isKindOfClass:[NSString class]])
|
|
{
|
|
if (restoreFrame)
|
|
[restoreFrame release];
|
|
restoreFrame = [[NSString alloc] initWithString:[desc objectForKey:WindowFrameKey]];
|
|
}
|
|
}
|
|
|
|
[self loadEffectUI];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSData *)dataOfType:(NSString *)type error:(NSError **)error {
|
|
CFPropertyListRef classInfo;
|
|
UInt32 infoSize = sizeof(classInfo);
|
|
OSStatus const status = AudioUnitGetProperty(
|
|
effectUnit,
|
|
kAudioUnitProperty_ClassInfo,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
&classInfo,
|
|
&infoSize);
|
|
if (noErr != status)
|
|
{
|
|
if (NULL != error)
|
|
{
|
|
NSString const *message = @"Error getting effect settings";
|
|
NSError const *underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
underlying, NSUnderlyingErrorKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
return nil;
|
|
}
|
|
NSDictionary *desc = nil;
|
|
if ([type isEqualToString:AUEffectDocumentType])
|
|
{
|
|
NSNumber const *typeVal = [NSNumber numberWithUnsignedLong:description.componentType];
|
|
NSNumber const *subtypeVal = [NSNumber numberWithUnsignedLong:description.componentSubType];
|
|
NSNumber const *manufacturerVal = [NSNumber numberWithUnsignedLong:description.componentManufacturer];
|
|
NSNumber const *forceGenericViewVal = [NSNumber numberWithBool:forceGenericView];
|
|
NSString const *windowFrameVal = [window stringWithSavedFrame];
|
|
desc = [NSDictionary dictionaryWithObjectsAndKeys:typeVal, ComponentTypeKey,
|
|
subtypeVal, ComponentSubTypeKey,
|
|
manufacturerVal, ComponentManufacturerKey,
|
|
classInfo, ClassInfoKey,
|
|
forceGenericViewVal, ForceGenericViewKey,
|
|
windowFrameVal, WindowFrameKey,
|
|
nil];
|
|
}
|
|
else if ([type isEqualToString:AUPresetDocumentType])
|
|
{
|
|
desc = [NSDictionary dictionaryWithDictionary:(NSDictionary *)classInfo];
|
|
}
|
|
CFRelease(classInfo);
|
|
if (!desc)
|
|
{
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:@"Unsupported document type", NSLocalizedDescriptionKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
return nil;
|
|
}
|
|
|
|
NSString *errDesc = nil;
|
|
NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc
|
|
format:NSPropertyListXMLFormat_v1_0
|
|
errorDescription:&errDesc];
|
|
if (!data || errDesc)
|
|
{
|
|
if (error)
|
|
{
|
|
NSString *message;
|
|
if (errDesc)
|
|
message = [NSString stringWithFormat:@"Error serialising effect settings: %@", errDesc];
|
|
else
|
|
message = @"Error serialising effect settings";
|
|
NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey,
|
|
nil];
|
|
*error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info];
|
|
}
|
|
if (errDesc)
|
|
[errDesc release];
|
|
return nil;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
- (IBAction)toggleGenericView:(id)sender {
|
|
forceGenericView = (NSOnState == [sender state]);
|
|
if (view)
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self
|
|
name:NSViewFrameDidChangeNotification
|
|
object:nil];
|
|
[view removeFromSuperview];
|
|
view = nil;
|
|
}
|
|
if (0 != effectNode)
|
|
[self loadEffectUI];
|
|
}
|
|
|
|
- (IBAction)loadPreset:(id)sender {
|
|
OSStatus status;
|
|
|
|
CFIndex const idx = [sender tag];
|
|
CFIndex const total = (NULL == presets) ? 0 : CFArrayGetCount(presets);
|
|
if ((0 > idx) || (total <= idx))
|
|
{
|
|
NSAlert const *alert = [[NSAlert alloc] init];
|
|
[alert setMessageText:@"Invalid preset selected"];
|
|
[alert setInformativeText:[NSString stringWithFormat:@"Tried to select preset %ld of %ld",
|
|
(long)idx + 1,
|
|
(long)total]];
|
|
[alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
|
|
return;
|
|
}
|
|
|
|
AUPreset const *preset = (AUPreset const *)CFArrayGetValueAtIndex(presets, idx);
|
|
status = AudioUnitSetProperty(
|
|
effectUnit,
|
|
kAudioUnitProperty_PresentPreset,
|
|
kAudioUnitScope_Global,
|
|
0,
|
|
preset,
|
|
sizeof(AUPreset));
|
|
if (noErr != status)
|
|
{
|
|
NSAlert const *alert = [[NSAlert alloc] init];
|
|
[alert setMessageText:[NSString stringWithFormat:@"Error loading preset %@", preset->presetName]];
|
|
[alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while setting AudioUnit property",
|
|
(long)status]];
|
|
[alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
|
|
return;
|
|
}
|
|
|
|
AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 };
|
|
status = AUParameterListenerNotify(NULL, NULL, &change);
|
|
if (noErr != status)
|
|
{
|
|
NSAlert const *alert = [[NSAlert alloc] init];
|
|
[alert setMessageText:[NSString stringWithFormat:@"Error notifying of parameter changes for preset %@",
|
|
preset->presetName]];
|
|
[alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while sending notification",
|
|
(long)status]];
|
|
[alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
|
|
return;
|
|
}
|
|
}
|
|
|
|
- (void)viewFrameDidChange:(NSNotification *)notification {
|
|
NSRect const oldFrame = [window frame];
|
|
NSRect const desired = [window frameRectForContentRect:[[notification object] frame]];
|
|
NSRect const newFrame = NSMakeRect(
|
|
oldFrame.origin.x,
|
|
oldFrame.origin.y + oldFrame.size.height - headerSize.height- desired.size.height,
|
|
desired.size.width,
|
|
headerSize.height + desired.size.height);
|
|
[window setFrame:newFrame display:YES animate:NO];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface AUEffectUtilAppDelegate : NSObject <NSApplicationDelegate>
|
|
{
|
|
EffectInfo *effects;
|
|
|
|
IBOutlet NSMenu *newEffectMenu;
|
|
}
|
|
|
|
- (id)init;
|
|
- (void)dealloc;
|
|
|
|
- (IBAction)newEffect:(id)sender;
|
|
|
|
- (void)applicationWillFinishLaunching:(NSNotification *)notification;
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)notification;
|
|
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender;
|
|
|
|
@end
|
|
|
|
@implementation AUEffectUtilAppDelegate
|
|
|
|
- (void)appendApplicationMenu:(NSMenu *)parent {
|
|
NSMenuItem *item;
|
|
NSMenu *submenu;
|
|
NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"];
|
|
|
|
NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Application"];
|
|
item = [parent addItemWithTitle:@"Application" action:NULL keyEquivalent:@""];
|
|
[parent setSubmenu:menu forItem:item];
|
|
[menu release];
|
|
[menu setValue:@"NSAppleMenu" forKey:@"name"];
|
|
|
|
item = [menu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
|
|
[item setTarget:NSApp];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [menu addItemWithTitle:@"Services" action:NULL keyEquivalent:@""];
|
|
submenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Services"];
|
|
[menu setSubmenu:submenu forItem:item];
|
|
[submenu release];
|
|
[NSApp setServicesMenu:submenu];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [menu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] action:@selector(hide:) keyEquivalent:@"h"];
|
|
item = [menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
|
|
[item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask];
|
|
item = [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [menu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] action:@selector(terminate:) keyEquivalent:@"q"];
|
|
[item setTarget:NSApp];
|
|
}
|
|
|
|
- (void)appendFileMenu:(NSMenu *)parent {
|
|
NSMenuItem *item;
|
|
|
|
NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"File"];
|
|
item = [parent addItemWithTitle:@"File" action:NULL keyEquivalent:@""];
|
|
[parent setSubmenu:menu forItem:item];
|
|
[menu release];
|
|
|
|
item = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""];
|
|
newEffectMenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"New"];
|
|
[menu setSubmenu:newEffectMenu forItem:item];
|
|
[newEffectMenu release];
|
|
item = [menu addItemWithTitle:[NSString stringWithFormat:@"Open%C", (unichar)0x2026] action:@selector(openDocument:) keyEquivalent:@"o"];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [menu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"];
|
|
item = [menu addItemWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"];
|
|
item = [menu addItemWithTitle:[NSString stringWithFormat:@"Save As%C", (unichar)0x2026] action:@selector(saveDocumentAs:) keyEquivalent:@"S"];
|
|
item = [menu addItemWithTitle:@"Save All" action:@selector(saveAllDocuments:) keyEquivalent:@""];
|
|
item = [menu addItemWithTitle:@"Revert to Saved" action:@selector(revertDocumentToSaved:) keyEquivalent:@"u"];
|
|
}
|
|
|
|
- (void)appendEditMenu:(NSMenu *)parent {
|
|
NSMenuItem *item;
|
|
|
|
NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Edit"];
|
|
item = [parent addItemWithTitle:@"Edit" action:NULL keyEquivalent:@""];
|
|
[parent setSubmenu:menu forItem:item];
|
|
[menu release];
|
|
|
|
item = [menu addItemWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"];
|
|
item = [menu addItemWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [menu addItemWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
|
|
item = [menu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
|
|
item = [menu addItemWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
|
|
item = [menu addItemWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""];
|
|
item = [menu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
|
|
}
|
|
|
|
- (void)appendWindowMenu:(NSMenu *)parent {
|
|
NSMenuItem *item;
|
|
|
|
NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Window"];
|
|
item = [parent addItemWithTitle:@"Window" action:NULL keyEquivalent:@""];
|
|
[parent setSubmenu:menu forItem:item];
|
|
[menu release];
|
|
[NSApp setWindowsMenu:menu];
|
|
|
|
item = [menu addItemWithTitle:@"Minimize" action:@selector(performMinimize:) keyEquivalent:@"m"];
|
|
item = [menu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""];
|
|
|
|
[menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
item = [menu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""];
|
|
}
|
|
|
|
- (void)appendHelpMenu:(NSMenu *)parent {
|
|
NSMenuItem *item;
|
|
NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"];
|
|
|
|
NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Help"];
|
|
item = [parent addItemWithTitle:@"Help" action:NULL keyEquivalent:@""];
|
|
[parent setSubmenu:menu forItem:item];
|
|
[menu release];
|
|
[menu setValue:@"NSHelpMenu" forKey:@"name"];
|
|
if ([NSApp respondsToSelector:@selector(setHelpMenu:)])
|
|
[NSApp performSelector:@selector(setHelpMenu:) withObject:menu];
|
|
|
|
item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ Help", appName] action:@selector(showHelp:) keyEquivalent:@"?"];
|
|
}
|
|
|
|
- (id)init {
|
|
if (!(self = [super init]))
|
|
return nil;
|
|
effects = NULL;
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
if (effects)
|
|
free(effects);
|
|
[super dealloc];
|
|
}
|
|
|
|
- (IBAction)newEffect:(id)sender {
|
|
int const index = [sender tag];
|
|
if ((0 > index) || (0 == effects[index].component))
|
|
{
|
|
NSAlert *const alert = [[NSAlert alloc] init];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
[alert setMessageText:@"Invalid effect component"];
|
|
[alert addButtonWithTitle:@"OK"];
|
|
[alert runModal];
|
|
[alert release];
|
|
return;
|
|
}
|
|
|
|
NSNumber *const typeValue = [NSNumber numberWithUnsignedLong:effects[index].type];
|
|
NSNumber *const subtypeValue = [NSNumber numberWithUnsignedLong:effects[index].subtype];
|
|
NSNumber *const manufacturerValue = [NSNumber numberWithUnsignedLong:effects[index].manufacturer];
|
|
NSDictionary *const desc = [NSDictionary dictionaryWithObjectsAndKeys:typeValue, ComponentTypeKey,
|
|
subtypeValue, ComponentSubTypeKey,
|
|
manufacturerValue, ComponentManufacturerKey,
|
|
nil];
|
|
NSString *errDesc = nil;
|
|
NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc
|
|
format:NSPropertyListXMLFormat_v1_0
|
|
errorDescription:&errDesc];
|
|
if (!data || errDesc)
|
|
{
|
|
NSAlert *const alert = [[NSAlert alloc] init];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
[alert setMessageText:@"Error serialising properties for new effect"];
|
|
if (errDesc)
|
|
[alert setInformativeText:[errDesc autorelease]];
|
|
[alert addButtonWithTitle:@"OK"];
|
|
[alert runModal];
|
|
[alert release];
|
|
return;
|
|
}
|
|
|
|
NSError *err = nil;
|
|
AUEffectDocument *const document = [[AUEffectDocument alloc] init];
|
|
if (!document || ![document readFromData:data ofType:AUEffectDocumentType error:&err])
|
|
{
|
|
[document release];
|
|
if (err)
|
|
{
|
|
[[NSAlert alertWithError:err] runModal];
|
|
}
|
|
else
|
|
{
|
|
NSAlert *const alert = [[NSAlert alloc] init];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
[alert setMessageText:@"Error creating new effect document"];
|
|
[alert addButtonWithTitle:@"OK"];
|
|
[alert runModal];
|
|
[alert release];
|
|
}
|
|
return;
|
|
}
|
|
|
|
[document makeWindowControllers];
|
|
[document showWindows];
|
|
[[NSDocumentController sharedDocumentController] addDocument:document];
|
|
[document release];
|
|
}
|
|
|
|
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
|
|
NSMenu *const menubar = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"MainMenu"];
|
|
[NSApp setMainMenu:menubar];
|
|
[menubar release];
|
|
[self appendApplicationMenu:menubar];
|
|
[self appendFileMenu:menubar];
|
|
[self appendEditMenu:menubar];
|
|
[self appendWindowMenu:menubar];
|
|
[self appendHelpMenu:menubar];
|
|
|
|
ProcessSerialNumber const serial = { 0, kCurrentProcess };
|
|
OSStatus const status = TransformProcessType(&serial, kProcessTransformToForegroundApplication);
|
|
if (noErr != status)
|
|
{
|
|
NSLog(@"Error transforming to foreground application (%ld)", (long)status);
|
|
[NSApp terminate:self];
|
|
}
|
|
else
|
|
{
|
|
[NSApp activateIgnoringOtherApps:YES];
|
|
}
|
|
}
|
|
|
|
- (void)applicationDidFinishLaunching:(NSNotification *)notification {
|
|
ComponentDescription effectFilter = { kAudioUnitType_Effect, 0, 0, 0, 0 };
|
|
long const count = CountComponents(&effectFilter);
|
|
if (0 == count)
|
|
{
|
|
NSAlert *const alert = [[NSAlert alloc] init];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
[alert setMessageText:@"No AudioUnit effects found"];
|
|
[alert addButtonWithTitle:@"OK"];
|
|
[alert runModal];
|
|
[alert release];
|
|
}
|
|
|
|
std::vector<std::pair<Component, OSStatus> > failed;
|
|
effects = (EffectInfo *)malloc(count * sizeof(*effects));
|
|
Component effect = FindNextComponent(0, &effectFilter);
|
|
for (long i = 0; (i < count) && (effect != 0); i++, effect = FindNextComponent(effect, &effectFilter))
|
|
{
|
|
ComponentDescription effectDesc;
|
|
Handle const nameHandle = NewHandle(4);
|
|
OSStatus const err = GetComponentInfo(effect, &effectDesc, nameHandle, NULL, NULL);
|
|
if (noErr == err)
|
|
{
|
|
effects[i].component = effect;
|
|
effects[i].type = effectDesc.componentType;
|
|
effects[i].subtype = effectDesc.componentSubType;
|
|
effects[i].manufacturer = effectDesc.componentManufacturer;
|
|
HLock(nameHandle);
|
|
CFStringRef const name = CFStringCreateWithPascalString(
|
|
NULL,
|
|
(unsigned char const *)*nameHandle,
|
|
kCFStringEncodingMacRoman);
|
|
HUnlock(nameHandle);
|
|
NSMenuItem *const item = [newEffectMenu addItemWithTitle:(NSString *)name
|
|
action:@selector(newEffect:)
|
|
keyEquivalent:@""];
|
|
[item setTag:i];
|
|
[item setTarget:self];
|
|
CFRelease(name);
|
|
}
|
|
else
|
|
{
|
|
effects[i].component = 0;
|
|
failed.push_back(std::make_pair(effect, err));
|
|
}
|
|
DisposeHandle(nameHandle);
|
|
}
|
|
|
|
if (!failed.empty())
|
|
{
|
|
NSString *const message = [NSString stringWithFormat:@"Failed to get info for %lu effect%s",
|
|
(unsigned long)failed.size(),
|
|
((1U == failed.size()) ? "" : "s")];
|
|
NSMutableString *const detail = [NSMutableString stringWithCapacity:(16 * failed.size())];
|
|
std::vector<std::pair<Component, OSStatus> >::const_iterator it = failed.begin();
|
|
[detail appendFormat:@"%lu (%ld)", (unsigned long)it->first, (long)it->second];
|
|
++it;
|
|
while (failed.end() != it)
|
|
{
|
|
[detail appendFormat:@", %lu (%ld)", (unsigned long)it->first, (long)it->second];
|
|
++it;
|
|
}
|
|
NSAlert *const alert = [[NSAlert alloc] init];
|
|
[alert setAlertStyle:NSWarningAlertStyle];
|
|
[alert setMessageText:message];
|
|
[alert setInformativeText:[NSString stringWithString:detail]];
|
|
[alert addButtonWithTitle:@"OK"];
|
|
[alert runModal];
|
|
[alert release];
|
|
}
|
|
}
|
|
|
|
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender {
|
|
return NO;
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
NSAutoreleasePool *pool;
|
|
|
|
// Initialise NSApplication
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
[NSApplication sharedApplication];
|
|
AUEffectUtilAppDelegate *const delegate = [[AUEffectUtilAppDelegate alloc] init];
|
|
[[NSApplication sharedApplication] setDelegate:delegate];
|
|
[pool release];
|
|
|
|
// Let's go!
|
|
pool = [[NSAutoreleasePool alloc] init];
|
|
[NSApp run];
|
|
[delegate release];
|
|
[pool release];
|
|
return 0;
|
|
}
|