diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bce6af --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +xcuserdata diff --git a/BUGS.md b/BUGS.md new file mode 100644 index 0000000..f0d679d --- /dev/null +++ b/BUGS.md @@ -0,0 +1,30 @@ +MIDI Patchbay Bugs +================== + +Open +---- + +* MIDI inputs and outputs are sorted alphabetically rather than by port number. + +* Sometimes virtual endpoints can't be deleted even when no patch is obviously using them because an old patch can be sitting on the undo stack. The way to fix this is that patches that are in limbo should unregister themselves with the endpoints with which they are talking. + +* Creating a document, creating a virtual input or output then hitting undo will not reset the document saved flag for some reason. It has something to do with the undo grouping used by the virtual endpoint sheet, because if I disable that the problem goes away. + +* Creating a MIDI routing loop will crash the OS! It's really easy to do on Panther with IAC busses. + +* Select All in the Edit menu is enabled when editing the patch list despite the fact that only one patch can be selected at a time + +* Running an old version of MIDI Patchbay will show Open Recent menu items from the new version even if the old version doesn't support files + +* Start a new document, create a new virtual input, hit undo...the document is still marked as changed and requiring saving. (Maybe this has to do with the use of undo groups?) + +* Choosing the "Edit Virtual Inputs/Outputs..." options from the input or output popup menus will briefly cause this to become the selected item, and will only flick back to the real selected item after a second or so. It's very annoying visually. + +* Moving the lowest allowed note or highest allowed note sliders will cause a lot of undo events to be registered, which is a bit of a waste of memory. + +Closed +------ + +* There *may* be problems when multiple inputs are patched through to a single real output. If you see issues in this situation, please report them. This should not happen under OS X 10.1.5 or later. + +_$Id: Bugs.html,v 1.2.2.2 2004/11/25 04:38:48 pete Exp $_ diff --git a/Base.lproj/MainMenu.nib/designable.nib b/Base.lproj/MainMenu.nib/designable.nib new file mode 100644 index 0000000..f1b8717 --- /dev/null +++ b/Base.lproj/MainMenu.nib/designable.nib @@ -0,0 +1,224 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Base.lproj/MainMenu.nib/keyedobjects.nib b/Base.lproj/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000..b30779f Binary files /dev/null and b/Base.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/Base.lproj/PatchbayDocument.nib/designable.nib b/Base.lproj/PatchbayDocument.nib/designable.nib new file mode 100644 index 0000000..6f52e94 --- /dev/null +++ b/Base.lproj/PatchbayDocument.nib/designable.nib @@ -0,0 +1,597 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Base.lproj/PatchbayDocument.nib/keyedobjects.nib b/Base.lproj/PatchbayDocument.nib/keyedobjects.nib new file mode 100644 index 0000000..aa0d60e Binary files /dev/null and b/Base.lproj/PatchbayDocument.nib/keyedobjects.nib differ diff --git a/Bugs.html b/Bugs.html deleted file mode 100644 index 4efca4a..0000000 --- a/Bugs.html +++ /dev/null @@ -1,76 +0,0 @@ - - - MIDI Patchbay Bugs - - - - -

MIDI Patchbay Bugs

- -

Open

- -
    -
  • - MIDI inputs and outputs are sorted alphabetically rather than by - port number. -
  • -
  • - Sometimes virtual endpoints can't be deleted even when no patch is - obviously using them because an old patch can be sitting on the - undo stack. The way to fix this is that patches that are in limbo - should unregister themselves with the endpoints with which they - are talking. -
  • -
  • - Creating a document, creating a virtual input or output then hitting - undo will not reset the document saved flag for some reason. It has - something to do with the undo grouping used by the virtual endpoint - sheet, because if I disable that the problem goes away. -
  • -
  • - Creating a MIDI routing loop will crash the OS! It's really easy to - do on Panther with IAC busses. -
  • -
  • - Select All in the Edit menu is enabled when editing the patch list - despite the fact that only one patch can be selected at a time -
  • -
  • - Running an old version of MIDI Patchbay will show Open Recent menu - items from the new version even if the old version doesn't support - files -
  • -
  • - Start a new document, create a new virtual input, hit undo...the document - is still marked as changed and requiring saving. (Maybe this has to do - with the use of undo groups?) -
  • -
  • - Choosing the "Edit Virtual Inputs/Outputs..." options from the input - or output popup menus will briefly cause this to become the selected item, - and will only flick back to the real selected item after a second or so. - It's very annoying visually. -
  • -
  • - Moving the lowest allowed note or highest allowed note sliders will - cause a lot of undo events to be registered, which is a bit of a - waste of memory. -
  • -
- - -

Closed

- -
    -
  • - There may be problems when multiple inputs are patched through - to a single real output. If you see issues in this situation, - please report them. This should not happen under OS X 10.1.5 or later. -
  • -
- - -

$Id: Bugs.html,v 1.2.2.2 2004/11/25 04:38:48 pete Exp $

- - - \ No newline at end of file diff --git a/Controller Classes/EndpointTableDataSource.h b/Controller Classes/EndpointTableDataSource.h index 7b24e91..1f15659 100644 --- a/Controller Classes/EndpointTableDataSource.h +++ b/Controller Classes/EndpointTableDataSource.h @@ -1,15 +1,14 @@ #import #import +#import "TableDataSource.h" - -@interface EndpointTableDataSource : NSObject { +@interface EndpointTableDataSource : TableDataSource { Class endpointClass; NSMutableArray* endpointArray; NSUndoManager* undoManager; } - (EndpointTableDataSource*)initWithEndpointClass:(Class)newEndpointClass endpointArray:(NSMutableArray*)newEndpointArray undoManager:(NSUndoManager*)newUndoManager; -- (void)dealloc; - (void)setEndpointArray:(NSMutableArray*)newEndpointArray; @@ -17,7 +16,6 @@ - (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn*)column row:(int)rowIndex; - (BOOL)control:(NSControl*)control isValidObject:(id)value; - (void)tableView:(NSTableView*)tableView setObjectValue:(id)value forTableColumn:(NSTableColumn*)column row:(int)rowIndex; -- (void)deleteSelection:(NSTableView*)tableView; - (BOOL)tabView:(NSTabView*)tabView shouldSelectTabViewItem:(NSTabViewItem*)tabViewItem; diff --git a/Controller Classes/EndpointTableDataSource.m b/Controller Classes/EndpointTableDataSource.m index aba0f8a..4124fc8 100644 --- a/Controller Classes/EndpointTableDataSource.m +++ b/Controller Classes/EndpointTableDataSource.m @@ -11,34 +11,23 @@ - (EndpointTableDataSource*)initWithEndpointClass:(Class)newEndpointClass endpoi if (self != nil) { endpointClass = newEndpointClass; - endpointArray = [newEndpointArray retain]; - undoManager = [newUndoManager retain]; + endpointArray = newEndpointArray; + undoManager = newUndoManager; } return self; } -- (void)dealloc -{ - [endpointArray release]; - [undoManager release]; - - [super dealloc]; -} - - - (void)setEndpointArray:(NSMutableArray*)newEndpointArray { - [newEndpointArray retain]; - [endpointArray release]; endpointArray = newEndpointArray; } - (int)numberOfRowsInTableView:(NSTableView*)tableView { - return [endpointArray count]; + return (int)[endpointArray count]; } @@ -54,11 +43,13 @@ - (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn - (BOOL)control:(NSControl*)control isValidObject:(id)value { if (PYMIDIIsEndpointNameTaken (value)) { - NSRunAlertPanel ( - [NSString stringWithFormat:@"The name \"%@\" is already taken.", value], - @"Please choose a different name.", - nil, nil, nil - ); + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + [alert setMessageText: [NSString stringWithFormat:@"The name \"%@\" is already taken.", value]]; + [alert setInformativeText:@"Please choose a different name."]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert runModal]; + return NO; } else @@ -72,11 +63,13 @@ - (void)tableView:(NSTableView*)tableView setObjectValue:(id)value forTableColum if (![value isEqualToString:@""] && ![value isEqualToString:[endpoint name]]) { if (PYMIDIIsEndpointNameTaken (value)) { - NSRunAlertPanel ( - [NSString stringWithFormat:@"The name \"%@\" is already taken.", value], - @"Please choose a different name.", - nil, nil, nil - ); + NSAlert *alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + [alert setMessageText: [NSString stringWithFormat:@"The name \"%@\" is already taken.", value]]; + [alert setInformativeText:@"Please choose a different name."]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert runModal]; + } else { [self tableView:tableView setName:(NSString*)value forEndpointAtIndex:rowIndex]; @@ -90,14 +83,17 @@ - (void)deleteSelection:(NSTableView*)tableView PYMIDIVirtualEndpoint* endpoint = [endpointArray objectAtIndex:[tableView selectedRow]]; if ([endpoint isInUse]) { - NSRunAlertPanel ( - @"The selection is in use by one or more patches and cannot be deleted.", - @"", - nil, nil, nil - ); + NSAlert *alert = [[NSAlert alloc] init]; + + [alert addButtonWithTitle:@"OK"]; + [alert setMessageText:@"The selection is in use by one or more patches and cannot be deleted."]; + [alert setInformativeText:@""]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert runModal]; + } else { - [self tableView:tableView removeEndpointAtIndex:[tableView selectedRow]]; + [self tableView:tableView removeEndpointAtIndex:(int)[tableView selectedRow]]; } } @@ -129,9 +125,7 @@ - (void)tableView:(NSTableView*)tableView newEndpointWithName:(NSString*)name // Allocate the new endpoint and add it to the endpoint array newEndpoint = [[endpointClass alloc] initWithName:newName]; - [self tableView:tableView addEndpoint:newEndpoint atIndex:[endpointArray count]]; - - [newEndpoint release]; + [self tableView:tableView addEndpoint:newEndpoint atIndex:(int)[endpointArray count]]; } @@ -157,7 +151,7 @@ - (void)tableView:(NSTableView*)tableView removeEndpointAtIndex:(int)index NSWindow* window = [tableView window]; if ([window isKeyWindow] && ![window makeFirstResponder:nil]) return; - PYMIDIVirtualEndpoint* endpoint = [[endpointArray objectAtIndex:index] retain]; + PYMIDIVirtualEndpoint* endpoint = [endpointArray objectAtIndex:index]; [endpoint makePrivate:YES]; [endpointArray removeObjectAtIndex:index]; @@ -168,15 +162,13 @@ - (void)tableView:(NSTableView*)tableView removeEndpointAtIndex:(int)index [[undoManager prepareWithInvocationTarget:self] tableView:tableView addEndpoint:endpoint atIndex:index ]; - - [endpoint release]; } - (void)tableView:(NSTableView*)tableView setName:(NSString*)name forEndpointAtIndex:(int)index { PYMIDIVirtualEndpoint* endpoint = [endpointArray objectAtIndex:index]; - NSString* oldName = [[endpoint name] retain]; + NSString* oldName = [endpoint name]; [endpoint setName:name]; [tableView reloadData]; diff --git a/Controller Classes/PatchTableDataSource.h b/Controller Classes/PatchTableDataSource.h index fe58e2d..f8e69f0 100644 --- a/Controller Classes/PatchTableDataSource.h +++ b/Controller Classes/PatchTableDataSource.h @@ -1,22 +1,21 @@ #import +#import "TableDataSource.h" @class Patch; @class PatchbayDocument; -@interface PatchTableDataSource : NSObject { +@interface PatchTableDataSource : TableDataSource { PatchbayDocument* document; NSMutableArray* patchArray; } - (PatchTableDataSource*)initWithDocument:(PatchbayDocument*)newDocument patchArray:(NSMutableArray*)newPatchArray; -- (void)dealloc; - (void)setPatchArray:(NSMutableArray*)newPatchArray; - (int)numberOfRowsInTableView:(NSTableView*)tableView; - (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn*)column row:(int)rowIndex; - (void)tableView:(NSTableView*)tableView setObjectValue:(id)value forTableColumn:(NSTableColumn*)column row:(int)rowIndex; -- (void)deleteSelection:(NSTableView*)tableView; @end diff --git a/Controller Classes/PatchTableDataSource.m b/Controller Classes/PatchTableDataSource.m index 80c097b..3568962 100644 --- a/Controller Classes/PatchTableDataSource.m +++ b/Controller Classes/PatchTableDataSource.m @@ -13,32 +13,22 @@ - (PatchTableDataSource*)initWithDocument:(PatchbayDocument*)newDocument patchAr if (self != nil) { document = newDocument; - patchArray = [newPatchArray retain]; + patchArray = newPatchArray; } return self; } -- (void)dealloc -{ - [patchArray release]; - - [super dealloc]; -} - - - (void)setPatchArray:(NSMutableArray*)newPatchArray { - [newPatchArray retain]; - [patchArray release]; patchArray = newPatchArray; } - (int)numberOfRowsInTableView:(NSTableView*)tableView { - return [patchArray count]; + return (int)[patchArray count]; } @@ -91,7 +81,7 @@ - (void)tableView:(NSTableView*)tableView setObjectValue:(id)value forTableColum - (void)deleteSelection:(NSTableView*)tableView { - [document removePatchAtIndex:[tableView selectedRow]]; + [document removePatchAtIndex:(int)[tableView selectedRow]]; } diff --git a/Controller Classes/PatchbayDocument.h b/Controller Classes/PatchbayDocument.h index 7a7a497..4b0b589 100644 --- a/Controller Classes/PatchbayDocument.h +++ b/Controller Classes/PatchbayDocument.h @@ -11,7 +11,7 @@ @class EndpointTableDataSource; -@interface PatchbayDocument : NSDocument { +@interface PatchbayDocument : NSDocument { IBOutlet NSWindow* documentWindow; // Stuff related to the table of patches @@ -20,10 +20,11 @@ PatchTableDataSource* patchTableDataSource; IBOutlet NSButton* addPatchButton; + IBOutlet NSButton* removePatchButton; NSMutableArray* patchArray; Patch* selectedPatch; - + int selectedIndex; // Stuff related to editing a patch @@ -89,6 +90,7 @@ - (void)selectedPatchChanged:(NSNotification*)notification; - (IBAction)addPatchButtonPressed:(id)sender; +- (IBAction)removePatchButtonPressed:(id)sender; - (void)addPatch:(Patch*)patch atIndex:(int)index; - (void)addPatchFromArchive:(NSData*)data atIndex:(int)index; @@ -170,7 +172,7 @@ - (IBAction)endpointPanelButtonPressed:(id)sender; -- (void)endpointPanelDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo; +- (void)endpointPanelDidEnd:(NSWindow*)sheet returnCode:(NSModalResponse)returnCode contextInfo:(void*)contextInfo; @end diff --git a/Controller Classes/PatchbayDocument.m b/Controller Classes/PatchbayDocument.m index 1c80f9c..c82e826 100644 --- a/Controller Classes/PatchbayDocument.m +++ b/Controller Classes/PatchbayDocument.m @@ -32,7 +32,6 @@ - (id)init selectedPatch = [[Patch alloc] initWithInput:input output:output]; [selectedPatch rescueFromLimbo]; patchArray = [[NSMutableArray alloc] initWithObjects:selectedPatch, nil]; - [selectedPatch release]; // Virtual endpoint related initialisation virtualSourceArray = [[NSMutableArray alloc] init]; @@ -41,7 +40,7 @@ - (id)init // Uncomment the following line for debugging if you want to // disable undo, which makes tracking retain/release issues easier. //[self setUndoManager:nil]; - + selectedIndex = -1; return self; } @@ -50,7 +49,7 @@ - (void)windowControllerDidLoadNib:(NSWindowController*)windowController { NSButtonCell* buttonCell; PatchTableCell* patchTableCell; - + [super windowControllerDidLoadNib:windowController]; // These are so that our window and panel use the correct undo manager @@ -65,11 +64,9 @@ - (void)windowControllerDidLoadNib:(NSWindowController*)windowController [buttonCell setButtonType:NSSwitchButton]; [buttonCell setTitle:@""]; [[patchTable tableColumnWithIdentifier:@"enabled"] setDataCell:buttonCell]; - [buttonCell release]; patchTableCell = [[PatchTableCell alloc] init]; [[patchTable tableColumnWithIdentifier:@"patch"] setDataCell:patchTableCell]; - [patchTableCell release]; patchTableDataSource = [[PatchTableDataSource alloc] initWithDocument:self patchArray:patchArray]; [patchTable setDataSource:patchTableDataSource]; @@ -113,7 +110,6 @@ - (void)windowControllerDidLoadNib:(NSWindowController*)windowController // See the comment on tabView:shouldSelectTabViewItem; in EndpointTableDataSource [virtualEndpointTabView setDelegate:inputTableDataSource]; - [self syncWithLoadedData]; } @@ -121,17 +117,6 @@ - (void)windowControllerDidLoadNib:(NSWindowController*)windowController - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [patchTableDataSource release]; - [patchArray release]; - - [inputTableDataSource release]; - [outputTableDataSource release]; - - [virtualSourceArray release]; - [virtualDestinationArray release]; - - [super dealloc]; } @@ -150,7 +135,6 @@ - (NSData*)dataRepresentationOfType:(NSString*)type [archiver encodeObject:patchArray forKey:@"patchArray"]; [archiver finishEncoding]; - [archiver release]; return data; } @@ -158,21 +142,16 @@ - (NSData*)dataRepresentationOfType:(NSString*)type - (BOOL)loadDataRepresentation:(NSData*)data ofType:(NSString*)type { - [patchArray release]; - [virtualDestinationArray release]; - [virtualSourceArray release]; - NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data]; - virtualDestinationArray = [[unarchiver decodeObjectForKey:@"virtualDestinationArray"] retain]; + virtualDestinationArray = [unarchiver decodeObjectForKey:@"virtualDestinationArray"]; - virtualSourceArray = [[unarchiver decodeObjectForKey:@"virtualSourceArray"] retain]; + virtualSourceArray = [unarchiver decodeObjectForKey:@"virtualSourceArray"]; - patchArray = [[unarchiver decodeObjectForKey:@"patchArray"] retain]; + patchArray = [unarchiver decodeObjectForKey:@"patchArray"]; [patchArray makeObjectsPerformSelector:@selector(rescueFromLimbo)]; [unarchiver finishDecoding]; - [unarchiver release]; // We need to do this to handle redisplay of the new data when a Revert is done [self syncWithLoadedData]; @@ -192,7 +171,7 @@ - (void)syncWithLoadedData [patchTable reloadData]; if ([patchArray count] > 0) - [patchTable selectRow:0 byExtendingSelection:NO]; + [patchTable selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; else [patchTable deselectAll:self]; @@ -238,7 +217,7 @@ - (void)midiSetupChanged:(NSNotification*)notification - (void)selectedPatchChanged:(NSNotification*)notification { - int patchIndex = [patchTable selectedRow]; + int patchIndex = (int)[patchTable selectedRow]; if (patchIndex != -1) selectedPatch = [patchArray objectAtIndex:patchIndex]; @@ -252,15 +231,16 @@ - (void)selectedPatchChanged:(NSNotification*)notification [self setTransposeControls]; [self setTransmitClockControls]; [self setOutputPopUp]; + + selectedIndex = patchIndex; } -- (IBAction)addPatchButtonPressed:(id)sender +- (IBAction) addPatchButtonPressed:(id)sender { - Patch* patch; - if (selectedPatch != nil) { - patch = [[Patch alloc] initFromPatch:selectedPatch]; + Patch *patch = [[Patch alloc] initFromPatch:selectedPatch]; + [self addPatch:patch atIndex:(int)[patchArray count]]; } else { // Pick a default input and output for a blank patch @@ -276,14 +256,19 @@ - (IBAction)addPatchButtonPressed:(id)sender PYMIDIEndpoint* output; do { output = [enumerator nextObject]; } while ([output isIACBus]); - patch = [[Patch alloc] initWithInput:input output:output]; + Patch *patch = [[Patch alloc] initWithInput:input output:output]; + [self addPatch:patch atIndex:(int)[patchArray count]]; } - - [self addPatch:patch atIndex:[patchArray count]]; - - [patch release]; + } +- (IBAction)removePatchButtonPressed:(id)sender +{ + if (selectedIndex >= 0) { + [self removePatchAtIndex: selectedIndex]; + } + +} - (void)addPatch:(Patch*)patch atIndex:(int)index { @@ -293,7 +278,7 @@ - (void)addPatch:(Patch*)patch atIndex:(int)index [patchArray insertObject:patch atIndex:index]; [patchTable reloadData]; - [patchTable selectRow:index byExtendingSelection:NO]; + [patchTable selectRowIndexes:[NSIndexSet indexSetWithIndex:index] byExtendingSelection:NO]; [[undoManager prepareWithInvocationTarget:self] removePatchAtIndex:index @@ -313,7 +298,7 @@ - (void)addPatchFromArchive:(NSData*)data atIndex:(int)index - (void)removePatchAtIndex:(int)index { NSUndoManager* undoManager = [self undoManager]; - Patch* patch = [[patchArray objectAtIndex:index] retain]; + Patch* patch = [patchArray objectAtIndex:index]; [patch banishToLimbo]; [patchArray removeObjectAtIndex:index]; @@ -323,8 +308,6 @@ - (void)removePatchAtIndex:(int)index [[undoManager prepareWithInvocationTarget:self] addPatch:patch atIndex:index ]; - - [patch release]; } @@ -362,7 +345,6 @@ - (NSData*)archivePatchForPasteBoard:(Patch*)patch [archiver encodeObject:patch forKey:@"patch"]; [archiver finishEncoding]; - [archiver release]; return data; } @@ -384,7 +366,6 @@ - (Patch*)unarchivePatchFromPasteBoard:(NSData*)data patch = [unarchiver decodeObjectForKey:@"patch"]; [unarchiver finishDecoding]; - [unarchiver release]; return patch; } @@ -480,7 +461,6 @@ - (void)buildFilterChannelControls [filterChannelMatrix setIntercellSpacing:NSMakeSize(5,4)]; [filterChannelMatrix setMode:NSTrackModeMatrix]; [filterChannelMatrix renewRows:2 columns:8]; - [buttonCell release]; for (i = 0; i < 8; i++) { buttonCell = [filterChannelMatrix cellAtRow:0 column:i]; @@ -625,7 +605,7 @@ - (void)setShouldRemapChannel:(BOOL)shouldRemapChannel forPatch:(Patch*)patch - (IBAction)remapChannelPopUpChanged:(id)sender { - [self setRemappingChannel:[remapChannelPopUp indexOfSelectedItem] + 1 forPatch:selectedPatch]; + [self setRemappingChannel:(int)[remapChannelPopUp indexOfSelectedItem] + 1 forPatch:selectedPatch]; } @@ -1020,14 +1000,10 @@ - (IBAction)editVirtualInputs:(id)sender [[self undoManager] beginUndoGrouping]; panelWasOpenedToInputs = YES; - - [[NSApplication sharedApplication] - beginSheet:virtualEndpointPanel - modalForWindow:documentWindow - modalDelegate:self - didEndSelector:@selector(endpointPanelDidEnd:returnCode:contextInfo:) - contextInfo:nil - ]; + + [documentWindow beginSheet:virtualEndpointPanel completionHandler:^(NSModalResponse returnCode) { + [self endpointPanelDidEnd:self->virtualEndpointPanel returnCode:returnCode contextInfo:nil]; + }]; } @@ -1041,14 +1017,9 @@ - (IBAction)editVirtualOutputs:(id)sender [[self undoManager] beginUndoGrouping]; panelWasOpenedToInputs = NO; - - [[NSApplication sharedApplication] - beginSheet:virtualEndpointPanel - modalForWindow:documentWindow - modalDelegate:self - didEndSelector:@selector(endpointPanelDidEnd:returnCode:contextInfo:) - contextInfo:nil - ]; + [documentWindow beginSheet:virtualEndpointPanel completionHandler:^(NSModalResponse returnCode) { + [self endpointPanelDidEnd:self->virtualEndpointPanel returnCode:returnCode contextInfo:nil]; + }]; } @@ -1058,10 +1029,11 @@ - (IBAction)newInputButtonPressed:(id)sender [[[self displayName] lastPathComponent] stringByDeletingPathExtension] ]; - [[inputTable dataSource] tableView:inputTable newEndpointWithName:baseName]; + [(EndpointTableDataSource *)[inputTable dataSource] tableView:inputTable newEndpointWithName:baseName]; // Set up the newly created endpoint for editing - [inputTable selectRow:[inputTable numberOfRows]-1 byExtendingSelection:NO]; + + [inputTable selectRowIndexes:[NSIndexSet indexSetWithIndex:[inputTable numberOfRows]-1] byExtendingSelection:NO]; [inputTable editColumn:0 row:[inputTable selectedRow] withEvent:nil select:YES]; } @@ -1073,10 +1045,11 @@ - (IBAction)newOutputButtonPressed:(id)sender [[[self displayName] lastPathComponent] stringByDeletingPathExtension] ]; - [[outputTable dataSource] tableView:outputTable newEndpointWithName:baseName]; + [(EndpointTableDataSource *)[outputTable dataSource] tableView:outputTable newEndpointWithName:baseName]; // Set up the newly created endpoint for editing - [outputTable selectRow:[outputTable numberOfRows]-1 byExtendingSelection:NO]; + [outputTable selectRowIndexes:[NSIndexSet indexSetWithIndex:[outputTable numberOfRows]-1] byExtendingSelection:NO]; + [outputTable editColumn:0 row:[outputTable selectedRow] withEvent:nil select:YES]; } @@ -1086,25 +1059,29 @@ - (IBAction)endpointPanelButtonPressed:(id)sender { if ([virtualEndpointPanel makeFirstResponder:nil]) { [virtualEndpointPanel orderOut:self]; - [[NSApplication sharedApplication] endSheet:virtualEndpointPanel returnCode:0]; + [documentWindow endSheet:virtualEndpointPanel returnCode:0]; } } -- (void)endpointPanelDidEnd:(NSWindow*)sheet returnCode:(int)returnCode contextInfo:(void*)contextInfo +- (void)endpointPanelDidEnd:(NSWindow*)sheet returnCode:(NSModalResponse)returnCode contextInfo:(void*)contextInfo { - int tab = [virtualEndpointTabView indexOfTabViewItem:[virtualEndpointTabView selectedTabViewItem]]; + int tab = (int)[virtualEndpointTabView indexOfTabViewItem:[virtualEndpointTabView selectedTabViewItem]]; if (panelWasOpenedToInputs) { if (tab == 0 && [inputTable selectedRow] != -1) { PYMIDIEndpoint* input = [virtualDestinationArray objectAtIndex:[inputTable selectedRow]]; - [self setInput:input forPatch:selectedPatch]; + if (selectedPatch.input != input) { + [self setInput:input forPatch:selectedPatch]; + } } } else { if (tab == 1 && [outputTable selectedRow] != -1) { PYMIDIEndpoint* output = [virtualSourceArray objectAtIndex:[outputTable selectedRow]]; - [self setOutput:output forPatch:selectedPatch];; + if (selectedPatch.output != output) { + [self setOutput:output forPatch:selectedPatch]; + } } } diff --git a/Controller Classes/TableDataSource.h b/Controller Classes/TableDataSource.h new file mode 100644 index 0000000..8650eef --- /dev/null +++ b/Controller Classes/TableDataSource.h @@ -0,0 +1,8 @@ +#import + +// simple base class for deletion selector +@interface TableDataSource : NSObject + +- (void)deleteSelection:(NSTableView*)tableView; + +@end diff --git a/Controller Classes/TableDataSource.m b/Controller Classes/TableDataSource.m new file mode 100644 index 0000000..0aa6ada --- /dev/null +++ b/Controller Classes/TableDataSource.m @@ -0,0 +1,7 @@ +#import "TableDataSource.h" + +@implementation TableDataSource + +- (void)deleteSelection:(NSTableView*)tableView {} + +@end diff --git a/Credits.html b/Credits.html index ff1115b..b09f331 100644 --- a/Credits.html +++ b/Credits.html @@ -4,29 +4,15 @@

Thanks to Kevan Staples for the icon.

+ -

Copyright © 2003-2008 Peter Yandell

+ -

Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions:

+

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

-

The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software.

+

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

-

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE.

+

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+
- \ No newline at end of file diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings deleted file mode 100644 index 8dbb936..0000000 Binary files a/English.lproj/InfoPlist.strings and /dev/null differ diff --git a/English.lproj/MainMenu.nib/classes.nib b/English.lproj/MainMenu.nib/classes.nib deleted file mode 100644 index aefca1a..0000000 --- a/English.lproj/MainMenu.nib/classes.nib +++ /dev/null @@ -1,36 +0,0 @@ - - - - - IBClasses - - - CLASS - FirstResponder - LANGUAGE - ObjC - SUPERCLASS - NSObject - - - ACTIONS - - displayLicense - id - sendFeedback - id - visitWebSite - id - - CLASS - PatchbayController - LANGUAGE - ObjC - SUPERCLASS - NSObject - - - IBVersion - 1 - - diff --git a/English.lproj/MainMenu.nib/info.nib b/English.lproj/MainMenu.nib/info.nib deleted file mode 100644 index bb86cd1..0000000 --- a/English.lproj/MainMenu.nib/info.nib +++ /dev/null @@ -1,20 +0,0 @@ - - - - - IBFramework Version - 629 - IBLastKnownRelativeProjectPath - ../../MIDI Patchbay.xcodeproj - IBOldestOS - 5 - IBOpenObjects - - 57 - - IBSystem Version - 9B18 - targetFramework - IBCocoaFramework - - diff --git a/English.lproj/MainMenu.nib/keyedobjects.nib b/English.lproj/MainMenu.nib/keyedobjects.nib deleted file mode 100644 index 24c52be..0000000 Binary files a/English.lproj/MainMenu.nib/keyedobjects.nib and /dev/null differ diff --git a/English.lproj/PatchbayDocument.nib/classes.nib b/English.lproj/PatchbayDocument.nib/classes.nib deleted file mode 100644 index 8058cb4..0000000 --- a/English.lproj/PatchbayDocument.nib/classes.nib +++ /dev/null @@ -1,144 +0,0 @@ - - - - - IBClasses - - - ACTIONS - - addPatchButtonPressed - id - endpointPanelButtonPressed - id - filterChannelMatrixChanged - id - filterChannelRadioButtonsChanged - id - filterRangeRadioButtonsChanged - id - highestAllowedNoteSliderChanged - id - highestAllowedNoteStepperChanged - id - inputPopUpChanged - id - lowestAllowedNoteSliderChanged - id - lowestAllowedNoteStepperChanged - id - newInputButtonPressed - id - newOutputButtonPressed - id - outputPopUpChanged - id - remapChannelButtonChanged - id - remapChannelPopUpChanged - id - transmitClockButtonChanged - id - transposeButtonChanged - id - transposeDistanceSliderChanged - id - transposeDistanceStepperChanged - id - - CLASS - PatchbayDocument - LANGUAGE - ObjC - OUTLETS - - addPatchButton - NSButton - documentWindow - NSWindow - filterChannelMatrix - NSMatrix - filterChannelRadioButtons - NSMatrix - filterRangeLabelField - NSTextField - filterRangeRadioButtons - NSMatrix - highestAllowedNoteField - NSTextField - highestAllowedNoteSlider - NSSlider - highestAllowedNoteStepper - NSStepper - inputPopUp - NSPopUpButton - inputTable - NSTableView - lowestAllowedNoteField - NSTextField - lowestAllowedNoteSlider - NSSlider - lowestAllowedNoteStepper - NSStepper - outputPopUp - NSPopUpButton - outputTable - NSTableView - patchTable - NSTableView - remapChannelButton - NSButton - remapChannelPopUp - NSPopUpButton - transmitClockButton - NSButton - transposeButton - NSButton - transposeDistanceField - NSTextField - transposeDistanceSlider - NSSlider - transposeDistanceStepper - NSStepper - virtualEndpointPanel - NSPanel - virtualEndpointTabView - NSTabView - - SUPERCLASS - NSDocument - - - ACTIONS - - clear - id - deleteBackward - id - deleteForward - id - moveDown - id - moveUp - id - - CLASS - KeyTableView - LANGUAGE - ObjC - SUPERCLASS - NSTableView - - - CLASS - FirstResponder - LANGUAGE - ObjC - SUPERCLASS - NSObject - - - IBVersion - 1 - - diff --git a/English.lproj/PatchbayDocument.nib/info.nib b/English.lproj/PatchbayDocument.nib/info.nib deleted file mode 100644 index b25c89f..0000000 --- a/English.lproj/PatchbayDocument.nib/info.nib +++ /dev/null @@ -1,18 +0,0 @@ - - - - - IBFramework Version - 629 - IBLastKnownRelativeProjectPath - ../../MIDI Patchbay.xcodeproj - IBOldestOS - 3 - IBOpenObjects - - IBSystem Version - 9B18 - targetFramework - IBCocoaFramework - - diff --git a/English.lproj/PatchbayDocument.nib/keyedobjects.nib b/English.lproj/PatchbayDocument.nib/keyedobjects.nib deleted file mode 100644 index 6766a1c..0000000 Binary files a/English.lproj/PatchbayDocument.nib/keyedobjects.nib and /dev/null differ diff --git a/English.lproj/PatchbayDocument~.nib/classes.nib b/English.lproj/PatchbayDocument~.nib/classes.nib deleted file mode 100644 index 0083d00..0000000 --- a/English.lproj/PatchbayDocument~.nib/classes.nib +++ /dev/null @@ -1,72 +0,0 @@ -{ - IBClasses = ( - {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - { - ACTIONS = { - clear = id; - deleteBackward = id; - deleteForward = id; - moveDown = id; - moveUp = id; - }; - CLASS = KeyTableView; - LANGUAGE = ObjC; - SUPERCLASS = NSTableView; - }, - { - ACTIONS = { - addPatchButtonPressed = id; - endpointPanelButtonPressed = id; - filterChannelMatrixChanged = id; - filterChannelRadioButtonsChanged = id; - filterRangeRadioButtonsChanged = id; - highestAllowedNoteSliderChanged = id; - highestAllowedNoteStepperChanged = id; - inputPopUpChanged = id; - lowestAllowedNoteSliderChanged = id; - lowestAllowedNoteStepperChanged = id; - newInputButtonPressed = id; - newOutputButtonPressed = id; - outputPopUpChanged = id; - remapChannelButtonChanged = id; - remapChannelPopUpChanged = id; - transmitClockButtonChanged = id; - transposeButtonChanged = id; - transposeDistanceSliderChanged = id; - transposeDistanceStepperChanged = id; - }; - CLASS = PatchbayDocument; - LANGUAGE = ObjC; - OUTLETS = { - addPatchButton = NSButton; - documentWindow = NSWindow; - filterChannelMatrix = NSMatrix; - filterChannelRadioButtons = NSMatrix; - filterRangeLabelField = NSTextField; - filterRangeRadioButtons = NSMatrix; - highestAllowedNoteField = NSTextField; - highestAllowedNoteSlider = NSSlider; - highestAllowedNoteStepper = NSStepper; - inputPopUp = NSPopUpButton; - inputTable = NSTableView; - lowestAllowedNoteField = NSTextField; - lowestAllowedNoteSlider = NSSlider; - lowestAllowedNoteStepper = NSStepper; - outputPopUp = NSPopUpButton; - outputTable = NSTableView; - patchTable = NSTableView; - remapChannelButton = NSButton; - remapChannelPopUp = NSPopUpButton; - transmitClockButton = NSButton; - transposeButton = NSButton; - transposeDistanceField = NSTextField; - transposeDistanceSlider = NSSlider; - transposeDistanceStepper = NSStepper; - virtualEndpointPanel = NSPanel; - virtualEndpointTabView = NSTabView; - }; - SUPERCLASS = NSDocument; - } - ); - IBVersion = 1; -} \ No newline at end of file diff --git a/English.lproj/PatchbayDocument~.nib/info.nib b/English.lproj/PatchbayDocument~.nib/info.nib deleted file mode 100644 index 50938ff..0000000 --- a/English.lproj/PatchbayDocument~.nib/info.nib +++ /dev/null @@ -1,16 +0,0 @@ - - - - - IBDocumentLocation - 145 64 474 240 0 0 1600 1178 - IBFramework Version - 291.0 - IBOpenObjects - - 5 - - IBSystem Version - 6G30 - - diff --git a/English.lproj/PatchbayDocument~.nib/objects.nib b/English.lproj/PatchbayDocument~.nib/objects.nib deleted file mode 100644 index ffa3815..0000000 Binary files a/English.lproj/PatchbayDocument~.nib/objects.nib and /dev/null differ diff --git a/Info.plist b/Info.plist index 600c0d5..2e133c0 100644 --- a/Info.plist +++ b/Info.plist @@ -26,22 +26,24 @@ CFBundleIconFile AppIcon CFBundleIdentifier - com.notahat.MIDIPatchBay + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL + CFBundleShortVersionString + 1.0.4 CFBundleSignature ???? CFBundleVersion - 1.0.3 + 1 + LSApplicationCategoryType + public.app-category.music NSMainNibFile MainMenu NSPrincipalClass NSApplication - CFBundleShortVersionString - 1.0.3 diff --git a/MIDI Patchbay.xcodeproj/project.pbxproj b/MIDI Patchbay.xcodeproj/project.pbxproj index 3183293..dbc30ab 100644 --- a/MIDI Patchbay.xcodeproj/project.pbxproj +++ b/MIDI Patchbay.xcodeproj/project.pbxproj @@ -3,10 +3,11 @@ archiveVersion = 1; classes = { }; - objectVersion = 44; + objectVersion = 46; objects = { /* Begin PBXBuildFile section */ + 3092FEA6239969AF0057E329 /* TableDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = 3092FEA5239969AF0057E329 /* TableDataSource.m */; }; C12C7FE905AECD9D0066E007 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 2A37F4B6FDCFA73011CA2CEA /* MainMenu.nib */; }; C12C7FEA05AECD9D0066E007 /* PatchbayDocument.nib in Resources */ = {isa = PBXBuildFile; fileRef = 2A37F4B4FDCFA73011CA2CEA /* PatchbayDocument.nib */; }; C12C7FEB05AECD9D0066E007 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165FFE840EACC02AAC07 /* InfoPlist.strings */; }; @@ -39,12 +40,14 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 089C1660FE840EACC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; 2A37F4B0FDCFA73011CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 2A37F4B5FDCFA73011CA2CEA /* PatchbayDocument.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = PatchbayDocument.nib; path = English.lproj/PatchbayDocument.nib; sourceTree = ""; }; - 2A37F4B7FDCFA73011CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; 2A37F4C4FDCFA73011CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; 2A37F4C5FDCFA73011CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 3092FEA123995FA60057E329 /* Base */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Base; path = Base.lproj/MainMenu.nib; sourceTree = ""; }; + 3092FEA223995FA60057E329 /* Base */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Base; path = Base.lproj/PatchbayDocument.nib; sourceTree = ""; }; + 3092FEA323995FA90057E329 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 3092FEA4239968090057E329 /* TableDataSource.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TableDataSource.h; path = "Controller Classes/TableDataSource.h"; sourceTree = ""; }; + 3092FEA5239969AF0057E329 /* TableDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TableDataSource.m; path = "Controller Classes/TableDataSource.m"; sourceTree = ""; }; C12C800205AECD9D0066E007 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C12C800305AECD9D0066E007 /* MIDI Patchbay.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MIDI Patchbay.app"; sourceTree = BUILT_PRODUCTS_DIR; }; C152A6E907559AE9004FF49E /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = ""; }; @@ -159,6 +162,8 @@ F5C0AB14033196D6018C91A3 /* PatchbayDocument.m */, C183B43603E8F7C500BCF81F /* PatchbayController.h */, C183B43703E8F7C500BCF81F /* PatchbayController.m */, + 3092FEA4239968090057E329 /* TableDataSource.h */, + 3092FEA5239969AF0057E329 /* TableDataSource.m */, ); name = "Controller Classes"; sourceTree = ""; @@ -257,7 +262,6 @@ C12C7FE805AECD9D0066E007 /* Resources */, C12C7FF005AECD9D0066E007 /* Sources */, C12C7FFA05AECD9D0066E007 /* Frameworks */, - C14601500D3CB30B002E94E5 /* ShellScript */, ); buildRules = ( ); @@ -274,9 +278,17 @@ /* Begin PBXProject section */ 2A37F4A9FDCFA73011CA2CEA /* Project object */ = { isa = PBXProject; + attributes = { + LastUpgradeCheck = 1030; + }; buildConfigurationList = C101046C088A0A7C001DFE4F /* Build configuration list for PBXProject "MIDI Patchbay" */; - compatibilityVersion = "Xcode 3.0"; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; hasScannedForEncodings = 1; + knownRegions = ( + en, + Base, + ); mainGroup = 2A37F4AAFDCFA73011CA2CEA /* MIDI Patchbay */; projectDirPath = ""; projectRoot = ""; @@ -303,22 +315,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - C14601500D3CB30B002E94E5 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = "/usr/bin/perl -w"; - shellScript = "# Xcode auto-versioning script for Subversion\n# by Axel Andersson, modified by Daniel Jalkut to add\n# \"--revision HEAD\" to the svn info line, which allows\n# the latest revision to always be used.\n\n# See http://www.red-sweater.com/blog/23/automatic-build-sub-versioning-in-xcode#finalscript\n\nuse strict;\n\ndie \"$0: Must be run from Xcode\" unless $ENV{\"BUILT_PRODUCTS_DIR\"};\n\n# Get the current subversion revision number and use it to set the CFBundleVersion value\nmy $REV = `/opt/local/bin/svnversion -n ./`;\nmy $INFO = \"$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Contents/Info.plist\";\n\nmy $version = $REV;\n\n# (Match the last group of digits and optional letter M/S):\n\n# ugly yet functional (barely) regex by Daniel Jalkut:\n#$version =~ s/([\\d]*:)(\\d+[M|S]*).*/$2/;\n\n# better yet still functional regex via Kevin \"Regex Nerd\" Ballard\n#($version =~ m/\\d+[MS]*$/) && ($version = $&);\n\n# or a custom version that ignores the M and S flags by Pete Yandell\n($version =~ m/(\\d+)[MS]*$/) && ($version = $1);\n\ndie \"$0: No Subversion revision found\" unless $version;\n\nopen(FH, \"$INFO\") or die \"$0: $INFO: $!\";\nmy $info = join(\"\", );\nclose(FH);\n\n$info =~ s/([\\t ]+CFBundleVersion<\\/key>\\n[\\t ]+).*?(<\\/string>)/$1$version$2/;\n\nopen(FH, \">$INFO\") or die \"$0: $INFO: $!\";\nprint FH $info;\nclose(FH);"; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ C12C7FF005AECD9D0066E007 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -327,6 +323,7 @@ C12C7FF105AECD9D0066E007 /* main.m in Sources */, C12C7FF205AECD9D0066E007 /* PatchbayDocument.m in Sources */, C12C7FF305AECD9D0066E007 /* EndpointTableDataSource.m in Sources */, + 3092FEA6239969AF0057E329 /* TableDataSource.m in Sources */, C12C7FF405AECD9D0066E007 /* KeyTableView.m in Sources */, C12C7FF505AECD9D0066E007 /* Patch.m in Sources */, C12C7FF605AECD9D0066E007 /* PatchTableCellData.m in Sources */, @@ -353,7 +350,7 @@ 089C165FFE840EACC02AAC07 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( - 089C1660FE840EACC02AAC07 /* English */, + 3092FEA323995FA90057E329 /* en */, ); name = InfoPlist.strings; sourceTree = ""; @@ -361,7 +358,7 @@ 2A37F4B4FDCFA73011CA2CEA /* PatchbayDocument.nib */ = { isa = PBXVariantGroup; children = ( - 2A37F4B5FDCFA73011CA2CEA /* PatchbayDocument.nib */, + 3092FEA223995FA60057E329 /* Base */, ); name = PatchbayDocument.nib; sourceTree = ""; @@ -369,7 +366,7 @@ 2A37F4B6FDCFA73011CA2CEA /* MainMenu.nib */ = { isa = PBXVariantGroup; children = ( - 2A37F4B7FDCFA73011CA2CEA /* English */, + 3092FEA123995FA60057E329 /* Base */, ); name = MainMenu.nib; sourceTree = ""; @@ -380,22 +377,28 @@ C1010469088A0A7C001DFE4F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - FRAMEWORK_SEARCH_PATHS = ../PYMIDI/build; + FRAMEWORK_SEARCH_PATHS = ""; GCC_DYNAMIC_NO_PIC = NO; - GCC_ENABLE_FIX_AND_CONTINUE = YES; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_USE_GCC3_PFE_SUPPORT = NO; - HEADER_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET = 10.10; + ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.notahat.MIDIPatchBay; PRODUCT_NAME = "MIDI Patchbay"; + SDKROOT = macosx; SECTORDER_FLAGS = ""; + VALID_ARCHS = x86_64; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -409,21 +412,29 @@ C101046A088A0A7C001DFE4F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = YES; - FRAMEWORK_SEARCH_PATHS = ../PYMIDI/build; - GCC_ENABLE_FIX_AND_CONTINUE = NO; + FRAMEWORK_SEARCH_PATHS = ""; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_OPTIMIZATION_LEVEL = 3; GCC_USE_GCC3_PFE_SUPPORT = NO; - HEADER_SEARCH_PATHS = ""; + HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Applications"; LIBRARY_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.2; + MACOSX_DEPLOYMENT_TARGET = 10.10; + ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.notahat.MIDIPatchBay; PRODUCT_NAME = "MIDI Patchbay"; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=ppc]" = macosx; SECTORDER_FLAGS = ""; + VALID_ARCHS = x86_64; WARNING_CFLAGS = ( "-Wmost", "-Wno-four-char-constants", @@ -437,32 +448,83 @@ C101046D088A0A7C001DFE4F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - HEADER_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.2; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; - USER_HEADER_SEARCH_PATHS = .; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + USER_HEADER_SEARCH_PATHS = ""; + VALID_ARCHS = x86_64; }; name = Debug; }; C101046E088A0A7C001DFE4F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = YES; - ARCHS = ( - i386, - ppc, - ); + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_NO_COMMON_BLOCKS = YES; GCC_VERSION_i386 = 4.0; GCC_VERSION_ppc = 3.3; - HEADER_SEARCH_PATHS = ""; - MACOSX_DEPLOYMENT_TARGET = 10.2; - "MACOSX_DEPLOYMENT_TARGET[arch=i386]" = 10.4; - "MACOSX_DEPLOYMENT_TARGET[arch=ppc]" = 10.3; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; - "SDKROOT[arch=i386]" = "$(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk"; - "SDKROOT[arch=ppc]" = /Developer/SDKs/MacOSX10.3.9.sdk; - USER_HEADER_SEARCH_PATHS = .; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "$(PROJECT_DIR)"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + "SDKROOT[arch=i386]" = macosx; + "SDKROOT[arch=ppc]" = macosx; + USER_HEADER_SEARCH_PATHS = ""; + VALID_ARCHS = x86_64; }; name = Release; }; diff --git a/MIDI Patchbay.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/MIDI Patchbay.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/MIDI Patchbay.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/MIDI Patchbay.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/MIDI Patchbay.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/MIDI Patchbay.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/MIDI Patchbay.xcodeproj/xcshareddata/xcschemes/MIDI Patchbay.xcscheme b/MIDI Patchbay.xcodeproj/xcshareddata/xcschemes/MIDI Patchbay.xcscheme new file mode 100644 index 0000000..a2b2f0d --- /dev/null +++ b/MIDI Patchbay.xcodeproj/xcshareddata/xcschemes/MIDI Patchbay.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Model Classes/Patch.m b/Model Classes/Patch.m index 4736a20..76872e7 100644 --- a/Model Classes/Patch.m +++ b/Model Classes/Patch.m @@ -17,11 +17,11 @@ @implementation Patch #pragma mark Initialisation -- (Patch*)initWithInput:(PYMIDIEndpoint*)newInput output:(PYMIDIEndpoint*)newOutput +- (void)setupInput:(PYMIDIEndpoint*)newInput output:(PYMIDIEndpoint*)newOutput { isInLimbo = YES; - input = [newInput retain]; + input = newInput; [input addReceiver:self]; shouldFilterChannel = NO; @@ -40,44 +40,53 @@ - (Patch*)initWithInput:(PYMIDIEndpoint*)newInput output:(PYMIDIEndpoint*)newOut shouldTransmitClock = NO; - output = [newOutput retain]; + output = newOutput; [output addSender:self]; isEnabled = YES; - - return self; +} + +- (Patch*)initWithInput:(PYMIDIEndpoint*)newInput output:(PYMIDIEndpoint*)newOutput +{ + self = [super init]; + if (self) { + [self setupInput:newInput output:newOutput]; + } + return self; } - (Patch*)initFromPatch:(Patch*)patch { - BOOL useNextMIDIChannel = NO; - - [self initWithInput:[patch input] output:[patch output]]; - - if ([patch shouldFilterChannel]) { - [self setShouldFilterChannel:YES]; - channelMask = [patch channelMask]; - } - - [self setShouldAllowNotes:[patch shouldAllowNotes]]; - - if ([patch shouldFilterRange]) { - [self setShouldFilterRange:YES]; - if ([patch highestAllowedNote] < 127) - [self setLowestAllowedNote:[patch highestAllowedNote] + 1]; - useNextMIDIChannel = YES; - } - - if ([patch shouldRemapChannel]) { - [self setShouldRemapChannel:YES]; - // *** Need to bounds check this: - if (useNextMIDIChannel) - [self setRemappingChannel:[patch remappingChannel] + 1]; - else - [self setRemappingChannel:[patch remappingChannel]]; - } - + self = [super init]; + if (self) { + [self setupInput:[patch input] output:[patch output]]; + + BOOL useNextMIDIChannel = NO; + + if ([patch shouldFilterChannel]) { + [self setShouldFilterChannel:YES]; + channelMask = [patch channelMask]; + } + + [self setShouldAllowNotes:[patch shouldAllowNotes]]; + + if ([patch shouldFilterRange]) { + [self setShouldFilterRange:YES]; + if ([patch highestAllowedNote] < 127) + [self setLowestAllowedNote:[patch highestAllowedNote] + 1]; + useNextMIDIChannel = YES; + } + + if ([patch shouldRemapChannel]) { + [self setShouldRemapChannel:YES]; + // *** Need to bounds check this: + if (useNextMIDIChannel) + [self setRemappingChannel:[patch remappingChannel] + 1]; + else + [self setRemappingChannel:[patch remappingChannel]]; + } + } return self; } @@ -85,12 +94,7 @@ - (Patch*)initFromPatch:(Patch*)patch - (void)dealloc { [input removeReceiver:self]; - [input release]; - [output removeSender:self]; - [output release]; - - [super dealloc]; } @@ -314,10 +318,7 @@ - (PYMIDIEndpoint*)input - (void)setInput:(PYMIDIEndpoint*)newInput { [input removeReceiver:self]; - - [input autorelease]; - input = [newInput retain]; - + input = newInput; [input addReceiver:self]; } @@ -476,10 +477,7 @@ - (PYMIDIEndpoint*)output - (void)setOutput:(PYMIDIEndpoint*)newOutput { [output removeSender:self]; - - [output autorelease]; - output = [newOutput retain]; - + output = newOutput; [output addSender:self]; } @@ -503,7 +501,7 @@ - (void)setOutput:(PYMIDIEndpoint*)newOutput for (i = 0; i < packetList->numPackets; i++) packet = MIDIPacketNext (packet); - return (void*)packet - (void*)packetList; + return (unsigned int)((void*)packet - (void*)packetList); } diff --git a/PYMIDI/PYMIDIEndpoint.h b/PYMIDI/PYMIDIEndpoint.h index 4455b82..54ce6f9 100644 --- a/PYMIDI/PYMIDIEndpoint.h +++ b/PYMIDI/PYMIDIEndpoint.h @@ -7,9 +7,9 @@ @interface PYMIDIEndpoint : NSObject { MIDIEndpointRef midiEndpointRef; - NSString* name; + NSMutableString* name; SInt32 uniqueID; - NSString* displayName; + NSMutableString* displayName; NSMutableSet* receivers; NSMutableSet* senders; @@ -37,10 +37,10 @@ /* Resets the name and uniqueID from the MIDIEndpointRef */ - (void)setPropertiesFromMIDIEndpoint; -- (NSString*)name; +- (NSMutableString*)name; - (BOOL)setName:(NSString*)newName; -- (NSString*)displayName; +- (NSMutableString*)displayName; - (NSComparisonResult)compareByDisplayName:(PYMIDIEndpoint*)endpoint; - (SInt32)uniqueID; diff --git a/PYMIDI/PYMIDIEndpoint.m b/PYMIDI/PYMIDIEndpoint.m index 46c0cdd..8779552 100644 --- a/PYMIDI/PYMIDIEndpoint.m +++ b/PYMIDI/PYMIDIEndpoint.m @@ -36,7 +36,7 @@ - (id)initWithName:(NSString*)newName uniqueID:(SInt32)newUniqueID { self = [self init]; - name = [newName retain]; + name = newName.mutableCopy; uniqueID = newUniqueID; return self; @@ -57,12 +57,6 @@ - (id)initWithMIDIEndpointRef:(MIDIEndpointRef)newMIDIEndpointRef; - (void)dealloc { [self stopIO]; - [name release]; - [displayName release]; - [receivers release]; - [senders release]; - - [super dealloc]; } @@ -96,7 +90,7 @@ - (PYMIDIEndpointDescriptor*)descriptor - (void)setPropertiesFromMIDIEndpoint { - if (midiEndpointRef == NULL) return; + if (!midiEndpointRef) return; [self setUniqueIDFromMIDIEndpoint]; [self setNameFromMIDIEndpoint]; @@ -112,8 +106,7 @@ - (void)setUniqueIDFromMIDIEndpoint - (void)setNameFromMIDIEndpoint { - [name release]; - name = [PYMIDIGetEndpointName (midiEndpointRef) retain]; + name = [PYMIDIGetEndpointName (midiEndpointRef) mutableCopy]; } @@ -121,8 +114,6 @@ - (void)setDisplayNameFromMIDIEndpoint { CFDataRef externalIDs; OSStatus result; - - [displayName release]; NSMutableArray* names = [NSMutableArray arrayWithCapacity:0]; @@ -158,7 +149,7 @@ - (void)setDisplayNameFromMIDIEndpoint CFStringRef externalName; result = MIDIObjectGetStringProperty (externalDevice, kMIDIPropertyName, &externalName); if (result == noErr) { - [names addObject:[NSString stringWithString:(NSString*)externalName]]; + [names addObject:[NSString stringWithString:(__bridge NSString*)externalName]]; CFRelease (externalName); } } @@ -177,15 +168,13 @@ - (void)setDisplayNameFromMIDIEndpoint // Generate the display name from the info we've gathered if ([names count] > 0) - displayName = [names componentsJoinedByString:@", "]; + displayName = [[names componentsJoinedByString:@", "] mutableCopy]; else { if (name != nil) displayName = name; else - displayName = @"UNKNOWN DEVICE"; + displayName = [@"UNKNOWN DEVICE" mutableCopy]; } - - [displayName retain]; } @@ -195,21 +184,20 @@ - (NSString*)name } -- (BOOL)setName:(NSString*)newName +- (BOOL)setName:(NSMutableString*)newName { PYMIDIManager* manager = [PYMIDIManager sharedInstance]; OSStatus result; [manager disableNotifications]; - result = MIDIObjectSetStringProperty (midiEndpointRef, kMIDIPropertyName, (CFStringRef)newName); + result = MIDIObjectSetStringProperty (midiEndpointRef, kMIDIPropertyName, (__bridge CFStringRef)newName); [manager enableNotifications]; if (result == noErr) { - [name autorelease]; - name = [newName retain]; - displayName = [newName retain]; + name = newName; + displayName = newName; return YES; } else @@ -217,10 +205,10 @@ - (BOOL)setName:(NSString*)newName } -- (NSString*)displayName +- (NSMutableString*)displayName { if ([self isOffline]) - return [NSString stringWithFormat:@"%@ (offline)", displayName]; + return [NSMutableString stringWithFormat:@"%@ (offline)", displayName]; else return displayName; } @@ -282,7 +270,7 @@ - (BOOL)isIACBus BOOL isIACBus = driverName != nil && - [@"com.apple.AppleMIDIIACDriver" isEqualToString:(NSString*)driverName]; + [@"com.apple.AppleMIDIIACDriver" isEqualToString:(__bridge NSString*)driverName]; if (driverName != nil) CFRelease (driverName); @@ -301,7 +289,7 @@ - (BOOL)isOffline SInt32 isOffline; OSStatus result; - if (midiEndpointRef == nil) return YES; + if (!midiEndpointRef) return YES; result = MIDIObjectGetIntegerProperty (midiEndpointRef, kMIDIPropertyOffline, &isOffline); return result == noErr && isOffline; diff --git a/PYMIDI/PYMIDIEndpointDescriptor.h b/PYMIDI/PYMIDIEndpointDescriptor.h index c806e15..d6ca7cd 100644 --- a/PYMIDI/PYMIDIEndpointDescriptor.h +++ b/PYMIDI/PYMIDIEndpointDescriptor.h @@ -10,8 +10,6 @@ - (id)initWithName:(NSString*)newName uniqueID:(SInt32)newUniqueID; -- (void)dealloc; - - (id)initWithCoder:(NSCoder*)coder; - (void)encodeWithCoder:(NSCoder*)coder; diff --git a/PYMIDI/PYMIDIEndpointDescriptor.m b/PYMIDI/PYMIDIEndpointDescriptor.m index a14bf0f..1875e49 100644 --- a/PYMIDI/PYMIDIEndpointDescriptor.m +++ b/PYMIDI/PYMIDIEndpointDescriptor.m @@ -6,7 +6,7 @@ @implementation PYMIDIEndpointDescriptor + (id)descriptorWithName:(NSString*)newName uniqueID:(SInt32)newUniqueID; { - return [[[PYMIDIEndpointDescriptor alloc] initWithName:newName uniqueID:newUniqueID] autorelease]; + return [[PYMIDIEndpointDescriptor alloc] initWithName:newName uniqueID:newUniqueID]; } @@ -14,26 +14,18 @@ - (id)initWithName:(NSString*)newName uniqueID:(SInt32)newUniqueID { self = [super init]; - name = [newName retain]; + name = newName; uniqueID = newUniqueID; return self; } -- (void)dealloc -{ - [name release]; - - [super dealloc]; -} - - - (id)initWithCoder:(NSCoder*)coder { self = [super init]; - name = [[coder decodeObjectForKey:@"name"] retain]; + name = [coder decodeObjectForKey:@"name"]; uniqueID = [coder decodeInt32ForKey:@"uniqueID"]; return self; diff --git a/PYMIDI/PYMIDIEndpointSet.h b/PYMIDI/PYMIDIEndpointSet.h index c58b48e..54b89de 100644 --- a/PYMIDI/PYMIDIEndpointSet.h +++ b/PYMIDI/PYMIDIEndpointSet.h @@ -3,7 +3,7 @@ @class PYMIDIEndpointDescriptor; @class PYMIDIEndpoint; -@interface PYMIDIEndpointSet : NSObject { +@interface PYMIDIEndpointSet : NSObject { NSArray* endpointArray; } @@ -11,7 +11,6 @@ + (id)endpointSetWithArray:(NSArray*)newEndpointArray; - (id)initWithEndpointArray:(NSArray*)newEndpointArray; -- (void)dealloc; - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object; - (id)unarchiver:(NSKeyedUnarchiver*)unarchiver didDecodeObject:(id)object; diff --git a/PYMIDI/PYMIDIEndpointSet.m b/PYMIDI/PYMIDIEndpointSet.m index 59695c2..0fbf968 100644 --- a/PYMIDI/PYMIDIEndpointSet.m +++ b/PYMIDI/PYMIDIEndpointSet.m @@ -8,7 +8,7 @@ @implementation PYMIDIEndpointSet + (id)endpointSetWithArray:(NSArray*)newEndpointArray { - return [[[PYMIDIEndpointSet alloc] initWithEndpointArray:newEndpointArray] retain]; + return [[PYMIDIEndpointSet alloc] initWithEndpointArray:newEndpointArray]; } @@ -17,21 +17,13 @@ - (id)initWithEndpointArray:(NSArray*)newEndpointArray self = [super init]; if (self != nil) { - endpointArray = [newEndpointArray retain]; + endpointArray = newEndpointArray; } return self; } -- (void)dealloc -{ - [endpointArray release]; - - [super dealloc]; -} - - - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object { if ([endpointArray containsObject:object]) @@ -44,7 +36,7 @@ - (id)archiver:(NSKeyedArchiver*)archiver willEncodeObject:(id)object - (id)unarchiver:(NSKeyedUnarchiver*)unarchiver didDecodeObject:(id)object { if ([object isMemberOfClass:[PYMIDIEndpointDescriptor class]]) - object = [[self endpointWithDescriptor:object] retain]; + object = [self endpointWithDescriptor:object]; // Note that we DON'T need to release the original object when doing a // subsitution. This is the opposite of what happens when we do it in diff --git a/PYMIDI/PYMIDIManager.h b/PYMIDI/PYMIDIManager.h index aa9f033..dc1223c 100644 --- a/PYMIDI/PYMIDIManager.h +++ b/PYMIDI/PYMIDIManager.h @@ -18,7 +18,6 @@ + (PYMIDIManager*)sharedInstance; - (PYMIDIManager*)init; -- (void)dealloc; - (MIDIClientRef)midiClientRef; @@ -43,4 +42,4 @@ - (NSString*)nameOfNote:(Byte)note; -@end \ No newline at end of file +@end diff --git a/PYMIDI/PYMIDIManager.m b/PYMIDI/PYMIDIManager.m index ab1af66..733f297 100644 --- a/PYMIDI/PYMIDIManager.m +++ b/PYMIDI/PYMIDIManager.m @@ -41,7 +41,7 @@ - (PYMIDIManager*)init { notificationsEnabled = NO; - MIDIClientCreate (CFSTR("PYMIDIManager"), midiNotifyProc, (void*)self, &midiClientRef); + MIDIClientCreate (CFSTR("PYMIDIManager"), midiNotifyProc, (__bridge void*)self, &midiClientRef); realSourceArray = [[NSMutableArray alloc] init]; realDestinationArray = [[NSMutableArray alloc] init]; @@ -57,17 +57,6 @@ - (PYMIDIManager*)init } -- (void)dealloc -{ - [realSourceArray release]; - [realDestinationArray release]; - - [noteNamesArray release]; - - [super dealloc]; -} - - - (MIDIClientRef)midiClientRef { return midiClientRef; @@ -112,6 +101,8 @@ - (void)processMIDINotification:(const MIDINotification*)message [[NSNotificationCenter defaultCenter] postNotificationName:@"PYMIDISetupChanged" object:self]; } break; + default: + break; } isHandlingNotification = NO; @@ -122,7 +113,7 @@ - (void)processMIDINotification:(const MIDINotification*)message static void midiNotifyProc (const MIDINotification* message, void* refCon) { - PYMIDIManager* manager = (PYMIDIManager*)refCon; + PYMIDIManager* manager = (__bridge PYMIDIManager*)refCon; [manager processMIDINotification:message]; } @@ -143,7 +134,7 @@ - (void)updateRealSources // Find any non-virtual endpoints that we don't already know about int i; - int count = MIDIGetNumberOfSources(); + int count = (int)MIDIGetNumberOfSources(); for (i = 0; i < count; i++) { MIDIEndpointRef midiEndpointRef = MIDIGetSource (i); @@ -152,6 +143,15 @@ - (void)updateRealSources [self realSourceWithMIDIEndpointRef:midiEndpointRef] == nil) { endpoint = [[PYMIDIRealSource alloc] initWithMIDIEndpointRef:midiEndpointRef]; + int dups; + NSArray* filteredArray; + NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"SELF.displayName beginswith[c] %@",endpoint.displayName]; + filteredArray = [realSourceArray filteredArrayUsingPredicate:bPredicate]; + dups = (int)[filteredArray count]; + if(dups>0) { + [[endpoint displayName] appendString:[NSString stringWithFormat:@" %d",dups+1]]; + } + [realSourceArray addObject:endpoint]; } } @@ -197,7 +197,6 @@ - (PYMIDIEndpoint*)realSourceWithDescriptor:(PYMIDIEndpointDescriptor*)descripto if (endpoint == nil) { endpoint = [[PYMIDIRealSource alloc] initWithDescriptor:descriptor]; [realSourceArray addObject:endpoint]; - [endpoint release]; } return endpoint; @@ -220,7 +219,7 @@ - (void)updateRealDestinations // Find any non-virtual endpoints that we don't already know about int i; - int count = MIDIGetNumberOfDestinations(); + int count = (int)MIDIGetNumberOfDestinations(); for (i = 0; i < count; i++) { MIDIEndpointRef midiEndpointRef = MIDIGetDestination (i); @@ -229,6 +228,15 @@ - (void)updateRealDestinations [self realDestinationWithMIDIEndpointRef:midiEndpointRef] == nil) { endpoint = [[PYMIDIRealDestination alloc] initWithMIDIEndpointRef:midiEndpointRef]; + int dups; + NSArray* filteredArray; + NSPredicate *bPredicate = [NSPredicate predicateWithFormat:@"SELF.displayName beginswith[c] %@",endpoint.displayName]; + filteredArray = [realDestinationArray filteredArrayUsingPredicate:bPredicate]; + dups = (int)[filteredArray count]; + if(dups>0) { + [[endpoint displayName] appendString:[NSString stringWithFormat:@" %d",dups+1]]; + } + [realDestinationArray addObject:endpoint]; } } @@ -274,7 +282,6 @@ - (PYMIDIEndpoint*)realDestinationWithDescriptor:(PYMIDIEndpointDescriptor*)desc if (endpoint == nil) { endpoint = [[PYMIDIRealDestination alloc] initWithDescriptor:descriptor]; [realDestinationArray addObject:endpoint]; - [endpoint release]; } return endpoint; @@ -317,4 +324,4 @@ - (NSString*)nameOfNote:(Byte)note -@end \ No newline at end of file +@end diff --git a/PYMIDI/PYMIDIRealDestination.m b/PYMIDI/PYMIDIRealDestination.m index 62fcb84..365d83a 100644 --- a/PYMIDI/PYMIDIRealDestination.m +++ b/PYMIDI/PYMIDIRealDestination.m @@ -20,9 +20,8 @@ - (id)initWithCoder:(NSCoder*)coder newUniqueID = [coder decodeInt32ForKey:@"uniqueID"]; descriptor = [PYMIDIEndpointDescriptor descriptorWithName:newName uniqueID:newUniqueID]; - - [self release]; - return [[manager realDestinationWithDescriptor:descriptor] retain]; + + return (PYMIDIRealDestination *)[manager realDestinationWithDescriptor:descriptor]; } @@ -33,10 +32,10 @@ - (void)syncWithMIDIEndpoint if (midiEndpointRef && PYMIDIDoesDestinationStillExist (midiEndpointRef)) newEndpointRef = midiEndpointRef; else - newEndpointRef = NULL; + newEndpointRef = 0; - if (newEndpointRef == NULL) newEndpointRef = PYMIDIGetDestinationByUniqueID (uniqueID); - if (newEndpointRef == NULL) newEndpointRef = PYMIDIGetDestinationByName (name); + if (!newEndpointRef) newEndpointRef = PYMIDIGetDestinationByUniqueID (uniqueID); + if (!newEndpointRef) newEndpointRef = PYMIDIGetDestinationByName (name); if (midiEndpointRef != newEndpointRef) { [self stopIO]; @@ -50,7 +49,7 @@ - (void)syncWithMIDIEndpoint - (void)startIO { - if (midiEndpointRef == nil || midiPortRef != nil) return; + if ( !midiEndpointRef || midiPortRef ) return; MIDIOutputPortCreate ( [[PYMIDIManager sharedInstance] midiClientRef], CFSTR("PYMIDIRealDestination"), @@ -61,16 +60,16 @@ - (void)startIO - (void)stopIO { - if (midiPortRef == nil) return; + if (!midiPortRef) return; MIDIPortDispose (midiPortRef); - midiPortRef = nil; + midiPortRef = 0; } - (void)processMIDIPacketList:(const MIDIPacketList*)packetList sender:(id)sender { - if (midiEndpointRef != NULL && midiPortRef != NULL) + if (midiEndpointRef && midiPortRef) MIDISend (midiPortRef, midiEndpointRef, packetList); } diff --git a/PYMIDI/PYMIDIRealEndpoint.m b/PYMIDI/PYMIDIRealEndpoint.m index 4ae7d2a..34cb3ec 100644 --- a/PYMIDI/PYMIDIRealEndpoint.m +++ b/PYMIDI/PYMIDIRealEndpoint.m @@ -10,7 +10,7 @@ - (id)initWithMIDIEndpointRef:(MIDIEndpointRef)newMIDIEndpointRef self = [super initWithMIDIEndpointRef:newMIDIEndpointRef]; if (self != nil) { - midiPortRef = nil; + midiPortRef = 0; } return self; @@ -22,7 +22,7 @@ - (id)initWithDescriptor:(PYMIDIEndpointDescriptor*)descriptor self = [super initWithName:[descriptor name] uniqueID:[descriptor uniqueID]]; if (self != nil) { - midiPortRef = nil; + midiPortRef = 0; } return self; @@ -36,7 +36,7 @@ - (void)syncWithMIDIEndpoint - (BOOL)ioIsRunning { - return midiPortRef != nil; + return !midiPortRef; } diff --git a/PYMIDI/PYMIDIRealSource.m b/PYMIDI/PYMIDIRealSource.m index 9623a91..1cf6121 100644 --- a/PYMIDI/PYMIDIRealSource.m +++ b/PYMIDI/PYMIDIRealSource.m @@ -23,9 +23,8 @@ - (id)initWithCoder:(NSCoder*)coder newUniqueID = [coder decodeInt32ForKey:@"uniqueID"]; descriptor = [PYMIDIEndpointDescriptor descriptorWithName:newName uniqueID:newUniqueID]; - - [self release]; - return [[manager realSourceWithDescriptor:descriptor] retain]; + + return (PYMIDIRealSource *)[manager realSourceWithDescriptor:descriptor]; } @@ -37,10 +36,10 @@ - (void)syncWithMIDIEndpoint if (midiEndpointRef && PYMIDIDoesSourceStillExist (midiEndpointRef)) newEndpointRef = midiEndpointRef; else - newEndpointRef = NULL; + newEndpointRef = 0; - if (newEndpointRef == NULL) newEndpointRef = PYMIDIGetSourceByUniqueID (uniqueID); - if (newEndpointRef == NULL) newEndpointRef = PYMIDIGetSourceByName (name); + if (!newEndpointRef) newEndpointRef = PYMIDIGetSourceByUniqueID (uniqueID); + if (!newEndpointRef) newEndpointRef = PYMIDIGetSourceByName (name); if (midiEndpointRef != newEndpointRef) { [self stopIO]; @@ -54,11 +53,11 @@ - (void)syncWithMIDIEndpoint - (void)startIO { - if (midiEndpointRef == nil || midiPortRef != nil) return; + if (!midiEndpointRef || midiPortRef ) return; MIDIInputPortCreate ( [[PYMIDIManager sharedInstance] midiClientRef], CFSTR("PYMIDIRealSource"), - midiReadProc, (void*)self, &midiPortRef + midiReadProc, (__bridge void*)self, &midiPortRef ); MIDIPortConnectSource (midiPortRef, midiEndpointRef, nil); } @@ -66,11 +65,11 @@ - (void)startIO - (void)stopIO { - if (midiPortRef == nil) return; + if (!midiPortRef) return; MIDIPortDisconnectSource (midiPortRef, midiEndpointRef); MIDIPortDispose (midiPortRef); - midiPortRef = nil; + midiPortRef = 0; } @@ -81,22 +80,21 @@ - (void)processMIDIPacketList:(const MIDIPacketList*)packetList sender:(id)sende // means that we can do memory allocation freely in the processing and // it will all get automatically cleaned up once we've passed the data // on, which is a win. - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - - NSEnumerator* enumerator = [receivers objectEnumerator]; - id receiver; + @autoreleasepool { + NSEnumerator* enumerator = [receivers objectEnumerator]; + id receiver; + + while ((receiver = [[enumerator nextObject] nonretainedObjectValue])) + [receiver processMIDIPacketList:packetList sender:self]; - while (receiver = [[enumerator nextObject] nonretainedObjectValue]) - [receiver processMIDIPacketList:packetList sender:self]; - - [pool release]; + } } static void midiReadProc (const MIDIPacketList* packetList, void* createRefCon, void* connectRefConn) { - PYMIDIRealSource* source = (PYMIDIRealSource*)createRefCon; + PYMIDIRealSource* source = (__bridge PYMIDIRealSource*)createRefCon; [source processMIDIPacketList:packetList sender:source]; } diff --git a/PYMIDI/PYMIDIUtils.m b/PYMIDI/PYMIDIUtils.m index a82fe1c..178b22e 100644 --- a/PYMIDI/PYMIDIUtils.m +++ b/PYMIDI/PYMIDIUtils.m @@ -27,28 +27,24 @@ // Stick the two names together, handling all the cases where one or the other doesn't exist NSString* name; - if (endpointName != nil) { if (deviceName != nil) { - bool endpointNameBeginsWithDeviceName = - CFStringCompareWithOptions ( - endpointName, deviceName, - CFRangeMake(0, CFStringGetLength (deviceName)), - kCFCompareCaseInsensitive - ) == kCFCompareEqualTo; + NSString *endpointNameNS = (__bridge NSString *)endpointName; + NSString *deviceNameNS = (__bridge NSString *)deviceName; + bool endpointNameBeginsWithDeviceName = [endpointNameNS compare:deviceNameNS options:NSCaseInsensitiveSearch] == NSOrderedSame; if (endpointNameBeginsWithDeviceName) - name = [NSString stringWithString:(NSString*)endpointName]; + name = [NSString stringWithString:(__bridge NSString*)endpointName]; else name = [NSString stringWithFormat:@"%@ %@", deviceName, endpointName]; } else - name = [NSString stringWithString:(NSString*)endpointName]; + name = [NSString stringWithString:(__bridge NSString*)endpointName]; } else { if (deviceName != nil) - name = [NSString stringWithString:(NSString*)deviceName]; + name = [NSString stringWithString:(__bridge NSString*)deviceName]; else name = nil; // Hopefully we'll never get here! } @@ -95,7 +91,7 @@ return endpoint; } - return NULL; + return 0; } @@ -113,7 +109,7 @@ if (name != nil && [name isEqualToString:nameToMatch]) return endpoint; } - return NULL; + return 0; } @@ -151,7 +147,7 @@ return endpoint; } - return NULL; + return 0; } @@ -168,7 +164,7 @@ if (name != nil && [name isEqualToString:nameToMatch]) return endpoint; } - return NULL; + return 0; } @@ -180,14 +176,14 @@ MIDIEndpointRef endpoint; SInt32 usedID; - count = MIDIGetNumberOfSources(); + count = (int)MIDIGetNumberOfSources(); for (index = 0; index < count; index++) { endpoint = MIDIGetSource (index); MIDIObjectGetIntegerProperty (endpoint, kMIDIPropertyUniqueID, &usedID); if (usedID == uniqueID) return true; } - count = MIDIGetNumberOfDestinations(); + count = (int)MIDIGetNumberOfDestinations(); for (index = 0; index < count; index++) { endpoint = MIDIGetDestination (index); MIDIObjectGetIntegerProperty (endpoint, kMIDIPropertyUniqueID, &usedID); @@ -205,7 +201,7 @@ static SInt32 sequence = 0; do { - uniqueID = time (NULL) + sequence++; + uniqueID = (SInt32)time(NULL) + sequence++; } while (PYMIDIIsUniqueIDInUse (uniqueID)); return uniqueID; @@ -215,8 +211,8 @@ Boolean PYMIDIIsEndpointNameTaken (NSString* name) { - return PYMIDIGetSourceByName (name) != NULL || - PYMIDIGetDestinationByName (name) != NULL; + return PYMIDIGetSourceByName (name) || + PYMIDIGetDestinationByName (name) ; } diff --git a/PYMIDI/PYMIDIVirtualDestination.m b/PYMIDI/PYMIDIVirtualDestination.m index 0ff7865..c83ecca 100644 --- a/PYMIDI/PYMIDIVirtualDestination.m +++ b/PYMIDI/PYMIDIVirtualDestination.m @@ -20,7 +20,7 @@ - (id)initWithName:(NSString*)newName // we're done creating it. [manager disableNotifications]; - MIDIDestinationCreate ([manager midiClientRef], (CFStringRef)newName, midiReadProc, self, &newEndpoint); + MIDIDestinationCreate ([manager midiClientRef], (__bridge CFStringRef)newName, midiReadProc, (__bridge void * _Nullable)(self), &newEndpoint); // This code works around a bug in OS X 10.1 that causes // new sources/destinations to be created without unique IDs. @@ -44,7 +44,6 @@ - (id)initWithName:(NSString*)newName - (void)processMIDIPacketList:(const MIDIPacketList*)packetList sender:(id)sender { - NSAutoreleasePool* pool; NSEnumerator* enumerator; id receiver; @@ -55,20 +54,18 @@ - (void)processMIDIPacketList:(const MIDIPacketList*)packetList sender:(id)sende // means that we can do memory allocation freely in the processing and // it will all get automatically cleaned up once we've passed the data // on, which is a win. - pool = [[NSAutoreleasePool alloc] init]; - - enumerator = [receivers objectEnumerator]; - while (receiver = [[enumerator nextObject] nonretainedObjectValue]) - [receiver processMIDIPacketList:packetList sender:self]; - - [pool release]; + @autoreleasepool { + enumerator = [receivers objectEnumerator]; + while ((receiver = [[enumerator nextObject] nonretainedObjectValue])) + [receiver processMIDIPacketList:packetList sender:self]; + } } static void midiReadProc (const MIDIPacketList* packetList, void* createRefCon, void* connectRefConn) { - PYMIDIVirtualDestination* destination = (PYMIDIVirtualDestination*)createRefCon; + PYMIDIVirtualDestination* destination = (__bridge PYMIDIVirtualDestination*)createRefCon; [destination processMIDIPacketList:packetList sender:destination]; } diff --git a/PYMIDI/PYMIDIVirtualEndpoint.m b/PYMIDI/PYMIDIVirtualEndpoint.m index 6dc9ed3..33a67eb 100644 --- a/PYMIDI/PYMIDIVirtualEndpoint.m +++ b/PYMIDI/PYMIDIVirtualEndpoint.m @@ -18,8 +18,6 @@ - (void)dealloc [manager disableNotifications]; MIDIEndpointDispose (midiEndpointRef); [manager enableNotifications]; - - [super dealloc]; } diff --git a/PYMIDI/PYMIDIVirtualSource.m b/PYMIDI/PYMIDIVirtualSource.m index 893a793..5599f1c 100644 --- a/PYMIDI/PYMIDIVirtualSource.m +++ b/PYMIDI/PYMIDIVirtualSource.m @@ -17,7 +17,7 @@ - (id)initWithName:(NSString*)newName // we're done creating it. [manager disableNotifications]; - MIDISourceCreate ([manager midiClientRef], (CFStringRef)newName, &newEndpoint); + MIDISourceCreate ([manager midiClientRef], (__bridge CFStringRef)newName, &newEndpoint); // This code works around a bug in OS X 10.1 that causes // new sources/destinations to be created without unique IDs. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0af13a --- /dev/null +++ b/README.md @@ -0,0 +1,175 @@ +MIDI Patchbay (Version 1.0.4) +============================= + +MIDI patchbay lets you hook up your various pieces of MIDI software and hardware and pass MIDI data between them, applying assorted filters on the way. Things like key splits, MIDI channel remapping, transposition, etc. are made very simple. + +* [License](#license) +* [Requirements](#requirements) +* [Using MIDI Patchbay](#usage) +* [Known Problems](#problems) +* [Future Additions](#future) +* [Credits](#credits) +* [Version History](#history) + +License +----------------------------- + +This software is distributed under the terms of [Pete's Public License](License.html). It's free (as in beer) but the license sets out a few restrictions on its distribution and modification. + +If you use it I only ask for one thing: [send me an e-mail](#mailto:pete@notahat.com). Tell me what you use it for, anything you like and don't like, anything you'd like to see in future versions, etc. + +For other ways to reward me for producing this, see[my web site](#http://www.notahat.com/software/). + +Requirements +--------------------------------------- + +* Mac OS X 10.2 or later +* A MIDI interface with OS X drivers +* Other MIDI software such as a sequencer or soft synth + +If you're looking for MIDI interface drivers or audio and MIDI applications for Mac OS X, check out my [links page](#http://pete.yandell.com/links/). + +Using MIDI Patchbay +--------------------------------------- + +Each MIDI Patchbay document window is divided into two parts: a patch list on the left and a set of patch controls on the right. + +### The Patch List + +Each patch in the list represents a connection between a MIDI input and MIDI output. The patch's input is shown on the left of the arrow and the patch's output on the right. + +Below each patch's arrow is a description of the data passed by the patch and any filters that are applied to the data. + +Each patch also has a checkbox that can be used to enable or disable it. + +New patches can be created with the *Add patch* button. MIDI Patchbay tries to create new patches intelligently based on the currently selected patch. + +### The Patch Controls + +The patch controls configure the input, output and filters for the currently selected patch. + +The MIDI input and output for the patch can be selected from the appropriate popup menus. As well as the MIDI interfaces connected to your system, you can use the *Edit virtual inputs/outputs...* options to create inputs and outputs that will be visible to other MIDI software. + +Under the various tabs are options allowing you to filter the MIDI data for the currently selected patch. + +### Example: Creating a Keyboard Split + +* Create a new MIDI Patchbay document +* Select the first patch in the list on the left +* From the *MIDI Input* popup menu choose your MIDI interface +* From the *MIDI Output* popup menu, select *Edit virtual outputs...* +* Create a new output named *Example output* and click *OK* +* Select *Example output* in the *MIDI Output* popup menu +* In the *Channels* tab, check the *Remap all data to MIDI channel:* button and choose channel 1 +* In the *Notes* tab, check the *Only allow notes in the range:* button and slide the lower slider until the note A4 is selected +* Click the *Add Patch* button. Note that the new patch created is intelligently based on the currently selected patch and already has the other half of the keyboard selected as its range of filtered notes and has its traffic remapped to channel 2. +* Open your favourite synth software and select *Example Output* as its MIDI input + +Now any notes up to A4 will be sent your synth software's MIDI channel 1 and notes above A4 will go to channel 2. + +Known Problems +------------------------------------- + +### Creating a MIDI routing loop can crash OS X + +For example, creating a patch that receives from and sends to the one IAC bus is a bad idea. As soon as MIDI data gets sent to that IAC bus the entire computer will lock up. + +You also have to be careful not to create indirect loops, as can easily happen with Logic. + +### Using MIDI Patchbay with Logic + +Using MIDI Patchbay with Logic can crash the computer, but the problem is easily worked around. + +If you open a default song in Logic it will open with a MIDI track enabled as MIDI thru. The default MIDI instrument is set to *all* for the OUT port. + +If your MIDI Patchbay document contains a patch that routes from a virtual input to a virtual output, this creates a MIDI routing loop. Logic sends instrument settings into this loop at song-open and, hence, a crash occurs. + +You can work around it by first disabling the patches in MIDI Patchbay, opening Logic and turning off MIDI Thru, and then re-enabling the patches in MIDI Patchbay. + +### MIDI and Classic + +Some people have reported problems when trying to use Classic and a MIDI interface simultaneously. It seems that Classic will try to grab control of USB devices. When using this software, you should make sure that Classic is not running. + +### Deleting virtual inputs and outputs + +Sometimes trying to delete a virtual input or output will give you a message saying that the input or output is in use by a patch and can't be deleted even when it isn't. Saving, closing and re-opening the document will fix this. + +Future Additions +------------------------------------- + +This is what's currently on my to do list for MIDI Patchbay in rough order +of priority: + +* Additional filters: + * remapping of controller messages + * bank select/program change messages + * pitch bend data + * mod wheel data + * sysex data + * delay MIDI data (build your own arpeggiator!) + * transpose within a key (magic harmonies!) +* Copy and paste of patches +* Grouping of patches and assigning of keyboard/MIDI event shortcuts to enable or disable groups for live use +* Spiffier interface which takes up less screen space and has neat ways of doing things like keyboard splits or arpeggiation + +Credits +---------------------------------- + +The software was designed and written by [Pete Yandell](mailto:pete@notahat.com) + +Thanks to Kevan Staples for generously donating the MIDI Patchbay icon. + +Many, many thanks to Kurt Revis both for writing [MIDI Monitor](http://www.snoize.com/MIDIMonitor), without which testing this thing would have been a nightmare, and for answering a million of my questions. Without his help I would have struggled over the code for a lot longer. + +Version History +------------------------------------- + +### Version 1.0.4 (??? 2019) + +* Modernization for macOS 10.10+ (Dan Wilcox & Anthony Lauzon) +* Allow more then a single MIDI interface with the same name (Joshua Bates) +* Ported Read Me.html to README.md markdown (Martin Delille) as well as BUGS.md and TODO.md + +### Version 1.0.3 (5 June 2008) + +* Fixed the build targets so that my release build script will correctly build a universal binary +* Updated the version strings to match SimpleSynth's style + +### Version 1.0.2 (31 May 2008) + +* Added an icon generously provided by Kevan Staples + +### Version 1.0.1 (13 January 2004) + +* Fixed bugs to do with handling of MIDI devices on OS X 10.3 +* Fixed a major bug with undo handling that caused deleted patches to invisibly hang around and keep working in certain circumstances +* The default patch in an empty document will no longer use IAC bus endpoints in order to avoid creating potential MIDI routing loops +* Text in the patch list gets an ellipsis on the end when truncated + +### Version 1.0 (1 February 2003) + +* Patch sets can be saved and loaded +* Undo works +* External device names as configured in Audio MIDI Setup are used when available +* The interface now uses tabs, is a lot more compact and allows windows to be resized +* Middle C is now displayed as C4 (rather than C5) + +### Version 0.2 (29 June 2002) + +* Virtual MIDI inputs and outputs can now be edited +* OS X 10.1.5 is now required, as this fixes a couple of MIDI bugs which were causing grief, particularly with realtime data + +* A switch to allow MIDI clock/realtime data through patches has been added +* The MIDI channel filtering controls now work +* The window now remembers its position between application restarts +* Up and down arrow keys work in the list of patches +* Patches can now be deleted +* Creating a new patch from a patch with a range filter set and an upper note of G10 no longer causes errors + +### Version 0.1 (21 February 2002) + +* initial release + +*** + +*Copyright © 2003-2019 [Peter Yandell](mailto:pete@notahat.com).* All Rights Reserved. diff --git a/Read Me.html b/Read Me.html deleted file mode 100644 index 2e4d7fb..0000000 --- a/Read Me.html +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - - -MIDI Patchbay 1.0.2 by Pete Yandell - - - - - - - - -

MIDI Patchbay (Version 1.0.2)

- - - -

MIDI patchbay lets you hook up your various pieces of MIDI software and -hardware and pass MIDI data between them, applying assorted filters on the way. -Things like key splits, MIDI channel remapping, transposition, etc. are -made very simple.

- - -

License

- -

This software is distributed under the terms of Pete's Public -License. It's free (as in beer) but the license sets out a few restrictions on -its distribution and modification.

- -

If you use it I only ask for one thing: -send me an e-mail. Tell me what -you use it for, anything you like and don't like, anything you'd like -to see in future versions, etc.

- -

For other ways to reward me for producing this, see -my web site.

- - -

Requirements

- -
    -
  • Mac OS X 10.2 or later
  • -
  • A MIDI interface with OS X drivers
  • -
  • Other MIDI software such as a sequencer or soft synth
  • -
- -

If you're looking for MIDI interface drivers or audio and MIDI applications -for Mac OS X, check out my links page. - - -

Using MIDI Patchbay

- -

Each MIDI Patchbay document window is divided into two parts: a patch list -on the left and a set of patch controls on the right.

- -

The Patch List

- -

Each patch in the list represents a connection -between a MIDI input and MIDI output. The patch's input is shown on -the left of the arrow and the patch's output on the right.

- -

Below each patch's -arrow is a description of the data passed by the patch and -any filters that are applied to the data.

- -

Each patch also has a checkbox that can be used to enable or disable it.

- -

New patches can be created with the "Add patch" button. MIDI Patchbay -tries to create new patches intelligently based on the currently selected -patch.

- - -

The Patch Controls

- -

The patch controls configure the input, output and filters for the -currently selected patch.

- -

The MIDI input and output for the patch can be selected from the -appropriate popup menus. As well as the MIDI interfaces connected to your -system, you can use the "Edit virtual inputs/outputs..." options to create -inputs and outputs that will be visible to other MIDI software.

- -

Under the various tabs are options allowing you to filter the MIDI data -for the currently selected patch.

- - -

Example: Creating a Keyboard Split

- -
    -
  • Create a new MIDI Patchbay document
  • -
  • Select the first patch in the list on the left
  • -
  • From the "MIDI Input" popup menu choose your MIDI interface
  • -
  • From the "MIDI Output" popup menu, select "Edit virtual outputs..."
  • -
  • Create a new output named "Example output" and click "OK"
  • -
  • Select "Example output" in the "MIDI Output" popup menu
  • -
  • - In the "Channels" tab, check the "Remap all data to MIDI channel:" - button and choose channel 1 -
  • -
  • - In the "Notes" tab, check the "Only allow notes in the range:" button - and slide the lower slider until the note A4 is selected -
  • -
  • - Click the "Add Patch" button. Note that the new patch created is - intelligently based on the currently selected patch and already has - the other half of the keyboard selected as its range of filtered notes - and has its traffic remapped to channel 2. -
  • -
  • - Open your favourite synth software and select "Example Output" as - its MIDI input -
  • -
- -

Now any notes up to A4 will be sent your synth software's MIDI channel 1 -and notes above A4 will go to channel 2.

- - -

Known Problems

- -

Creating a MIDI routing loop can crash OS X

- -

For example, creating a patch that receives from and sends to the one -IAC bus is a bad idea. As soon as MIDI data gets sent to that IAC bus the -entire computer will lock up.

- -

You also have to be careful not to create indirect loops, as can -easily happen with Logic.

- - -

Using MIDI Patchbay with Logic

- -

Using MIDI Patchbay with Logic can crash the computer, but the problem -is easily worked around.

- -

If you open a default song in Logic it will open with a MIDI track -enabled as MIDI thru. The default MIDI instrument is set to ‘all’ -for the OUT port.

- -

If your MIDI Patchbay document contains a patch that routes from a -virtual input to a virtual output, this creates a MIDI routing loop. -Logic sends instrument settings into this loop at song-open and, hence, -a crash occurs.

- -

You can work around it by first disabling the patches in MIDI Patchbay, -opening Logic and turning off MIDI Thru, and then re-enabling the patches -in MIDI Patchbay.

- - -

MIDI and Classic

- -

Some people have reported problems when trying to use Classic and -a MIDI interface simultaneously. It seems that Classic will try to grab -control of USB devices. When using this software, you should make sure -that Classic is not running.

- - -

Deleting virtual inputs and outputs

- -

Sometimes trying to delete a virtual input or output will give you -a message saying that the input or output is in use by a patch and can't -be deleted even when it isn't. Saving, closing and re-opening the -document will fix this.

- - -

Future Additions

- -

This is what's currently on my to do list for MIDI Patchbay in rough order -of priority:

- -
    -
  • - Additional filters: -
      -
    • remapping of controller messages
    • -
    • bank select/program change messages
    • -
    • pitch bend data
    • -
    • mod wheel data
    • -
    • sysex data
    • -
    • delay MIDI data (build your own arpeggiator!)
    • -
    • transpose within a key (magic harmonies!)
    • -
    -
  • -
  • Copy and paste of patches
  • -
  • - Grouping of patches and assigning of keyboard/MIDI event - shortcuts to enable or disable groups for live use -
  • -
  • - Spiffier interface which takes up less screen space and - has neat ways of doing things like keyboard splits or - arpeggiation -
  • -
- - -

Credits

- -

The software was designed and written by -Pete Yandell.

- -

Thanks to Kevan Staples for generously donating the MIDI Patchbay icon.

- -

Many, many thanks to Kurt Revis both for writing -MIDI Monitor, without which testing -this thing would have been a nightmare, and for answering a million of my questions. -Without his help I would have struggled over the code for a lot longer.

- - -

Version History

- - -

Version 1.0.2 (???)

- -
    -
  • Added an icon generously provided by Kevan Staples.
  • -
- - -

Version 1.0.1 (13 January 2004)

- -
    -
  • Fixed bugs to do with handling of MIDI devices on OS X 10.3
  • -
  • - Fixed a major bug with undo handling that caused deleted patches - to invisibly hang around and keep working in certain circumstances -
  • -
  • - The default patch in an empty document will no longer use IAC bus - endpoints in order to avoid creating potential MIDI routing loops -
  • -
  • Text in the patch list gets an ellipsis on the end when truncated
  • -
- - -

Version 1.0 (1 February 2003)

- -
    -
  • Patch sets can be saved and loaded
  • -
  • Undo works
  • -
  • External device names as configured in Audio MIDI Setup are used when available
  • -
  • The interface now uses tabs, is a lot more compact and allows windows to be resized
  • -
  • Middle C is now displayed as C4 (rather than C5)
  • -
- - -

Version 0.2 (29 June 2002)

- -
    -
  • Virtual MIDI inputs and outputs can now be edited
  • -
  • - OS X 10.1.5 is now required, as this fixes a couple of MIDI bugs which - were causing grief, particularly with realtime data -
  • -
  • A switch to allow MIDI clock/realtime data through patches has been added
  • -
  • The MIDI channel filtering controls now work
  • -
  • The window now remembers its position between application restarts
  • -
  • Up and down arrow keys work in the list of patches
  • -
  • Patches can now be deleted
  • -
  • - Creating a new patch from a patch with a range filter set and an - upper note of G10 no longer causes errors -
  • -
- - -

Version 0.1 (21 February 2002)

- -
    -
  • initial release
  • -
- - - - - diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..df6bdad --- /dev/null +++ b/TODO.md @@ -0,0 +1,36 @@ +MIDI Patchbay To Do List +======================== + +Features +-------- + +* Additional filters: + - controller messages + - bank select/program change messages + - pitch bend data + - mod wheel data + - sysex data + - delay? + - arpeggiator? + - remapping of controller values +* Add the ability to cut, copy and paste patches +* Add the ability to reorder patches in a set +* Grouping of patches for fast/enabling disabling +* Keyboard/MIDI shortcuts for enabling/disabling patches +* More scalable user interface +* Plugin filter support +* Ability to have outputs show up in input list for looping through multiple filters + +Improvements/Cleanups +--------------------- + +* Rewrite to use MIDIThruConnection interfaces for much better performance +* The input/output text should be reselected for editing after you are told that you've tried a duplicate name. What does the UI do if we block things with an NSFormatter? +* Deleting an input/output that is in use should not be disallowed. The user should be asked about it and given the option. That would involve searching the patches and removing it from all patches that use it. + +Done +---- + +* Rename TrafficTableCell...it's not a cell for the Traffic table! I should rename PatchTableCell while I'm at it. + +_$Id: To\040Do.html,v 1.2.2.1 2004/01/12 12:18:01 pete Exp $_ diff --git a/To Do.html b/To Do.html deleted file mode 100644 index 437f71f..0000000 --- a/To Do.html +++ /dev/null @@ -1,61 +0,0 @@ - - - MIDI Patchbay To Do List - - - - -

MIDI Patchbay To Do List

- - -

Features

- -
    -
  • - Additional filters: -
      -
    • controller messages
    • -
    • bank select/program change messages
    • -
    • pitch bend data
    • -
    • mod wheel data
    • -
    • sysex data
    • -
    • delay?
    • -
    • arpeggiator?
    • -
    • remapping of controller values
    • -
    -
  • -
  • Add the ability to cut, copy and paste patches
  • -
  • Add the ability to reorder patches in a set
  • -
  • Grouping of patches for fast/enabling disabling
  • -
  • Keyboard/MIDI shortcuts for enabling/disabling patches
  • -
  • More scalable user interface
  • -
  • Plugin filter support
  • -
  • Ability to have outputs show up in input list for looping through multiple filters
  • -
- - -

Improvements/Cleanups

- -
    -
  • Rewrite to use MIDIThruConnection interfaces for much better performance
  • -
  • - The input/output text should be reselected for editing after you - are told that you've tried a duplicate name. What does the UI do if - we block things with an NSFormatter? -
  • -
  • - Deleting an input/output that is in use should not be disallowed. The user should - be asked about it and given the option. That would involve searching - the patches and removing it from all patches that use it. - -
  • - Rename TrafficTableCell...it's not a cell for the Traffic table! - I should rename PatchTableCell while I'm at it. -
  • -
- - -

$Id: To\040Do.html,v 1.2.2.1 2004/01/12 12:18:01 pete Exp $

- - - \ No newline at end of file diff --git a/View Classes/KeyTableView.h b/View Classes/KeyTableView.h index 56d039a..53e6a7a 100644 --- a/View Classes/KeyTableView.h +++ b/View Classes/KeyTableView.h @@ -1,19 +1,18 @@ #import +#import - -@interface KeyTableView : NSTableView { +@interface KeyTableView : NSTableView { } - (void)keyDown:(NSEvent *)theEvent; - (void)deleteForward:(id)sender; - (void)deleteBackward:(id)sender; +- (void)cancelOperation:(id)sender; - (void)moveUp:(id)sender; - (void)moveDown:(id)sender; -- (BOOL)validateMenuItem:(id )menuItem; +- (BOOL)validateUserInterfaceItem:(id) menuItem; - (void)clear:(id)sender; -- (void)textDidEndEditing:(NSNotification*)notification; - @end diff --git a/View Classes/KeyTableView.m b/View Classes/KeyTableView.m index 7984b5b..cb90f53 100644 --- a/View Classes/KeyTableView.m +++ b/View Classes/KeyTableView.m @@ -1,5 +1,5 @@ #import "KeyTableView.h" - +#import "TableDataSource.h" @implementation KeyTableView @@ -22,10 +22,16 @@ - (void)deleteBackward:(id)sender } +// remove focus from table view on Esc so Ok button can respond to Enter +- (void)cancelOperation:(id)sender { + [[self window] makeFirstResponder:self.window.contentView]; +} + + - (void)moveUp:(id)sender { if ([self selectedRow] > 0) { - [self selectRow:[self selectedRow] - 1 byExtendingSelection:NO]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[self selectedRow] - 1] byExtendingSelection:NO]; } } @@ -33,12 +39,12 @@ - (void)moveUp:(id)sender - (void)moveDown:(id)sender { if ([self selectedRow] != -1 && [self selectedRow] < [self numberOfRows]-1) { - [self selectRow:[self selectedRow] + 1 byExtendingSelection:NO]; + [self selectRowIndexes:[NSIndexSet indexSetWithIndex:[self selectedRow] + 1] byExtendingSelection:NO]; } } -- (BOOL)validateMenuItem:(id )menuItem +- (BOOL)validateUserInterfaceItem:(id) menuItem { if ([menuItem action] == @selector(clear:)) return [self numberOfSelectedRows] > 0; @@ -51,39 +57,39 @@ - (BOOL)validateMenuItem:(id )menuItem - (void)clear:(id)sender { if ([self selectedRow] == -1) return; - - if ([[self dataSource] respondsToSelector:@selector(deleteSelection:)]) { - [[self dataSource] performSelector:@selector(deleteSelection:) withObject:self]; + if ([[self dataSource] isKindOfClass:[TableDataSource class]]) { + [(TableDataSource *)[self dataSource] deleteSelection:self]; [self deselectAll:self]; - [self reloadData]; + [self reloadData]; } } -- (void)textDidEndEditing:(NSNotification*)notification -{ - // This is a hack to make the return key end editing and leave the selection - // on the current row rather than begin editing the next row. It works by - // substituting a dodgy key code in place of the return key. - - if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement) { - NSMutableDictionary *newUserInfo = - [NSMutableDictionary dictionaryWithDictionary:[notification userInfo]]; - [newUserInfo setObject:[NSNumber numberWithInt:NSIllegalTextMovement] forKey:@"NSTextMovement"]; - - NSNotification *newNotification = [NSNotification - notificationWithName:[notification name] - object:[notification object] - userInfo:newUserInfo - ]; - - [super textDidEndEditing:newNotification]; - - [[self window] makeFirstResponder:self]; - } - else - [super textDidEndEditing:notification]; -} +// UPDATE: this isn't needed anymore in 10.10+ as the current selection appears to no longer change +//- (void)textDidEndEditing:(NSNotification*)notification +//{ +// // This is a hack to make the return key end editing and leave the selection +// // on the current row rather than begin editing the next row. It works by +// // substituting a dodgy key code in place of the return key. +// +// if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement) { +// NSMutableDictionary *newUserInfo = +// [NSMutableDictionary dictionaryWithDictionary:[notification userInfo]]; +// [newUserInfo setObject:[NSNumber numberWithInt:NSIllegalTextMovement] forKey:@"NSTextMovement"]; +// +// NSNotification *newNotification = [NSNotification +// notificationWithName:[notification name] +// object:[notification object] +// userInfo:newUserInfo +// ]; +// +// [super textDidEndEditing:newNotification]; +// +// [[self window] makeFirstResponder:self]; +// } +// else +// [super textDidEndEditing:notification]; +//} @end diff --git a/View Classes/PatchTableCellData.h b/View Classes/PatchTableCellData.h index 428136f..6c5915a 100644 --- a/View Classes/PatchTableCellData.h +++ b/View Classes/PatchTableCellData.h @@ -10,7 +10,6 @@ + (PatchTableCellData*)dataWithInputName:(NSString*)newInputName outputName:(NSString*)newOutputName description:(NSString*)newDescription; - (PatchTableCellData*)initWithInputName:(NSString*)newInputName outputName:(NSString*)newOutputName description:(NSString*)newDescription; -- (void)dealloc; - (NSString*)inputName; - (NSString*)outputName; diff --git a/View Classes/PatchTableCellData.m b/View Classes/PatchTableCellData.m index 2044067..671f8a9 100644 --- a/View Classes/PatchTableCellData.m +++ b/View Classes/PatchTableCellData.m @@ -9,30 +9,20 @@ + (PatchTableCellData*)dataWithInputName:(NSString*)newInputName outputName:(NSS PatchTableCellData* data = [[PatchTableCellData alloc] initWithInputName:newInputName outputName:newOutputName description:newDescription ]; - return [data autorelease]; + return data; } - (PatchTableCellData*)initWithInputName:(NSString*)newInputName outputName:(NSString*)newOutputName description:(NSString*)newDescription { - inputName = [newInputName retain]; - outputName = [newOutputName retain]; - description = [newDescription retain]; + inputName = newInputName; + outputName = newOutputName; + description = newDescription; return self; } -- (void)dealloc -{ - [inputName release]; - [outputName release]; - [description release]; - - [super dealloc]; -} - - - (NSString*)inputName { return inputName; @@ -52,9 +42,7 @@ - (NSString*)description - (id)copyWithZone:(NSZone *)zone { PatchTableCellData* copy = [[self class] allocWithZone:zone]; - [copy initWithInputName:inputName outputName:outputName description:description]; - - return copy; + return [copy initWithInputName:inputName outputName:outputName description:description]; } diff --git a/en.lproj/InfoPlist.strings b/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..939697b Binary files /dev/null and b/en.lproj/InfoPlist.strings differ