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