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 <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-03-28 15:25:23 -07:00
parent 56dcf4df86
commit e9b15bc269
12 changed files with 475 additions and 16 deletions

View file

@ -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

View file

@ -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)

View file

@ -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;
}
}

View file

@ -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 <string>
#include <vector>
#include <AppKit/AppKit.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioUnit/AudioUnitCarbonView.h>
#include <Carbon/Carbon.h>
@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

View file

@ -0,0 +1,222 @@
//
// AUPlayerView.mm
// MIDI
//
// Created by Christopher Snowhill on 1/29/16.
// Copyright © 2016-2022 Christopher Snowhill. All rights reserved.
//
#import <AudioUnit/AUCocoaUIView.h>
#import <CoreAudioKit/AUGenericView.h>
#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;
}

View file

@ -468,6 +468,9 @@
<customObject id="i5B-ga-Atm" userLabel="MIDIPane" customClass="MIDIPane">
<connections>
<outlet property="midiFlavorControl" destination="1Yw-25-Gbs" id="6In-S0-YE2"/>
<outlet property="midiPluginBehaviorArrayController" destination="czk-eG-6QG" id="6FE-WD-VwB"/>
<outlet property="midiPluginControl" destination="8Nj-G4-5ag" id="1k1-lQ-sQi"/>
<outlet property="midiPluginSetupButton" destination="49M-Dx-QSC" id="AVl-il-IYO"/>
<outlet property="view" destination="JXu-ar-J3Y" id="uI3-0O-h4x"/>
</connections>
</customObject>
@ -898,7 +901,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Nj-G4-5ag" userLabel="MIDI Plugin">
<rect key="frame" x="295" y="176" width="329" height="26"/>
<rect key="frame" x="295" y="176" width="263" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="vaQ-pZ-jXy" id="xcv-1b-kTI">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -912,12 +915,24 @@
</menu>
</popUpButtonCell>
<connections>
<action selector="setMidiPlugin:" target="i5B-ga-Atm" id="hN2-mi-z23"/>
<binding destination="czk-eG-6QG" name="content" keyPath="arrangedObjects" id="Mzg-yr-6mv"/>
<binding destination="czk-eG-6QG" name="contentValues" keyPath="arrangedObjects.name" previousBinding="NFp-di-hMf" id="6JE-ba-47Z"/>
<binding destination="czk-eG-6QG" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="Mzg-yr-6mv" id="NFp-di-hMf"/>
<binding destination="52" name="selectedObject" keyPath="values.midiPlugin" previousBinding="6JE-ba-47Z" id="ePY-TV-ngD"/>
</connections>
</popUpButton>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="49M-Dx-QSC" userLabel="Setup">
<rect key="frame" x="555" y="174" width="70" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Setup" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Tdt-2E-FVm" userLabel="Setup">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="setupPlugin:" target="i5B-ga-Atm" id="NNs-KH-p3S"/>
</connections>
</button>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="G86-18-hiK">
<rect key="frame" x="18" y="151" width="274" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>

View file

@ -10,9 +10,14 @@
#import <Cocoa/Cocoa.h>
@interface MIDIPane : GeneralPreferencePane {
IBOutlet NSArrayController *midiPluginBehaviorArrayController;
IBOutlet NSButton *midiPluginSetupButton;
IBOutlet NSPopUpButton *midiPluginControl;
IBOutlet NSPopUpButton *midiFlavorControl;
}
- (IBAction)setupPlugin:(id)sender;
- (IBAction)setSoundFont:(id)sender;
@end

View file

@ -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

View file

@ -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));
}

View file

@ -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 = "<group>"; };
83AC573E2861B77E009D6F50 /* SandboxBroker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SandboxBroker.h; path = ../../Utils/SandboxBroker.h; sourceTree = "<group>"; };
83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = "<group>"; };
83B06728180D85B8008E3612 /* MIDIPane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPane.m; sourceTree = "<group>"; };
83B06728180D85B8008E3612 /* MIDIPane.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MIDIPane.mm; sourceTree = "<group>"; };
83B0672A180D8B39008E3612 /* midi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = midi.png; path = Icons/midi.png; sourceTree = "<group>"; };
83B8FE232D59CDA7005854C1 /* rubberband.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = rubberband.pdf; path = Icons/rubberband.pdf; sourceTree = "<group>"; };
83B8FE272D59D03F005854C1 /* RubberbandPane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RubberbandPane.h; sourceTree = "<group>"; };
@ -145,6 +146,8 @@
83DAD9F1286EDBCD000FAA9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PathSuggester.xib; sourceTree = "<group>"; };
83DAD9F3286EDBD2000FAA9A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/PathSuggester.strings; sourceTree = "<group>"; };
83DAD9F5286EDBD6000FAA9A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/PathSuggester.strings; sourceTree = "<group>"; };
83E83F7C2D96780E0054365C /* AUPlayerView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AUPlayerView.h; sourceTree = "<group>"; };
83E83F7D2D96780E0054365C /* AUPlayerView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AUPlayerView.mm; sourceTree = "<group>"; };
83EF495D17FBC96A00642E3C /* VolumeBehaviorArrayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VolumeBehaviorArrayController.h; sourceTree = "<group>"; };
83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VolumeBehaviorArrayController.m; sourceTree = "<group>"; };
83F0E6AF287CAB4100D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/PathSuggester.strings; sourceTree = "<group>"; };
@ -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 */,

View file

@ -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";

View file

@ -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.";