mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 19:23:59 +00:00
Store per-section groove, store tempo in file
This commit is contained in:
parent
77dce75c96
commit
7b7ae7f3d8
|
@ -102,6 +102,7 @@ class MusicXMLListener
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@text = ""
|
@text = ""
|
||||||
|
@nativeFile = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def tag_start(tag, attrs)
|
def tag_start(tag, attrs)
|
||||||
|
@ -199,6 +200,12 @@ class MusicXMLListener
|
||||||
when 'beat-type'
|
when 'beat-type'
|
||||||
@kind = 'prop'
|
@kind = 'prop'
|
||||||
@key = 'timeDenom'
|
@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'
|
when 'harmony'
|
||||||
@note = { 'pitch' => VL::NoPitch, 'steps' => 0, 'root' => VL::NoPitch,
|
@note = { 'pitch' => VL::NoPitch, 'steps' => 0, 'root' => VL::NoPitch,
|
||||||
'at' => @at }
|
'at' => @at }
|
||||||
|
@ -304,6 +311,9 @@ class MusicXMLListener
|
||||||
case @kind
|
case @kind
|
||||||
when 'textProp'
|
when 'textProp'
|
||||||
OUTPUT[@key] = @text
|
OUTPUT[@key] = @text
|
||||||
|
if @key == 'software' && @text =~ /VocalEasel/
|
||||||
|
@nativeFile = true # We (presumably) encoded this
|
||||||
|
end
|
||||||
when 'dateProp'
|
when 'dateProp'
|
||||||
begin
|
begin
|
||||||
OUTPUT[@key] = Time.parse(@text)
|
OUTPUT[@key] = Time.parse(@text)
|
||||||
|
@ -312,6 +322,8 @@ class MusicXMLListener
|
||||||
end
|
end
|
||||||
when 'prop'
|
when 'prop'
|
||||||
@prop[@key] = @text.to_i
|
@prop[@key] = @text.to_i
|
||||||
|
when 'groove'
|
||||||
|
@prop['groove'] = @text
|
||||||
when 'mode'
|
when 'mode'
|
||||||
@prop['mode'] = @text == 'minor' ? -1 : 1
|
@prop['mode'] = @text == 'minor' ? -1 : 1
|
||||||
when 'step'
|
when 'step'
|
||||||
|
|
|
@ -79,6 +79,28 @@ def _attributes(prop)
|
||||||
return attr
|
return attr
|
||||||
end
|
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 '
|
STEPS = 'C DbD EbE F GbG AbA BbB '
|
||||||
|
|
||||||
def _pitch(name, pitch, prefix="")
|
def _pitch(name, pitch, prefix="")
|
||||||
|
@ -293,6 +315,7 @@ def _melody
|
||||||
melody.add_attribute('id', 'MELO')
|
melody.add_attribute('id', 'MELO')
|
||||||
|
|
||||||
lastProp = -1
|
lastProp = -1
|
||||||
|
lastGroove = nil
|
||||||
measNum = 0
|
measNum = 0
|
||||||
repeat = [0]
|
repeat = [0]
|
||||||
INPUT['measures'].each do |meas|
|
INPUT['measures'].each do |meas|
|
||||||
|
@ -306,8 +329,16 @@ def _melody
|
||||||
measNum += 1
|
measNum += 1
|
||||||
m = REXML::Element.new('measure')
|
m = REXML::Element.new('measure')
|
||||||
m.add_attribute('number', measNum.to_s);
|
m.add_attribute('number', measNum.to_s);
|
||||||
|
if measNum == 1
|
||||||
|
m.add_element(_tempo(INPUT['tempo'].to_i))
|
||||||
|
end
|
||||||
if meas['properties'] != lastProp
|
if meas['properties'] != lastProp
|
||||||
lastProp = meas['properties']
|
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]))
|
m.add_element(_attributes(INPUT['properties'][lastProp]))
|
||||||
end
|
end
|
||||||
if meas['new-page']
|
if meas['new-page']
|
||||||
|
|
|
@ -540,7 +540,7 @@ VLSong::VLSong(bool initialize)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const VLFraction fourFour(4,4);
|
const VLFraction fourFour(4,4);
|
||||||
VLProperties defaultProperties = {fourFour, 0, 1, 3};
|
VLProperties defaultProperties = {fourFour, 0, 1, 3, "Swing"};
|
||||||
|
|
||||||
fProperties.push_back(defaultProperties);
|
fProperties.push_back(defaultProperties);
|
||||||
|
|
||||||
|
@ -2053,7 +2053,7 @@ VLFract VLSong::TiedDuration(size_t measure)
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VLSong::DoesBeginSection(size_t measure)
|
bool VLSong::DoesBeginSection(size_t measure) const
|
||||||
{
|
{
|
||||||
return measure && measure < fMeasures.size()
|
return measure && measure < fMeasures.size()
|
||||||
&& fMeasures[measure-1].fPropIdx!=fMeasures[measure].fPropIdx;
|
&& fMeasures[measure-1].fPropIdx!=fMeasures[measure].fPropIdx;
|
||||||
|
@ -2081,6 +2081,47 @@ void VLSong::DelSection(size_t measure)
|
||||||
--fMeasures[measure++].fPropIdx;
|
--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::~VLSongVisitor()
|
VLSongVisitor::~VLSongVisitor()
|
||||||
|
|
|
@ -221,10 +221,11 @@ struct VLProperties {
|
||||||
int8_t fKey; // Circle of fifths from C, >0 sharps, <0 flats
|
int8_t fKey; // Circle of fifths from C, >0 sharps, <0 flats
|
||||||
int8_t fMode; // 1 = major -1 = minor
|
int8_t fMode; // 1 = major -1 = minor
|
||||||
int8_t fDivisions; // Number of divisions per quarter note
|
int8_t fDivisions; // Number of divisions per quarter note
|
||||||
|
std::string fGroove; // MMA Groove
|
||||||
|
|
||||||
bool operator==(const VLProperties & other)
|
bool operator==(const VLProperties & other)
|
||||||
{ return fTime == other.fTime && fKey == other.fKey && fMode == other.fMode
|
{ 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];
|
return fProperties[fMeasures[measure].fPropIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DoesBeginSection(size_t measure);
|
bool DoesBeginSection(size_t measure) const;
|
||||||
void AddSection(size_t measure);
|
void AddSection(size_t measure);
|
||||||
void DelSection(size_t measure);
|
void DelSection(size_t measure);
|
||||||
|
|
||||||
|
std::string PrimaryGroove() const;
|
||||||
private:
|
private:
|
||||||
void AddMeasure();
|
void AddMeasure();
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,6 +61,7 @@ NSDictionary * VLPlistVisitor::EncodeProperties(const VLProperties & properties)
|
||||||
[NSNumber numberWithInt: properties.fKey], @"key",
|
[NSNumber numberWithInt: properties.fKey], @"key",
|
||||||
[NSNumber numberWithInt: properties.fMode], @"mode",
|
[NSNumber numberWithInt: properties.fMode], @"mode",
|
||||||
[NSNumber numberWithInt: properties.fDivisions], @"divisions",
|
[NSNumber numberWithInt: properties.fDivisions], @"divisions",
|
||||||
|
[NSString stringWithUTF8String:properties.fGroove.c_str()], @"groove",
|
||||||
nil];
|
nil];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,8 +167,8 @@ void VLPlistVisitor::VisitChord(VLChord & c)
|
||||||
{
|
{
|
||||||
NSMutableDictionary * plist =
|
NSMutableDictionary * plist =
|
||||||
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
[NSMutableDictionary dictionaryWithObjectsAndKeys:
|
||||||
songTitle, @"title",
|
songTitle, @"title", songTempo, @"tempo",
|
||||||
songGroove, @"groove", songTempo, @"tempo",
|
[NSString stringWithUTF8String:song->PrimaryGroove().c_str()], @"groove",
|
||||||
songComposer, @"composer", songLyricist, @"lyricist",
|
songComposer, @"composer", songLyricist, @"lyricist",
|
||||||
[NSDate date], @"saved",
|
[NSDate date], @"saved",
|
||||||
[NSString stringWithFormat:@"VocalEasel %@",
|
[NSString stringWithFormat:@"VocalEasel %@",
|
||||||
|
@ -405,6 +406,11 @@ advanceAt:
|
||||||
prop.fMode = [[pdict objectForKey:@"mode"] intValue];
|
prop.fMode = [[pdict objectForKey:@"mode"] intValue];
|
||||||
prop.fDivisions = [[pdict objectForKey:@"divisions"] 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);
|
song->fProperties.push_back(prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,18 +13,6 @@
|
||||||
|
|
||||||
@implementation VLDocument (XML)
|
@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 *)XMLFileWrapperWithError:(NSError **)outError flat:(BOOL)flat;
|
||||||
{
|
{
|
||||||
NSFileWrapper * contents = [self fileWrapperWithFilter:@"VLMusicXMLType" error:outError];
|
NSFileWrapper * contents = [self fileWrapperWithFilter:@"VLMusicXMLType" error:outError];
|
||||||
|
@ -40,13 +28,6 @@
|
||||||
autorelease];
|
autorelease];
|
||||||
[contents setPreferredFilename:@"Song"];
|
[contents setPreferredFilename:@"Song"];
|
||||||
[wrap addFileWrapper:contents];
|
[wrap addFileWrapper:contents];
|
||||||
NSDictionary * prop =
|
|
||||||
[self dictionaryWithValuesForKeys:[self propertyKeys]];
|
|
||||||
[wrap addRegularFileWithContents:
|
|
||||||
[NSPropertyListSerialization dataFromPropertyList:prop
|
|
||||||
format:NSPropertyListXMLFormat_v1_0
|
|
||||||
errorDescription:nil]
|
|
||||||
preferredFilename:@"Properties"];
|
|
||||||
if (vcsWrapper)
|
if (vcsWrapper)
|
||||||
[wrap addFileWrapper:vcsWrapper];
|
[wrap addFileWrapper:vcsWrapper];
|
||||||
|
|
||||||
|
@ -62,6 +43,9 @@
|
||||||
|| (vcsWrapper = [wrappers objectForKey:@".svn"])
|
|| (vcsWrapper = [wrappers objectForKey:@".svn"])
|
||||||
)
|
)
|
||||||
[vcsWrapper retain];
|
[vcsWrapper retain];
|
||||||
|
//
|
||||||
|
// Read properties dictionary for backward compatibility
|
||||||
|
//
|
||||||
NSFileWrapper * prop = [wrappers objectForKey:@"Properties"];
|
NSFileWrapper * prop = [wrappers objectForKey:@"Properties"];
|
||||||
if (prop) {
|
if (prop) {
|
||||||
NSUndoManager * undoMgr = [self undoManager];
|
NSUndoManager * undoMgr = [self undoManager];
|
||||||
|
|
Loading…
Reference in New Issue
Block a user