diff --git a/English.lproj/VLDocument.nib/classes.nib b/English.lproj/VLDocument.nib/classes.nib index ea32267..28b8ef2 100644 --- a/English.lproj/VLDocument.nib/classes.nib +++ b/English.lproj/VLDocument.nib/classes.nib @@ -1,8 +1,8 @@ { IBClasses = ( {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, - {CLASS = MyDocument; LANGUAGE = ObjC; SUPERCLASS = NSDocument; }, {CLASS = VLDocument; LANGUAGE = ObjC; SUPERCLASS = NSDocument; }, + {CLASS = VLEditable; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, { ACTIONS = {hideFieldEditor = id; setDivisions = id; setKey = id; setTime = id; }; CLASS = VLSheetView; diff --git a/English.lproj/VLDocument.nib/info.nib b/English.lproj/VLDocument.nib/info.nib index 470d47e..fa0cb16 100644 --- a/English.lproj/VLDocument.nib/info.nib +++ b/English.lproj/VLDocument.nib/info.nib @@ -11,6 +11,6 @@ 5 IBSystem Version - 9A274 + 9A283 diff --git a/English.lproj/VLDocument.nib/keyedobjects.nib b/English.lproj/VLDocument.nib/keyedobjects.nib index 61dcccb..4191aa7 100644 Binary files a/English.lproj/VLDocument.nib/keyedobjects.nib and b/English.lproj/VLDocument.nib/keyedobjects.nib differ diff --git a/Resources/Info.plist b/Resources/Info.plist index a8198fc..2f134c1 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -12,7 +12,7 @@ vlsong CFBundleTypeIconFile - + vlsong.icns CFBundleTypeName Song CFBundleTypeOSTypes @@ -32,7 +32,7 @@ CFBundleExecutable Vocalese CFBundleIconFile - + vocalese CFBundleIdentifier com.aereperennius.Vocalese CFBundleInfoDictionaryVersion diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm index 88746a8..79c6723 100644 --- a/Sources/VLDocument.mm +++ b/Sources/VLDocument.mm @@ -76,8 +76,13 @@ { VLProperties & prop = song->fProperties.front(); + if (transpose) + song->Transpose((7*((key>>8)-prop.fKey) % 12)); + prop.fKey = key >> 8; prop.fMode= key & 0xFF; + + [self updateChangeCount:NSChangeDone]; } - (NSNumber *) songTime @@ -92,6 +97,8 @@ VLProperties & prop = song->fProperties.front(); prop.fTime = VLFraction(num, denom); + + [self updateChangeCount:NSChangeDone]; } - (NSNumber *) songDivisions @@ -106,37 +113,19 @@ VLProperties & prop = song->fProperties.front(); prop.fDivisions = divisions; + + [self updateChangeCount:NSChangeDone]; } - (NSString *)windowNibName { - // Override returning the nib file name of the document - // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead. return @"VLDocument"; } -- (void)windowControllerDidLoadNib:(NSWindowController *) aController +- (void)windowControllerDidLoadNib:(NSWindowController *) controller { - [super windowControllerDidLoadNib:aController]; - // Add any code here that needs to be executed once the windowController has loaded the document's window. -} - -- (NSData *)dataRepresentationOfType:(NSString *)aType -{ - // Insert code here to write your document from the given data. You can also choose to override -fileWrapperRepresentationOfType: or -writeToFile:ofType: instead. - - // For applications targeted for Tiger or later systems, you should use the new Tiger API -dataOfType:error:. In this case you can also choose to override -writeToURL:ofType:error:, -fileWrapperOfType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead. - - return nil; -} - -- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType -{ - // Insert code here to read your document from the given data. You can also choose to override -loadFileWrapperRepresentation:ofType: or -readFromFile:ofType: instead. - - // For applications targeted for Tiger or later systems, you should use the new Tiger API readFromData:ofType:error:. In this case you can also choose to override -readFromURL:ofType:error: or -readFromFileWrapper:ofType:error: instead. - - return YES; + [super windowControllerDidLoadNib:controller]; + [controller setShouldCloseDocument:YES]; } @end diff --git a/Sources/VLSheetViewChords.mm b/Sources/VLSheetViewChords.mm index a97226e..f316463 100644 --- a/Sources/VLSheetViewChords.mm +++ b/Sources/VLSheetViewChords.mm @@ -119,6 +119,7 @@ std::string NormalizeName(NSString* rawName) fSong->AddChord(chord, fMeasure, fAt); [fView setNeedsDisplay:YES]; } + [[fView document] updateChangeCount:NSChangeDone]; } - (BOOL) validValue:(NSString *)val diff --git a/Sources/VLSheetViewNotes.mm b/Sources/VLSheetViewNotes.mm index 107dc77..2b72703 100644 --- a/Sources/VLSheetViewNotes.mm +++ b/Sources/VLSheetViewNotes.mm @@ -23,6 +23,7 @@ [self song]->AddNote(newNote, fCursorMeasure, fCursorAt); [self setNeedsDisplay:YES]; + [[self document] updateChangeCount:NSChangeDone]; VLSoundOut::Instance()->PlayNote(newNote); diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index 5d8eef5..12b4d25 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -20,7 +20,8 @@ - (id) nodeForXPath:(NSString *)path error:(NSError **)outError { - return [[self nodesForXPath:path error:outError] objectAtIndex:0]; + NSArray * nodes = [self nodesForXPath:path error:outError]; + return [nodes count] ? [nodes objectAtIndex:0] : nil; } - (NSString *)stringForXPath:(NSString *)path error:(NSError **)outError @@ -259,6 +260,105 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; return YES; } +int8_t sStepToPitch[] = { + 9, 11, 0, 2, 4, 5, 7 +}; + +- (VLNote) readNote:(NSXMLElement*)note withUnit:(VLFraction)unit +{ + NSError * outError; + VLNote n; + + n.fTied = 0; + + if ([[note elementsForName:@"rest"] count]) { + n.fPitch = VLNote::kNoPitch; + } else { + n.fPitch = ([note intForXPath:@"./pitch/octave" error:&outError]+1)*12; + n.fPitch += + sStepToPitch[[[note stringForXPath:@"./pitch/step" error:&outError] + characterAtIndex:0] - 'A']; + if (NSXMLElement * alter = [note nodeForXPath:@"./pitch/alter" error:&outError]) + n.fPitch += [[alter stringValue] intValue]; + } + n.fDuration = VLFraction([note intForXPath:@"./duration" error:&outError])*unit; + + return n; +} + +- (void) readMelody:(NSArray *)measures error:(NSError **)outError +{ + NSEnumerator * e = [measures objectEnumerator]; + + for (NSXMLElement * measure; measure = [e nextObject]; ) { + VLProperties & prop = song->fProperties.front(); + VLFraction unit(1, 4*prop.fDivisions); + VLFraction at(0); + int m = [[[measure attributeForName:@"number"] + stringValue] intValue]-1; + + if (m >= song->CountMeasures()) + song->fMeasures.resize(m); + + NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator]; + + for (NSXMLElement * note; note = [n nextObject]; ) { + VLNote n = [self readNote:note withUnit:unit]; + if (n.fPitch != VLNote::kNoPitch) + song->AddNote(n, m, at); + at += n.fDuration; + } + } +} + +- (void) readChords:(NSArray *)measures error:(NSError **)outError +{ + NSEnumerator * e = [measures objectEnumerator]; + + for (NSXMLElement * measure; measure = [e nextObject]; ) { + VLProperties & prop = song->fProperties.front(); + VLFraction unit(1, 4*prop.fDivisions); + VLFraction at(0); + VLFraction dur(0); + int m = [[[measure attributeForName:@"number"] + stringValue] intValue]-1; + VLChord chord; + + chord.fSteps = 0; + if (m >= song->CountMeasures()) + song->fMeasures.resize(m); + + NSEnumerator * n = [[measure elementsForName:@"note"] objectEnumerator]; + + for (NSXMLElement * note; note = [n nextObject]; ) { + VLNote n = [self readNote:note withUnit:unit]; + if (![[note elementsForName:@"chord"] count]) { + // + // Start of new chord + // + if (chord.fSteps) + song->AddChord(chord, m, at); + at += dur; + chord.fPitch = n.fPitch; + chord.fRootPitch= VLNote::kNoPitch; + chord.fDuration = n.fDuration; + chord.fSteps = n.fPitch == VLNote::kNoPitch ? 0 : VLChord::kmUnison; + dur = n.fDuration; + if (n.fPitch < VLNote::kMiddleC) { + chord.fPitch = VLNote::kNoPitch; + chord.fRootPitch= n.fPitch; + } + } else if (chord.fPitch == VLNote::kNoPitch) { + chord.fPitch = n.fPitch; + } else { + chord.fSteps |= 1 << (n.fPitch-chord.fPitch); + } + } + if (chord.fSteps) + song->AddChord(chord, m, at); + } +} + - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError { NSXMLDocument * doc = [[NSXMLDocument alloc] initWithData:data @@ -280,7 +380,12 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; error:outError] ) return NO; - + + [self readMelody:[melody nodesForXPath:@"./measure" error:outError] + error:outError]; + [self readChords:[chords nodesForXPath:@"./measure" error:outError] + error:outError]; + return YES; }