From 7b7ae7f3d8312957c1dd8d96e3a49e58a63f8db6 Mon Sep 17 00:00:00 2001 From: Matthias Neeracher Date: Sat, 19 Jan 2008 21:31:40 +0000 Subject: [PATCH] Store per-section groove, store tempo in file --- Filters/VLMusicXMLType.reader | 14 ++++++++++- Filters/VLMusicXMLType.writer | 37 +++++++++++++++++++++++++--- Sources/VLModel.cpp | 45 +++++++++++++++++++++++++++++++++-- Sources/VLModel.h | 7 ++++-- Sources/VLPListDocument.mm | 10 ++++++-- Sources/VLXMLDocument.mm | 22 +++-------------- 6 files changed, 106 insertions(+), 29 deletions(-) diff --git a/Filters/VLMusicXMLType.reader b/Filters/VLMusicXMLType.reader index 3c9f9e0..d053361 100755 --- a/Filters/VLMusicXMLType.reader +++ b/Filters/VLMusicXMLType.reader @@ -101,7 +101,8 @@ class MusicXMLListener include REXML::StreamListener def initialize - @text = "" + @text = "" + @nativeFile = false end def tag_start(tag, attrs) @@ -199,6 +200,12 @@ class MusicXMLListener when 'beat-type' @kind = 'prop' @key = 'timeDenom' + when 'words' + # + # 'words' is a very general direction type, but if we encoded the file + # we know that we used it for groove names + # + @kind = 'groove' if @nativeFile when 'harmony' @note = { 'pitch' => VL::NoPitch, 'steps' => 0, 'root' => VL::NoPitch, 'at' => @at } @@ -304,6 +311,9 @@ class MusicXMLListener case @kind when 'textProp' OUTPUT[@key] = @text + if @key == 'software' && @text =~ /VocalEasel/ + @nativeFile = true # We (presumably) encoded this + end when 'dateProp' begin OUTPUT[@key] = Time.parse(@text) @@ -312,6 +322,8 @@ class MusicXMLListener end when 'prop' @prop[@key] = @text.to_i + when 'groove' + @prop['groove'] = @text when 'mode' @prop['mode'] = @text == 'minor' ? -1 : 1 when 'step' diff --git a/Filters/VLMusicXMLType.writer b/Filters/VLMusicXMLType.writer index 834acd6..56914d4 100755 --- a/Filters/VLMusicXMLType.writer +++ b/Filters/VLMusicXMLType.writer @@ -79,6 +79,28 @@ def _attributes(prop) return attr end +def _groove(groove) + dir = REXML::Element.new('direction') + dty = REXML::Element.new('direction-type') + dty.add_element newTextElement('words', groove) + dir.add_element(dty) + + return dir +end + +def _tempo(tempo) + dir = REXML::Element.new('direction') + dty = REXML::Element.new('direction-type') + metro = REXML::Element.new('metronome') + metro.add_element newTextElement('beat-unit', 'quarter') + metro.add_element newTextElement('per-minute', tempo) + dty.add_element(metro) + dir.add_element(dty) + dir.add_element 'sound', {'tempo' => tempo} + + return dir +end + STEPS = 'C DbD EbE F GbG AbA BbB ' def _pitch(name, pitch, prefix="") @@ -292,9 +314,10 @@ def _melody melody = REXML::Element.new('part') melody.add_attribute('id', 'MELO') - lastProp = -1 - measNum = 0 - repeat = [0] + lastProp = -1 + lastGroove = nil + measNum = 0 + repeat = [0] INPUT['measures'].each do |meas| r = 0 r |= 1 if meas['begin-repeat'] @@ -306,8 +329,16 @@ def _melody measNum += 1 m = REXML::Element.new('measure') m.add_attribute('number', measNum.to_s); + if measNum == 1 + m.add_element(_tempo(INPUT['tempo'].to_i)) + end if meas['properties'] != lastProp lastProp = meas['properties'] + groove = INPUT['properties'][lastProp]['groove'] + if groove != lastGroove + lastGroove = groove + m.add_element(_groove(lastGroove)) + end m.add_element(_attributes(INPUT['properties'][lastProp])) end if meas['new-page'] diff --git a/Sources/VLModel.cpp b/Sources/VLModel.cpp index 0f440cb..8b712b6 100644 --- a/Sources/VLModel.cpp +++ b/Sources/VLModel.cpp @@ -540,7 +540,7 @@ VLSong::VLSong(bool initialize) return; const VLFraction fourFour(4,4); - VLProperties defaultProperties = {fourFour, 0, 1, 3}; + VLProperties defaultProperties = {fourFour, 0, 1, 3, "Swing"}; fProperties.push_back(defaultProperties); @@ -2053,7 +2053,7 @@ VLFract VLSong::TiedDuration(size_t measure) return total; } -bool VLSong::DoesBeginSection(size_t measure) +bool VLSong::DoesBeginSection(size_t measure) const { return measure && measure < fMeasures.size() && fMeasures[measure-1].fPropIdx!=fMeasures[measure].fPropIdx; @@ -2081,6 +2081,47 @@ void VLSong::DelSection(size_t measure) --fMeasures[measure++].fPropIdx; } +std::string VLSong::PrimaryGroove() const +{ + std::string bestGroove = fProperties[0].fGroove; + + for (size_t p=1; p numMeas(fProperties.size()); + for (size_t m=0; m bestCount) { + bestGroove= curGroove; + bestCount = curCount; + } + } + + break; + } + + return bestGroove; +} + //////////////////////// VLSongVisitor //////////////////////////////// VLSongVisitor::~VLSongVisitor() diff --git a/Sources/VLModel.h b/Sources/VLModel.h index 7881ff3..5c18833 100644 --- a/Sources/VLModel.h +++ b/Sources/VLModel.h @@ -221,10 +221,11 @@ struct VLProperties { int8_t fKey; // Circle of fifths from C, >0 sharps, <0 flats int8_t fMode; // 1 = major -1 = minor int8_t fDivisions; // Number of divisions per quarter note + std::string fGroove; // MMA Groove bool operator==(const VLProperties & other) { return fTime == other.fTime && fKey == other.fKey && fMode == other.fMode - && fDivisions == other.fDivisions; + && fDivisions == other.fDivisions && fGroove == other.fGroove; } }; @@ -377,9 +378,11 @@ public: return fProperties[fMeasures[measure].fPropIdx]; } - bool DoesBeginSection(size_t measure); + bool DoesBeginSection(size_t measure) const; void AddSection(size_t measure); void DelSection(size_t measure); + + std::string PrimaryGroove() const; private: void AddMeasure(); }; diff --git a/Sources/VLPListDocument.mm b/Sources/VLPListDocument.mm index 4056fc5..9c4e4e8 100644 --- a/Sources/VLPListDocument.mm +++ b/Sources/VLPListDocument.mm @@ -61,6 +61,7 @@ NSDictionary * VLPlistVisitor::EncodeProperties(const VLProperties & properties) [NSNumber numberWithInt: properties.fKey], @"key", [NSNumber numberWithInt: properties.fMode], @"mode", [NSNumber numberWithInt: properties.fDivisions], @"divisions", + [NSString stringWithUTF8String:properties.fGroove.c_str()], @"groove", nil]; } @@ -166,8 +167,8 @@ void VLPlistVisitor::VisitChord(VLChord & c) { NSMutableDictionary * plist = [NSMutableDictionary dictionaryWithObjectsAndKeys: - songTitle, @"title", - songGroove, @"groove", songTempo, @"tempo", + songTitle, @"title", songTempo, @"tempo", + [NSString stringWithUTF8String:song->PrimaryGroove().c_str()], @"groove", songComposer, @"composer", songLyricist, @"lyricist", [NSDate date], @"saved", [NSString stringWithFormat:@"VocalEasel %@", @@ -405,6 +406,11 @@ advanceAt: prop.fMode = [[pdict objectForKey:@"mode"] intValue]; prop.fDivisions = [[pdict objectForKey:@"divisions"] intValue]; + if (NSString * groove = [pdict objectForKey:@"groove"]) + prop.fGroove = [groove UTF8String]; + else + prop.fGroove = [songGroove UTF8String]; + song->fProperties.push_back(prop); } } diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index e3a04fa..1a2e95a 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -13,18 +13,6 @@ @implementation VLDocument (XML) - -- (NSArray *)propertyKeys -{ - static NSArray * sPropertyKeys = nil; - - if (!sPropertyKeys) - sPropertyKeys = [[NSArray alloc] initWithObjects: - @"songGroove", @"songTempo", nil]; - - return sPropertyKeys; -} - - (NSFileWrapper *)XMLFileWrapperWithError:(NSError **)outError flat:(BOOL)flat; { NSFileWrapper * contents = [self fileWrapperWithFilter:@"VLMusicXMLType" error:outError]; @@ -40,13 +28,6 @@ autorelease]; [contents setPreferredFilename:@"Song"]; [wrap addFileWrapper:contents]; - NSDictionary * prop = - [self dictionaryWithValuesForKeys:[self propertyKeys]]; - [wrap addRegularFileWithContents: - [NSPropertyListSerialization dataFromPropertyList:prop - format:NSPropertyListXMLFormat_v1_0 - errorDescription:nil] - preferredFilename:@"Properties"]; if (vcsWrapper) [wrap addFileWrapper:vcsWrapper]; @@ -62,6 +43,9 @@ || (vcsWrapper = [wrappers objectForKey:@".svn"]) ) [vcsWrapper retain]; + // + // Read properties dictionary for backward compatibility + // NSFileWrapper * prop = [wrappers objectForKey:@"Properties"]; if (prop) { NSUndoManager * undoMgr = [self undoManager];