Store per-section groove, store tempo in file

This commit is contained in:
Matthias Neeracher 2008-01-19 21:31:40 +00:00
parent 77dce75c96
commit 7b7ae7f3d8
6 changed files with 106 additions and 29 deletions

View File

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

View File

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

View File

@ -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<fProperties.size()-EmptyEnding(); ++p)
if (fProperties[p].fGroove != bestGroove) {
//
// Multiple grooves in song, count them the hard way
//
std::vector<size_t> numMeas(fProperties.size());
for (size_t m=0; m<fMeasures.size(); ++m)
++numMeas[fMeasures[m].fPropIdx];
size_t bestCount = numMeas[0];
for (size_t px=1; px<fProperties.size(); ++px)
if (fProperties[px].fGroove == bestGroove) {
bestCount += numMeas[px];
numMeas[px]= 0;
}
for (; p<fProperties.size(); ++p)
if (numMeas[p]) {
std::string curGroove= fProperties[p].fGroove;
size_t curCount = numMeas[p];
for (size_t px=p+1; px<fProperties.size(); ++px)
if (fProperties[px].fGroove == curGroove) {
curCount += numMeas[px];
numMeas[px]= 0;
}
if (curCount > bestCount) {
bestGroove= curGroove;
bestCount = curCount;
}
}
break;
}
return bestGroove;
}
//////////////////////// VLSongVisitor ////////////////////////////////
VLSongVisitor::~VLSongVisitor()

View File

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

View File

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

View File

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