diff --git a/scripts/src/main.lua b/scripts/src/main.lua index ca3ac2d4507..c73f3f450e6 100644 --- a/scripts/src/main.lua +++ b/scripts/src/main.lua @@ -86,7 +86,7 @@ function mainProject(_target, _subtarget) linkoptions { "-sectcreate __TEXT __info_plist " .. GEN_DIR .. "/resource/" .. _target .. "-Info.plist" } - custombuildtask { + custombuildtask { { MAME_DIR .. "src/version.c" , GEN_DIR .. "/resource/" .. _target .. "-Info.plist", { MAME_DIR .. "src/build/verinfo.py" }, {"@echo Emitting " .. _target .. "-Info.plist" .. "...", "python $(1) -p -b " .. _target .. " $(<) > $(@)" }}, } dependency { diff --git a/scripts/src/osd/sdl.lua b/scripts/src/osd/sdl.lua index 4dafe5462c6..202e0f8571d 100644 --- a/scripts/src/osd/sdl.lua +++ b/scripts/src/osd/sdl.lua @@ -76,7 +76,7 @@ function maintargetosdoptions(_target) "bsd", } end - + configuration { "mingw*" or "vs*" } targetprefix "sdl" @@ -366,11 +366,11 @@ project ("ocore_" .. _OPTIONS["osd"]) } removeflags { - "SingleOutputDir", + "SingleOutputDir", } dofile("sdl_cfg.lua") - + includedirs { MAME_DIR .. "src/emu", MAME_DIR .. "src/osd", @@ -428,7 +428,7 @@ if _OPTIONS["with-tools"] then MAME_DIR .. "src/osd", MAME_DIR .. "src/lib/util", } - + targetdir(MAME_DIR) links { @@ -440,8 +440,8 @@ if _OPTIONS["with-tools"] then MAME_DIR .. "src/osd/sdl/testkeys.c", } - if _OPTIONS["targetos"]=="windows" then - if _OPTIONS["SDL_LIBVER"]=="sdl2" then + if _OPTIONS["targetos"] == "windows" then + if _OPTIONS["SDL_LIBVER"] == "sdl2" then links { "SDL2.dll", } @@ -456,10 +456,49 @@ if _OPTIONS["with-tools"] then files { MAME_DIR .. "src/osd/sdl/main.c", } - elseif _OPTIONS["targetos"]=="macosx" and _OPTIONS["SDL_LIBVER"]=="sdl" then + elseif _OPTIONS["targetos"] == "macosx" and _OPTIONS["SDL_LIBVER"] == "sdl" then -- SDLMain_tmpl isn't necessary for SDL2 files { MAME_DIR .. "src/osd/sdl/SDLMain_tmpl.m", } end end + + +-------------------------------------------------- +-- aueffectutil +-------------------------------------------------- + +if _OPTIONS["targetos"] == "macosx" and _OPTIONS["with-tools"] then + project("aueffectutil") + uuid ("3db8316d-fad7-4f5b-b46a-99373c91550e") + kind "ConsoleApp" + + options { + "ForceCPP", + } + + dofile("sdl_cfg.lua") + + targetdir(MAME_DIR) + + linkoptions { + "-sectcreate __TEXT __info_plist " .. MAME_DIR .. "src/osd/sdl/aueffectutil-Info.plist", + } + + dependency { + { "aueffectutil", MAME_DIR .. "src/osd/sdl/aueffectutil-Info.plist", true }, + } + + links { + "AudioUnit.framework", + "AudioToolbox.framework", + "CoreAudio.framework", + "CoreAudioKit.framework", + "CoreServices.framework", + } + + files { + MAME_DIR .. "src/osd/sdl/aueffectutil.m", + } +end diff --git a/src/osd/modules/debugger/osx/debugosx.h b/src/osd/modules/debugger/osx/debugosx.h index 016ea752e34..3fba83a277d 100644 --- a/src/osd/modules/debugger/osx/debugosx.h +++ b/src/osd/modules/debugger/osx/debugosx.h @@ -24,14 +24,6 @@ // workarounds for 10.6 warnings #ifdef MAC_OS_X_VERSION_MAX_ALLOWED -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 - -typedef int NSInteger; -typedef unsigned NSUInteger; -typedef float CGFloat; - -#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1050 - #if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 @protocol NSWindowDelegate diff --git a/src/osd/sdl/aueffectutil-Info.plist b/src/osd/sdl/aueffectutil-Info.plist new file mode 100644 index 00000000000..e5094157c33 --- /dev/null +++ b/src/osd/sdl/aueffectutil-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDisplayName + AUEffectUtil + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + aueffect + + CFBundleTypeName + AUEffect + CFBundleTypeOSTypes + + CFBundleTypeRole + Editor + NSDocumentClass + AUEffectDocument + + + CFBundleTypeExtensions + + aupreset + + CFBundleTypeName + AudioUnit Preset + CFBundleTypeOSTypes + + CFBundleTypeRole + Editor + NSDocumentClass + AUEffectDocument + + + CFBundleIdentifier + org.mamedev.aueffectutil + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + AUEffectUtil + + diff --git a/src/osd/sdl/aueffectutil.m b/src/osd/sdl/aueffectutil.m new file mode 100644 index 00000000000..322c5d3dc60 --- /dev/null +++ b/src/osd/sdl/aueffectutil.m @@ -0,0 +1,665 @@ +#import +#import +#import +#import +#import +#import +#import +#import + +#include +#include + +#include + + +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"; + + +@interface AUEffectDocument : NSDocument +{ + IBOutlet NSWindow *window; + IBOutlet NSScrollView *scroller; + + 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; + +@end + +@implementation AUEffectDocument + +- (void)loadEffectUI { + if ((0 == effectNode) || (nil == window)) + return; + + OSStatus status; + UInt32 uiDescSize; + AudioUnitCocoaViewInfo *viewInfo; + NSView *view = nil; + 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 ((NULL != viewClass) + && [viewClass conformsToProtocol:@protocol(AUCocoaUIBase)] + && [viewClass instancesRespondToSelector:@selector(uiViewForAudioUnit:withSize:)]) + { + id const factory = [[viewClass alloc] init]; + view = [factory uiViewForAudioUnit:effectUnit + withSize:[[scroller contentView] bounds].size]; + [factory release]; + } + CFRelease(viewInfo->mCocoaAUViewBundleLocation); + for (UInt32 i = 0; i < uiClassCount; i++) + CFRelease(viewInfo->mCocoaAUViewClass[i]); + } + free(viewInfo); + } + if (nil == view) + { + view = [[[AUGenericView alloc] initWithAudioUnit:effectUnit] autorelease]; + [(AUGenericView *)view setShowsExpertParameters:YES]; + } + + NSRect const frame = [view frame]; + NSSize const desired = [NSScrollView frameSizeForContentSize:frame.size + hasHorizontalScroller:[scroller hasHorizontalScroller] + hasVerticalScroller:[scroller hasVerticalScroller] + borderType:[scroller borderType]]; + NSSize const min = [window contentMinSize]; + NSRect const current = [scroller frame]; + [window setContentSize:NSMakeSize(MAX(min.width, desired.width), MAX(min.height, desired.height))]; + [view setFrameSize:[scroller contentSize]]; + [scroller setDocumentView:view]; +} + +- (id)init { + if (!(self = [super init])) return 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 (NULL != graph) + { + AUGraphClose(graph); + DisposeAUGraph(graph); + } + + [super dealloc]; +} + +- (void)makeWindowControllers { + window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 400, 300) + styleMask:(NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask | + NSResizableWindowMask) + backing:NSBackingStoreBuffered + defer:YES]; + [window setContentMinSize:NSMakeSize(400, 300)]; + [window setReleasedWhenClosed:NO]; + [window setDelegate:self]; + [window setTitle:@"Effect"]; + [self setWindow:window]; + + NSWindowController *const controller = [[NSWindowController alloc] initWithWindow:window]; + [self addWindowController:controller]; + [controller release]; + [window release]; + + scroller = [[NSScrollView alloc] initWithFrame:[[window contentView] bounds]]; + [scroller setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; + [scroller setHasHorizontalScroller:YES]; + [scroller setHasVerticalScroller:YES]; + [scroller setAutohidesScrollers:NO]; + [scroller setBorderType:NSNoBorder]; + [[window contentView] addSubview:scroller]; + [scroller release]; + + [self loadEffectUI]; +} + +- (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error { + OSStatus status; + + BOOL const hasWrapper = [type isEqualToString:AUEffectDocumentType]; + if (!hasWrapper && ![type isEqualToString:AUPresetDocumentType]) + { + if (NULL != 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 ((nil == desc) || ![desc isKindOfClass:[NSDictionary class]] || (nil != errDesc)) + { + if (NULL != 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 (nil != errDesc) + [errDesc release]; + return NO; + } + + id const typeValue = [desc objectForKey:(hasWrapper ? ComponentTypeKey : @"type")]; + id const subtypeValue = [desc objectForKey:(hasWrapper ? ComponentSubTypeKey : @"subtype")]; + id const manufacturerValue = [desc objectForKey:(hasWrapper ? ComponentManufacturerKey : @"manufacturer")]; + if ((nil == typeValue) || ![typeValue isKindOfClass:[NSNumber class]] + || (nil == subtypeValue) || ![subtypeValue isKindOfClass:[NSNumber class]] + || (nil == manufacturerValue) || ![manufacturerValue isKindOfClass:[NSNumber class]] + || ([typeValue unsignedLongValue] != kAudioUnitType_Effect)) + { + if (NULL != 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 (0 != effectNode) + { + [[scroller documentView] removeFromSuperview]; + 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 (NULL != 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 (NULL != 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 (NULL != 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; + } + } + + [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) + { + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:@"Error getting effect settings", NSLocalizedDescriptionKey, + [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil], NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return nil; + } + NSDictionary *const desc = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedLong:description.componentType], ComponentTypeKey, + [NSNumber numberWithUnsignedLong:description.componentSubType], ComponentSubTypeKey, + [NSNumber numberWithUnsignedLong:description.componentManufacturer], ComponentManufacturerKey, + classInfo, ClassInfoKey, + nil]; + CFRelease(classInfo); + NSString *errDesc = nil; + NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc + format:NSPropertyListXMLFormat_v1_0 + errorDescription:&errDesc]; + if ((nil == data) || (nil != errDesc)) + { + if (NULL != error) + { + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:@"Error serialising effect settings", NSLocalizedDescriptionKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + if (nil != errDesc) [errDesc release]; + return nil; + } + return data; +} + +@end + + +@interface AUEffectUtilAppDelegate : NSObject +{ + 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:@"Open…" action:@selector(openDocument:) keyEquivalent:@"o"]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:NSLocalizedString(@"Close", nil) action:@selector(performClose:) keyEquivalent:@"w"]; + item = [menu addItemWithTitle:NSLocalizedString(@"Save", nil) action:@selector(saveDocument:) keyEquivalent:@"s"]; + item = [menu addItemWithTitle:NSLocalizedString(@"Save As…", nil) action:@selector(saveDocumentAs:) keyEquivalent:@"S"]; + item = [menu addItemWithTitle:NSLocalizedString(@"Revert to Saved", nil) action:@selector(revertDocumentToSaved:) keyEquivalent:@""]; +} + +- (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]; + [NSApp setHelpMenu: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 ((nil == data) || (nil != errDesc)) + { + NSAlert *const alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:@"Error serialising properties for new effect"]; + if (nil != errDesc) [alert setInformativeText:[errDesc autorelease]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + [alert release]; + return; + } + + NSError *err = nil; + AUEffectDocument *const document = [[AUEffectDocument alloc] init]; + if ((nil == document) || ![document readFromData:data ofType:AUEffectDocumentType error:&err]) + { + [document release]; + if (nil != 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 > 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 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 >::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]; + [NSApp setDelegate:delegate]; + [pool release]; + + // Let's go! + pool = [[NSAutoreleasePool alloc] init]; + [NSApp run]; + [delegate release]; + [pool release]; + return 0; +}