From c97c6735dfe1afcc1b9a2e6a58673c70bcaa1214 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Fri, 28 Mar 2025 15:25:23 -0700 Subject: [PATCH] MIDI: Add unstable configuration for AU players It's regarded as unstable as some plugins randomly decide to overflow the main thread queue and crash the player on the main thread check, but only sometimes, and not always. Signed-off-by: Christopher Snowhill --- Plugins/MIDI/MIDI/AUPlayer.h | 3 + Plugins/MIDI/MIDI/AUPlayer.mm | 9 + Plugins/MIDI/MIDI/MIDIDecoder.mm | 8 + Preferences/Preferences/AUPlayerView.h | 57 +++++ Preferences/Preferences/AUPlayerView.mm | 222 ++++++++++++++++++ .../Preferences/Base.lproj/Preferences.xib | 17 +- Preferences/Preferences/MIDIPane.h | 5 + Preferences/Preferences/MIDIPane.mm | 128 ++++++++++ .../MIDIPluginBehaviorArrayController.m | 22 +- .../Preferences.xcodeproj/project.pbxproj | 14 +- .../Preferences/en.lproj/Preferences.strings | 3 + .../Preferences/es.lproj/Preferences.strings | 3 + 12 files changed, 475 insertions(+), 16 deletions(-) create mode 100644 Preferences/Preferences/AUPlayerView.h create mode 100644 Preferences/Preferences/AUPlayerView.mm create mode 100644 Preferences/Preferences/MIDIPane.mm diff --git a/Plugins/MIDI/MIDI/AUPlayer.h b/Plugins/MIDI/MIDI/AUPlayer.h index 75e0ba97c..23f5c9f89 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.h +++ b/Plugins/MIDI/MIDI/AUPlayer.h @@ -28,6 +28,7 @@ class AUPlayer : public MIDIPlayer { static void enumComponents(callback cbEnum); void setComponent(OSType uSubType, OSType uManufacturer); + void setPreset(NSDictionary *preset); protected: virtual unsigned int send_event_needs_time(); @@ -61,6 +62,8 @@ class AUPlayer : public MIDIPlayer { OSType componentSubType, componentManufacturer; BOOL needsInput; + + NSDictionary *preset; }; #endif diff --git a/Plugins/MIDI/MIDI/AUPlayer.mm b/Plugins/MIDI/MIDI/AUPlayer.mm index aaedd1f9a..6c20ee1c6 100644 --- a/Plugins/MIDI/MIDI/AUPlayer.mm +++ b/Plugins/MIDI/MIDI/AUPlayer.mm @@ -208,6 +208,10 @@ void AUPlayer::setSoundFont(const char *in) { } } +void AUPlayer::setPreset(NSDictionary *preset) { + this->preset = preset; +} + /*void AUPlayer::setFileSoundFont( const char * in ) { sFileSoundFontName = in; @@ -334,6 +338,11 @@ bool AUPlayer::startup() { value = 1; AudioUnitSetProperty(samplerUnit[i], kMusicDeviceProperty_StreamFromDisk, kAudioUnitScope_Global, 0, &value, size); + if(preset) { + CFDictionaryRef cdict = (__bridge CFDictionaryRef)preset; + AudioUnitSetProperty(samplerUnit[i], kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &cdict, sizeof(cdict)); + } + error = AudioUnitInitialize(samplerUnit[i]); if(error != noErr) diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index 1c553121b..02c8b594e 100644 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -270,6 +270,14 @@ static OSType getOSType(const char *in_) { soundFontsAssigned = YES; } + NSDictionary *midiPluginSettings = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"midiPluginSettings"]; + if(midiPluginSettings) { + NSDictionary *theSettings = [midiPluginSettings objectForKey:plugin]; + if(theSettings) { + auplayer->setPreset(theSettings); + } + } + player = auplayer; } } diff --git a/Preferences/Preferences/AUPlayerView.h b/Preferences/Preferences/AUPlayerView.h new file mode 100644 index 000000000..09270d1eb --- /dev/null +++ b/Preferences/Preferences/AUPlayerView.h @@ -0,0 +1,57 @@ +// +// AUPlayerView.h +// MIDI +// +// Created by Christopher Snowhill on 1/29/16. +// Copyright © 2016-2022 Christopher Snowhill. All rights reserved. +// + +#ifndef __AUPlayerView_h__ +#define __AUPlayerView_h__ + +#include +#include + +#include +#include +#include +#include + +@class NSWindowDeleter; + +class AUPluginUI { + public: + AUPluginUI(NSString *name, AudioUnit& sampler); + ~AUPluginUI(); + + bool window_opened() const; + + private: + AudioUnit& au; + NSString* name; + int prefheight; + int prefwidth; + + bool opened; + bool mapped; + bool resizable; + int min_width; + int min_height; + int req_width; + int req_height; + int alo_width; + int alo_height; + + /* Cocoa */ + + NSWindowDeleter* cocoa_window; + NSView* au_view; + NSRect last_au_frame; + + bool test_cocoa_view_support(); + int create_cocoa_view(); + + bool plugin_class_valid(Class pluginClass); +}; + +#endif diff --git a/Preferences/Preferences/AUPlayerView.mm b/Preferences/Preferences/AUPlayerView.mm new file mode 100644 index 000000000..03ed6c2a2 --- /dev/null +++ b/Preferences/Preferences/AUPlayerView.mm @@ -0,0 +1,222 @@ +// +// AUPlayerView.mm +// MIDI +// +// Created by Christopher Snowhill on 1/29/16. +// Copyright © 2016-2022 Christopher Snowhill. All rights reserved. +// + +#import +#import + +#import "AUPlayerView.h" + +@interface NSWindowDeleter : NSWindow { + AUPluginUI *parent; +} + +- (void)setParentObject:(AUPluginUI *)object; +- (void)dealloc; +@end + +@implementation NSWindowDeleter + +- (void)setParentObject:(AUPluginUI *)object { + parent = object; +} + +- (void)dealloc { + delete parent; +} +@end + +AUPluginUI::AUPluginUI(NSString *_name, AudioUnit &_au) +: name(_name), au(_au), mapped(false), resizable(false), min_width(0), min_height(0), req_width(0), req_height(0), alo_width(0), alo_height(0) { + opened = false; + cocoa_window = nil; + au_view = nil; + + if(test_cocoa_view_support()) { + create_cocoa_view(); + } + + if(au_view) { + cocoa_window = [[NSWindowDeleter alloc] initWithContentRect:NSMakeRect(0, 0, req_width, req_height) + styleMask:(NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable) + backing:NSBackingStoreBuffered + defer:NO]; + + [cocoa_window setParentObject:this]; + + [cocoa_window setAutodisplay:YES]; + [cocoa_window setTitle:@"AU Plug-in"]; + [cocoa_window setOneShot:YES]; + + [cocoa_window setContentView:au_view]; + + NSDictionary *midiPluginSettings = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"midiPluginSettings"]; + if(midiPluginSettings) { + NSDictionary *theSettings = [midiPluginSettings objectForKey:name]; + if(theSettings) { + CFDictionaryRef cdict = (__bridge CFDictionaryRef)theSettings; + AudioUnitSetProperty(au, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, + &cdict, + sizeof(cdict)); + } + } + + [cocoa_window orderFront:cocoa_window]; + + opened = true; + } +} + +AUPluginUI::~AUPluginUI() { + if(opened) { + NSMutableDictionary *midiPluginSettings = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"midiPluginSettings"] mutableCopy]; + if(!midiPluginSettings) { + midiPluginSettings = [[NSMutableDictionary alloc] init]; + } + CFDictionaryRef outClassInfo = nil; + UInt32 objectSize = sizeof(outClassInfo); + OSErr err = AudioUnitGetProperty(au, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, + &outClassInfo, + &objectSize); + if(err == noErr) { + NSDictionary *dict = (__bridge NSDictionary *)outClassInfo; + [midiPluginSettings setObject:dict forKey:name]; + [[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:midiPluginSettings forKey:@"midiPluginSettings"]; + dict = nil; + CFRelease(outClassInfo); + } + } + + AudioUnitUninitialize(au); + AudioComponentInstanceDispose(au); + au = NULL; +} + +bool AUPluginUI::test_cocoa_view_support() { + UInt32 dataSize = 0; + Boolean isWritable = 0; + OSStatus err = AudioUnitGetPropertyInfo(au, + kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, + 0, &dataSize, &isWritable); + + return dataSize > 0 && err == noErr; +} + +bool AUPluginUI::plugin_class_valid(Class pluginClass) { + if([pluginClass conformsToProtocol:@protocol(AUCocoaUIBase)]) { + if([pluginClass instancesRespondToSelector:@selector(interfaceVersion)] && + [pluginClass instancesRespondToSelector:@selector(uiViewForAudioUnit:withSize:)]) { + return true; + } + } + return false; +} + +int AUPluginUI::create_cocoa_view() { + bool wasAbleToLoadCustomView = false; + AudioUnitCocoaViewInfo *cocoaViewInfo = NULL; + UInt32 numberOfClasses = 0; + UInt32 dataSize; + Boolean isWritable; + NSString *factoryClassName = 0; + NSURL *CocoaViewBundlePath = NULL; + + OSStatus result = AudioUnitGetPropertyInfo(au, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + &dataSize, + &isWritable); + + numberOfClasses = (dataSize - sizeof(CFURLRef)) / sizeof(CFStringRef); + + // Does view have custom Cocoa UI? + + if((result == noErr) && (numberOfClasses > 0)) { + cocoaViewInfo = (AudioUnitCocoaViewInfo *)malloc(dataSize); + + if(AudioUnitGetProperty(au, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + cocoaViewInfo, + &dataSize) == noErr) { + CocoaViewBundlePath = (__bridge NSURL *)cocoaViewInfo->mCocoaAUViewBundleLocation; + + // we only take the first view in this example. + factoryClassName = (__bridge NSString *)cocoaViewInfo->mCocoaAUViewClass[0]; + } else { + if(cocoaViewInfo != NULL) { + free(cocoaViewInfo); + cocoaViewInfo = NULL; + } + } + } + + // [A] Show custom UI if view has it + + if(CocoaViewBundlePath && factoryClassName) { + NSBundle *viewBundle = [NSBundle bundleWithPath:[CocoaViewBundlePath path]]; + + if(viewBundle == NULL) { + return -1; + } else { + Class factoryClass = [viewBundle classNamed:factoryClassName]; + if(!factoryClass) { + return -1; + } + + // make sure 'factoryClass' implements the AUCocoaUIBase protocol + if(!plugin_class_valid(factoryClass)) { + return -1; + } + // make a factory + id factory = [[factoryClass alloc] init]; + if(factory == NULL) { + return -1; + } + + // make a view + au_view = [factory uiViewForAudioUnit:au withSize:NSZeroSize]; + + // cleanup + if(cocoaViewInfo) { + UInt32 i; + for(i = 0; i < numberOfClasses; i++) + CFRelease(cocoaViewInfo->mCocoaAUViewClass[i]); + + free(cocoaViewInfo); + } + wasAbleToLoadCustomView = true; + } + } + + if(!wasAbleToLoadCustomView) { + // load generic Cocoa view + au_view = [[AUGenericView alloc] initWithAudioUnit:au]; + [(AUGenericView *)au_view setShowsExpertParameters:1]; + } + + // Get the initial size of the new AU View's frame + NSRect frame = [au_view frame]; + min_width = req_width = CGRectGetWidth(NSRectToCGRect(frame)); + min_height = req_height = CGRectGetHeight(NSRectToCGRect(frame)); + resizable = [au_view autoresizingMask]; + + return 0; +} + +bool AUPluginUI::window_opened() const { + return opened; +} diff --git a/Preferences/Preferences/Base.lproj/Preferences.xib b/Preferences/Preferences/Base.lproj/Preferences.xib index 2268d323c..c602a870b 100644 --- a/Preferences/Preferences/Base.lproj/Preferences.xib +++ b/Preferences/Preferences/Base.lproj/Preferences.xib @@ -449,6 +449,9 @@ + + + @@ -879,7 +882,7 @@ - + @@ -893,12 +896,24 @@ + + diff --git a/Preferences/Preferences/MIDIPane.h b/Preferences/Preferences/MIDIPane.h index 57e2b6341..936f44629 100644 --- a/Preferences/Preferences/MIDIPane.h +++ b/Preferences/Preferences/MIDIPane.h @@ -10,9 +10,14 @@ #import @interface MIDIPane : GeneralPreferencePane { + IBOutlet NSArrayController *midiPluginBehaviorArrayController; + IBOutlet NSButton *midiPluginSetupButton; + IBOutlet NSPopUpButton *midiPluginControl; IBOutlet NSPopUpButton *midiFlavorControl; } +- (IBAction)setupPlugin:(id)sender; + - (IBAction)setSoundFont:(id)sender; @end diff --git a/Preferences/Preferences/MIDIPane.mm b/Preferences/Preferences/MIDIPane.mm new file mode 100644 index 000000000..5c53fb376 --- /dev/null +++ b/Preferences/Preferences/MIDIPane.mm @@ -0,0 +1,128 @@ +// +// MIDIPane.m +// General +// +// Created by Christopher Snowhill on 10/15/13. +// +// + +#import "MIDIPane.h" + +#import "SandboxBroker.h" + +#import "AppController.h" + +#import "AUPlayerView.h" + +@implementation MIDIPane { + NSTimer *startupTimer; +} + +- (void)awakeFromNib { + __block MIDIPane *_self = self; + startupTimer = [NSTimer timerWithTimeInterval:0.2 repeats:YES block:^(NSTimer * _Nonnull timer) { + NSUInteger selectedItem = [_self->midiPluginControl indexOfSelectedItem]; + NSArray *arrangedObjects = [_self->midiPluginBehaviorArrayController arrangedObjects]; + if([arrangedObjects count] > selectedItem) { + NSDictionary *selectedInfo = arrangedObjects[selectedItem]; + [_self->midiPluginSetupButton setEnabled:[[selectedInfo objectForKey:@"configurable"] boolValue]]; + [timer invalidate]; + } + }]; + [[NSRunLoop mainRunLoop] addTimer:startupTimer forMode:NSRunLoopCommonModes]; +} + +- (void)dealloc { + [startupTimer invalidate]; + startupTimer = nil; +} + +- (NSString *)title { + return NSLocalizedPrefString(@"Synthesis"); +} + +- (NSImage *)icon { + if(@available(macOS 11.0, *)) + return [NSImage imageWithSystemSymbolName:@"pianokeys" accessibilityDescription:nil]; + return [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForImageResource:@"midi"]]; +} + +- (IBAction)setSoundFont:(id)sender { + NSArray *fileTypes = @[@"sf2", @"sf2pack", @"sflist", @"sf3"]; + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection:NO]; + [panel setCanChooseDirectories:NO]; + [panel setCanChooseFiles:YES]; + [panel setFloatingPanel:YES]; + [panel setAllowedFileTypes:fileTypes]; + NSString *oldPath = [[NSUserDefaults standardUserDefaults] stringForKey:@"soundFontPath"]; + if(oldPath != nil) + [panel setDirectoryURL:[NSURL fileURLWithPath:oldPath]]; + NSInteger result = [panel runModal]; + if(result == NSModalResponseOK) { + [[NSUserDefaults standardUserDefaults] setValue:[[panel URL] path] forKey:@"soundFontPath"]; + + id sandboxBrokerClass = NSClassFromString(@"SandboxBroker"); + NSURL *pathUrl = [panel URL]; + if(![[sandboxBrokerClass sharedSandboxBroker] areAllPathsSafe:@[pathUrl]]) { + id appControllerClass = NSClassFromString(@"AppController"); + [appControllerClass globalShowPathSuggester]; + } + } +} + +- (IBAction)setMidiPlugin:(id)sender { + NSUInteger selectedItem = [midiPluginControl indexOfSelectedItem]; + NSDictionary *selectedInfo = [midiPluginBehaviorArrayController arrangedObjects][selectedItem]; + [midiPluginSetupButton setEnabled:[[selectedInfo objectForKey:@"configurable"] boolValue]]; +} + +static OSType getOSType(const char *in_) { + const unsigned char *in = (const unsigned char *)in_; + OSType v = (in[0] << 24) + (in[1] << 16) + (in[2] << 8) + in[3]; + return v; +} + +- (IBAction)setupPlugin:(id)sender { + NSUInteger selectedItem = [midiPluginControl indexOfSelectedItem]; + NSDictionary *selectedInfo = [midiPluginBehaviorArrayController arrangedObjects][selectedItem]; + if(![[selectedInfo objectForKey:@"configurable"] boolValue]) + return; + + NSString *plugin = [selectedInfo objectForKey:@"preference"]; + const char *cplugin = [plugin UTF8String]; + + AudioComponentDescription cd = { 0 }; + + cd.componentType = kAudioUnitType_MusicDevice; + cd.componentSubType = getOSType(cplugin); + cd.componentManufacturer = getOSType(cplugin + 4); + + AudioComponent comp = NULL; + + comp = AudioComponentFindNext(comp, &cd); + + if(!comp) + return; + + AudioUnit au = NULL; + + OSStatus error; + + error = AudioComponentInstanceNew(comp, &au); + + if(error != noErr) + return; + + /*error = AudioUnitInitialize(au); + if(error != noErr) + return;*/ + + AUPluginUI * pluginUI = new AUPluginUI(plugin, au); + + if(!pluginUI->window_opened()) { + delete pluginUI; + } +} + +@end diff --git a/Preferences/Preferences/MIDIPluginBehaviorArrayController.m b/Preferences/Preferences/MIDIPluginBehaviorArrayController.m index fdc7186eb..10c52ef59 100644 --- a/Preferences/Preferences/MIDIPluginBehaviorArrayController.m +++ b/Preferences/Preferences/MIDIPluginBehaviorArrayController.m @@ -60,25 +60,25 @@ static void enumCallback(void *context, OSType uSubType, OSType uManufacturer, c pref[8] = '\0'; [pself addObject: - [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String:name], @"name", - [NSString stringWithUTF8String:pref], @"preference", nil]]; + @{@"name": [NSString stringWithUTF8String:name], + @"preference": [NSString stringWithUTF8String:pref], + @"configurable": @YES}]; } @implementation MIDIPluginBehaviorArrayController - (void)awakeFromNib { [self removeObjects:[self arrangedObjects]]; - [self addObject:@{@"name": @"BASSMIDI", @"preference": @"BASSMIDI"}]; + [self addObject:@{@"name": @"BASSMIDI", @"preference": @"BASSMIDI", @"configurable": @NO}]; - [self addObject:@{@"name": @"DMX Generic", @"preference": @"DOOM0000"}]; - [self addObject:@{@"name": @"DMX Doom 1", @"preference": @"DOOM0001"}]; - [self addObject:@{@"name": @"DMX Doom 2", @"preference": @"DOOM0002"}]; - [self addObject:@{@"name": @"DMX Raptor", @"preference": @"DOOM0003"}]; - [self addObject:@{@"name": @"DMX Strife", @"preference": @"DOOM0004"}]; - [self addObject:@{@"name": @"DMXOPL", @"preference": @"DOOM0005"}]; + [self addObject:@{@"name": @"DMX Generic", @"preference": @"DOOM0000", @"configurable": @NO}]; + [self addObject:@{@"name": @"DMX Doom 1", @"preference": @"DOOM0001", @"configurable": @NO}]; + [self addObject:@{@"name": @"DMX Doom 2", @"preference": @"DOOM0002", @"configurable": @NO}]; + [self addObject:@{@"name": @"DMX Raptor", @"preference": @"DOOM0003", @"configurable": @NO}]; + [self addObject:@{@"name": @"DMX Strife", @"preference": @"DOOM0004", @"configurable": @NO}]; + [self addObject:@{@"name": @"DMXOPL", @"preference": @"DOOM0005", @"configurable": @NO}]; - [self addObject:@{@"name": @"OPL3Windows", @"preference": @"OPL3W000"}]; + [self addObject:@{@"name": @"OPL3Windows", @"preference": @"OPL3W000", @"configurable": @NO}]; enumComponents(enumCallback, (__bridge void *)(self)); } diff --git a/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj b/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj index db97b9466..f7b1c1f71 100644 --- a/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj +++ b/Preferences/Preferences/Preferences.xcodeproj/project.pbxproj @@ -32,10 +32,11 @@ 8391EA06286F9D3200A37593 /* PathSuggester.xib in Resources */ = {isa = PBXBuildFile; fileRef = 83DAD9F0286EDBCD000FAA9A /* PathSuggester.xib */; }; 83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */; }; 83AC573A2861A54D009D6F50 /* PathSuggester.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AC57382861A54D009D6F50 /* PathSuggester.m */; }; - 83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; }; + 83B06729180D85B8008E3612 /* MIDIPane.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.mm */; }; 83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; }; 83B8FE242D59CDA7005854C1 /* rubberband.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83B8FE232D59CDA7005854C1 /* rubberband.pdf */; }; 83B8FE292D59D059005854C1 /* RubberbandPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FE282D59D059005854C1 /* RubberbandPane.m */; }; + 83E83F7E2D96780E0054365C /* AUPlayerView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E83F7D2D96780E0054365C /* AUPlayerView.mm */; }; 83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; }; 83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E651810DD3A00CEF538 /* appearance@2x.png */; }; 83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E681810DD3A00CEF538 /* midi@2x.png */; }; @@ -136,7 +137,7 @@ 83AC57382861A54D009D6F50 /* PathSuggester.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PathSuggester.m; sourceTree = ""; }; 83AC573E2861B77E009D6F50 /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../Utils/SandboxBroker.h; sourceTree = ""; }; 83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = ""; }; - 83B06728180D85B8008E3612 /* MIDIPane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPane.m; sourceTree = ""; }; + 83B06728180D85B8008E3612 /* MIDIPane.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIPane.mm; sourceTree = ""; }; 83B0672A180D8B39008E3612 /* midi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = midi.png; path = Icons/midi.png; sourceTree = ""; }; 83B8FE232D59CDA7005854C1 /* rubberband.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = rubberband.pdf; path = Icons/rubberband.pdf; sourceTree = ""; }; 83B8FE272D59D03F005854C1 /* RubberbandPane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RubberbandPane.h; sourceTree = ""; }; @@ -145,6 +146,8 @@ 83DAD9F1286EDBCD000FAA9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PathSuggester.xib; sourceTree = ""; }; 83DAD9F3286EDBD2000FAA9A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/PathSuggester.strings; sourceTree = ""; }; 83DAD9F5286EDBD6000FAA9A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/PathSuggester.strings; sourceTree = ""; }; + 83E83F7C2D96780E0054365C /* AUPlayerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AUPlayerView.h; sourceTree = ""; }; + 83E83F7D2D96780E0054365C /* AUPlayerView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AUPlayerView.mm; sourceTree = ""; }; 83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeBehaviorArrayController.h; sourceTree = ""; }; 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeBehaviorArrayController.m; sourceTree = ""; }; 83F0E6AF287CAB4100D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/PathSuggester.strings; sourceTree = ""; }; @@ -269,7 +272,7 @@ 17C6433D0B8A783F00C53518 /* OutputPane.h */, 17C6433E0B8A783F00C53518 /* OutputPane.m */, 83B06727180D85B8008E3612 /* MIDIPane.h */, - 83B06728180D85B8008E3612 /* MIDIPane.m */, + 83B06728180D85B8008E3612 /* MIDIPane.mm */, 8307D309286057B5000FF8EB /* GeneralPane.h */, 8307D30A286057B5000FF8EB /* GeneralPane.m */, 83B8FE272D59D03F005854C1 /* RubberbandPane.h */, @@ -281,6 +284,8 @@ 17D503410ABDB1660022D1E8 /* Custom */ = { isa = PBXGroup; children = ( + 83E83F7C2D96780E0054365C /* AUPlayerView.h */, + 83E83F7D2D96780E0054365C /* AUPlayerView.mm */, 83651DA427322C8700A2C097 /* MIDIFlavorBehaviorArrayController.h */, 83651DA327322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m */, 837C0D3E1C50954000CAE18F /* MIDIPluginBehaviorArrayController.h */, @@ -502,8 +507,9 @@ 83AC573A2861A54D009D6F50 /* PathSuggester.m in Sources */, 83B8FE292D59D059005854C1 /* RubberbandPane.m in Sources */, 83726F022CF41B3200F15FBF /* AppearancePane.m in Sources */, + 83E83F7E2D96780E0054365C /* AUPlayerView.mm in Sources */, 83651DA527322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m in Sources */, - 83B06729180D85B8008E3612 /* MIDIPane.m in Sources */, + 83B06729180D85B8008E3612 /* MIDIPane.mm in Sources */, 8E07AA880AAC8EA200A4B32F /* HotKeyPane.m in Sources */, 83F27E741810E45D00CEF538 /* PathToFileTransformer.m in Sources */, 8E07AA890AAC8EA200A4B32F /* GeneralPreferencePane.m in Sources */, diff --git a/Preferences/Preferences/en.lproj/Preferences.strings b/Preferences/Preferences/en.lproj/Preferences.strings index 7fe1785fe..d2053a484 100644 --- a/Preferences/Preferences/en.lproj/Preferences.strings +++ b/Preferences/Preferences/en.lproj/Preferences.strings @@ -292,3 +292,6 @@ /* Class = "NSTextFieldCell"; title = "Channels:"; ObjectID = "LpA-ak-Aj5"; */ "LpA-ak-Aj5.title" = "Channels:"; + +/* Class = "NSButtonCell"; title = "Setup"; ObjectID = "Tdt-2E-FVm"; */ +"Tdt-2E-FVm.title" = "Setup"; diff --git a/Preferences/Preferences/es.lproj/Preferences.strings b/Preferences/Preferences/es.lproj/Preferences.strings index f15f09430..6511c4539 100644 --- a/Preferences/Preferences/es.lproj/Preferences.strings +++ b/Preferences/Preferences/es.lproj/Preferences.strings @@ -291,3 +291,6 @@ /* Class = "NSTextFieldCell"; title = "Channels:"; ObjectID = "LpA-ak-Aj5"; */ "LpA-ak-Aj5.title" = "Canales:"; + +/* Class = "NSButtonCell"; title = "Setup"; ObjectID = "Tdt-2E-FVm"; */ +"Tdt-2E-FVm.title" = "Config.";