mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 11:14:00 +00:00
Implement new duration visual model
This commit is contained in:
parent
fd684b8820
commit
a7cf4a28df
|
@ -154,6 +154,11 @@ VLNote::VLNote(std::string name)
|
||||||
fPitch = kNoPitch; // Failed to parse completely
|
fPitch = kNoPitch; // Failed to parse completely
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VLNote::VLNote(VLFraction dur, int pitch)
|
||||||
|
: fDuration(dur), fPitch(pitch), fTied(0), fVisual(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void VLNote::Name(std::string & name, bool useSharps) const
|
void VLNote::Name(std::string & name, bool useSharps) const
|
||||||
{
|
{
|
||||||
name = PitchName(fPitch, useSharps);
|
name = PitchName(fPitch, useSharps);
|
||||||
|
@ -274,6 +279,53 @@ void VLNote::MMAName(std::string & name, VLFraction at, VLFraction dur, VLFracti
|
||||||
name += '~';
|
name += '~';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VLNote::MakeRepresentable()
|
||||||
|
{
|
||||||
|
if (fDuration > 1)
|
||||||
|
fDuration = 1;
|
||||||
|
fVisual = kWhole;
|
||||||
|
VLFraction part(1,1);
|
||||||
|
VLFraction triplet(2,3);
|
||||||
|
//
|
||||||
|
// Power of 2 denominators are not triplets
|
||||||
|
//
|
||||||
|
bool nonTriplet(!(fDuration.fDenom & (fDuration.fDenom-1)));
|
||||||
|
while (part.fDenom < 64) {
|
||||||
|
if (fDuration >= part) {
|
||||||
|
fDuration = part;
|
||||||
|
return;
|
||||||
|
} else if (!nonTriplet && fDuration >= triplet) {
|
||||||
|
fDuration = triplet;
|
||||||
|
fVisual |= kTriplet;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
part /= 2;
|
||||||
|
triplet /= 2;
|
||||||
|
++fVisual;
|
||||||
|
}
|
||||||
|
fprintf(stderr, "Encountered preposterously brief note: %d/%d\n",
|
||||||
|
fDuration.fNum, fDuration.fDenom);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLNote::AlignToGrid(VLFraction at, VLFraction grid)
|
||||||
|
{
|
||||||
|
if (at+fDuration > grid) {
|
||||||
|
fDuration = grid-at;
|
||||||
|
MakeRepresentable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VLLyricsNote::VLLyricsNote(const VLNote & note)
|
||||||
|
: VLNote(note)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
VLLyricsNote::VLLyricsNote(VLFraction dur, int pitch)
|
||||||
|
: VLNote(dur, pitch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
struct VLChordModifier {
|
struct VLChordModifier {
|
||||||
const char * fName;
|
const char * fName;
|
||||||
uint32_t fAddSteps;
|
uint32_t fAddSteps;
|
||||||
|
@ -313,6 +365,11 @@ static const VLChordModifier kModifiers[] = {
|
||||||
{NULL, 0, 0}
|
{NULL, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VLChord::VLChord(VLFraction dur, int pitch, int rootPitch)
|
||||||
|
: VLNote(dur, pitch), fSteps(0), fRootPitch(kNoPitch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
VLChord::VLChord(std::string name)
|
VLChord::VLChord(std::string name)
|
||||||
{
|
{
|
||||||
size_t root;
|
size_t root;
|
||||||
|
@ -846,6 +903,141 @@ bool VLMeasure::NoChords() const
|
||||||
&& fChords.front().fPitch == VLNote::kNoPitch;
|
&& fChords.front().fPitch == VLNote::kNoPitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VLMeasure::DecomposeNotes(const VLProperties & prop, VLNoteList & decomposed) const
|
||||||
|
{
|
||||||
|
decomposed.clear();
|
||||||
|
|
||||||
|
const VLFraction kQuarterDur(1,4);
|
||||||
|
const VLFraction kEighthLoc(1,8);
|
||||||
|
const VLFraction kQuarTripLoc(1,6);
|
||||||
|
|
||||||
|
VLFraction at(0);
|
||||||
|
VLNoteList::const_iterator i = fMelody.begin();
|
||||||
|
VLNoteList::const_iterator e = fMelody.end();
|
||||||
|
int prevTriplets = 0;
|
||||||
|
int prevVisual;
|
||||||
|
VLFraction prevTripDur;
|
||||||
|
|
||||||
|
while (i!=e) {
|
||||||
|
VLNoteList::const_iterator n = i;
|
||||||
|
++n;
|
||||||
|
|
||||||
|
VLLyricsNote c = *i; // Current note, remaining duration
|
||||||
|
VLLyricsNote p = c; // Next partial note
|
||||||
|
do {
|
||||||
|
//
|
||||||
|
// Start with longest possible note
|
||||||
|
//
|
||||||
|
p.MakeRepresentable();
|
||||||
|
//
|
||||||
|
// Prefer further triplets
|
||||||
|
//
|
||||||
|
if (prevTriplets) {
|
||||||
|
if (p.fDuration >= 2*prevTripDur) {
|
||||||
|
p.fDuration = 2*prevTripDur;
|
||||||
|
if (prevTriplets == 1) {
|
||||||
|
p.fVisual = prevVisual-1;
|
||||||
|
prevTriplets = 2; // 1/8th, 1/4th triplet or similar
|
||||||
|
} else {
|
||||||
|
p.fDuration = prevTripDur; // 1/8th, 1/8th, 1/4th
|
||||||
|
p.fVisual = prevVisual;
|
||||||
|
}
|
||||||
|
goto haveDuration;
|
||||||
|
} else if (p.fDuration >= prevTripDur) {
|
||||||
|
p.fDuration = prevTripDur;
|
||||||
|
p.fVisual = prevVisual;
|
||||||
|
goto haveDuration;
|
||||||
|
} else if (p.fDuration >= prevTripDur/2) {
|
||||||
|
p.fDuration = prevTripDur/2;
|
||||||
|
p.fVisual = prevVisual+1;
|
||||||
|
prevTripDur /= 2;
|
||||||
|
if (prevTriplets == 1)
|
||||||
|
prevTriplets = 2; // 1/4th, 1/8th
|
||||||
|
else
|
||||||
|
prevTriplets = 1; // 1/4th, 1/4th, 1/8th
|
||||||
|
goto haveDuration;
|
||||||
|
}
|
||||||
|
prevTriplets = 0;
|
||||||
|
}
|
||||||
|
if (at.fDenom > 4) {
|
||||||
|
//
|
||||||
|
// Break up notes not starting on quarter beat
|
||||||
|
// - Never cross middle of measure
|
||||||
|
//
|
||||||
|
VLFraction middle;
|
||||||
|
if (prop.fTime.fNum & 1) // Treat 5/4 as 3+2, not 2+3
|
||||||
|
middle = VLFraction((prop.fTime.fNum+1)/2, prop.fTime.fDenom);
|
||||||
|
else
|
||||||
|
middle = prop.fTime / 2;
|
||||||
|
if (at < middle)
|
||||||
|
p.AlignToGrid(at, middle);
|
||||||
|
VLFraction inBeat = at % kQuarterDur;
|
||||||
|
if ((inBeat == kEighthLoc || inBeat == kQuarTripLoc)
|
||||||
|
&& p.fDuration == kQuarterDur
|
||||||
|
)
|
||||||
|
; // Allow syncopated quarters
|
||||||
|
else
|
||||||
|
p.AlignToGrid(inBeat, kQuarterDur); // Align all others
|
||||||
|
}
|
||||||
|
if (p.fVisual & VLNote::kTriplet) {
|
||||||
|
//
|
||||||
|
// Distinguish swing 8ths/16ths from triplets
|
||||||
|
//
|
||||||
|
VLFraction swung(1, prop.fDivisions < 6 ? 8 : 16);
|
||||||
|
VLFraction grid(2*swung);
|
||||||
|
if (p.fDuration == 4*swung/3 && (at % grid == 0)) {
|
||||||
|
if (p.fDuration == c.fDuration && n!=e
|
||||||
|
&& (n->fDuration == p.fDuration
|
||||||
|
|| (n->fDuration == 2*p.fDuration))
|
||||||
|
) {
|
||||||
|
; // Triplet, not swing note
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// First swing note (4th triplet -> 8th)
|
||||||
|
//
|
||||||
|
p.fVisual = (p.fVisual+1) & VLNote::kNoteHead;
|
||||||
|
}
|
||||||
|
} else if (p.fDuration == 2*swung/3
|
||||||
|
&& ((at+p.fDuration) % grid == 0)
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
// Second swing note (8th triplet -> 8th)
|
||||||
|
//
|
||||||
|
p.fVisual &= VLNote::kNoteHead;
|
||||||
|
} else if (p.fDuration != c.fDuration
|
||||||
|
&& 2*p.fDuration != c.fDuration
|
||||||
|
) {
|
||||||
|
//
|
||||||
|
// Get rid of awkward triplets
|
||||||
|
//
|
||||||
|
p.fDuration *= VLFraction(3,4);
|
||||||
|
p.fVisual = (p.fVisual+1) & VLNote::kNoteHead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
haveDuration:
|
||||||
|
if (p.fVisual & VLNote::kTriplet)
|
||||||
|
if (prevTriplets = (prevTriplets+1)%3) {
|
||||||
|
prevTripDur = p.fDuration;
|
||||||
|
prevVisual = p.fVisual;
|
||||||
|
}
|
||||||
|
p.fTied &= VLNote::kTiedWithPrev;
|
||||||
|
if (p.fDuration == c.fDuration)
|
||||||
|
p.fTied |= c.fTied & VLNote::kTiedWithNext;
|
||||||
|
else
|
||||||
|
p.fTied |= VLNote::kTiedWithNext;
|
||||||
|
if (p.fPitch == VLNote::kNoPitch)
|
||||||
|
p.fTied = VLNote::kNotTied;
|
||||||
|
decomposed.push_back(p);
|
||||||
|
at += p.fDuration;
|
||||||
|
c.fDuration -= p.fDuration;
|
||||||
|
p.fDuration = c.fDuration;
|
||||||
|
p.fTied |= VLNote::kTiedWithPrev;
|
||||||
|
p.fLyrics.clear();
|
||||||
|
} while (c.fDuration > 0);
|
||||||
|
i = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VLSong::VLSong(bool initialize)
|
VLSong::VLSong(bool initialize)
|
||||||
{
|
{
|
||||||
if (!initialize)
|
if (!initialize)
|
||||||
|
@ -865,9 +1057,8 @@ VLSong::VLSong(bool initialize)
|
||||||
void VLSong::AddMeasure()
|
void VLSong::AddMeasure()
|
||||||
{
|
{
|
||||||
VLFraction dur = fProperties.front().fTime;
|
VLFraction dur = fProperties.front().fTime;
|
||||||
VLLyricsNote rest = VLLyricsNote(VLRest(dur));
|
VLLyricsNote rest(dur);
|
||||||
VLChord rchord;
|
VLChord rchord(dur);
|
||||||
rchord.fDuration = dur;
|
|
||||||
VLMeasure meas;
|
VLMeasure meas;
|
||||||
|
|
||||||
meas.fChords.push_back(rchord);
|
meas.fChords.push_back(rchord);
|
||||||
|
@ -1357,9 +1548,8 @@ void VLSong::ChangeTime(VLFraction newTime)
|
||||||
VLProperties & prop = fProperties.front();
|
VLProperties & prop = fProperties.front();
|
||||||
if (prop.fTime == newTime)
|
if (prop.fTime == newTime)
|
||||||
return; // No change
|
return; // No change
|
||||||
VLChord rchord;
|
VLChord rchord(newTime-prop.fTime);
|
||||||
rchord.fDuration = newTime-prop.fTime;
|
VLLyricsNote rnote(newTime-prop.fTime);
|
||||||
VLLyricsNote rnote = VLLyricsNote(VLRest(newTime-prop.fTime));
|
|
||||||
for (size_t measure=0; measure<fMeasures.size(); ++measure) {
|
for (size_t measure=0; measure<fMeasures.size(); ++measure) {
|
||||||
if (newTime < prop.fTime) {
|
if (newTime < prop.fTime) {
|
||||||
VLChordList::iterator i = fMeasures[measure].fChords.begin();
|
VLChordList::iterator i = fMeasures[measure].fChords.begin();
|
||||||
|
@ -2255,10 +2445,8 @@ void VLSong::PasteMeasures(size_t beginMeasure, const VLSong & measures, int mod
|
||||||
VLMeasure rest;
|
VLMeasure rest;
|
||||||
rest.fPropIdx = fMeasures.back().fPropIdx;
|
rest.fPropIdx = fMeasures.back().fPropIdx;
|
||||||
VLFraction dur = fProperties[rest.fPropIdx].fTime;
|
VLFraction dur = fProperties[rest.fPropIdx].fTime;
|
||||||
rest.fMelody.push_back(VLLyricsNote(VLRest(dur)));
|
rest.fMelody.push_back(VLLyricsNote(dur));
|
||||||
VLChord rchord;
|
rest.fChords.push_back(VLChord(dur));
|
||||||
rchord.fDuration= dur;
|
|
||||||
rest.fChords.push_back(rchord);
|
|
||||||
|
|
||||||
fMeasures.insert(fMeasures.end(), nextMeasure-CountMeasures(), rest);
|
fMeasures.insert(fMeasures.end(), nextMeasure-CountMeasures(), rest);
|
||||||
}
|
}
|
||||||
|
@ -2276,7 +2464,7 @@ void VLSong::PasteMeasures(size_t beginMeasure, const VLSong & measures, int mod
|
||||||
void VLSong::DeleteMeasures(size_t beginMeasure, size_t endMeasure, int mode)
|
void VLSong::DeleteMeasures(size_t beginMeasure, size_t endMeasure, int mode)
|
||||||
{
|
{
|
||||||
if (mode == kOverwriteMelody) {
|
if (mode == kOverwriteMelody) {
|
||||||
VLLyricsNote rest(VLRest(fProperties.front().fTime));
|
VLLyricsNote rest(fProperties.front().fTime);
|
||||||
for (size_t m=beginMeasure; m<endMeasure; ++m) {
|
for (size_t m=beginMeasure; m<endMeasure; ++m) {
|
||||||
fMeasures[m].fMelody.clear();
|
fMeasures[m].fMelody.clear();
|
||||||
fMeasures[m].fMelody.push_back(rest);
|
fMeasures[m].fMelody.push_back(rest);
|
||||||
|
|
|
@ -132,20 +132,28 @@ struct VLNote {
|
||||||
kTiedWithNext = 1,
|
kTiedWithNext = 1,
|
||||||
kTiedWithPrev = 2
|
kTiedWithPrev = 2
|
||||||
};
|
};
|
||||||
|
//
|
||||||
VLNote() : fPitch(kNoPitch) {}
|
// Hint at visual representation (Computed in DecomposeNotes)
|
||||||
VLNote(VLFraction dur, int8_t pitch)
|
//
|
||||||
: fDuration(dur), fPitch(pitch), fTied(kNotTied)
|
uint8_t fVisual;
|
||||||
{}
|
enum {
|
||||||
|
kWhole = 0,
|
||||||
|
kHalf = 1,
|
||||||
|
kQuarter = 2,
|
||||||
|
kEighth = 3,
|
||||||
|
k16th = 4,
|
||||||
|
k32nd = 5,
|
||||||
|
|
||||||
|
kNoteHead = 0x07,
|
||||||
|
kTriplet = 0x80
|
||||||
|
};
|
||||||
|
VLNote(VLFraction dur=0, int pitch=kNoPitch);
|
||||||
VLNote(std::string name);
|
VLNote(std::string name);
|
||||||
|
|
||||||
void Name(std::string & name, bool useSharps = false) const;
|
void Name(std::string & name, bool useSharps = false) const;
|
||||||
void LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, bool & pickup, const VLProperties & prop) const;
|
void LilypondName(std::string & name, VLFraction at, VLFraction prevDur, VLFraction nextDur, bool & triplet, bool & pickup, const VLProperties & prop) const;
|
||||||
void MMAName(std::string & name, VLFraction at, VLFraction dur, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const;
|
void MMAName(std::string & name, VLFraction at, VLFraction dur, VLFraction prevDur, VLFraction nextDur, const VLProperties & prop) const;
|
||||||
};
|
void MakeRepresentable();
|
||||||
|
void AlignToGrid(VLFraction at, VLFraction grid);
|
||||||
struct VLRest : VLNote {
|
|
||||||
VLRest(VLFraction duration) : VLNote(duration, kNoPitch) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VLChord : VLNote {
|
struct VLChord : VLNote {
|
||||||
|
@ -199,7 +207,7 @@ struct VLChord : VLNote {
|
||||||
};
|
};
|
||||||
int8_t fRootPitch; // kNoPitch == no root
|
int8_t fRootPitch; // kNoPitch == no root
|
||||||
|
|
||||||
VLChord() {}
|
VLChord(VLFraction dur=0, int pitch=kNoPitch, int rootPitch=kNoPitch);
|
||||||
VLChord(std::string name);
|
VLChord(std::string name);
|
||||||
void Name(std::string & base, std::string & ext, std::string & root, bool useSharps = false) const;
|
void Name(std::string & base, std::string & ext, std::string & root, bool useSharps = false) const;
|
||||||
void LilypondName(std::string & name, bool useSharps = false) const;
|
void LilypondName(std::string & name, bool useSharps = false) const;
|
||||||
|
@ -228,9 +236,8 @@ struct VLProperties {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VLLyricsNote : VLNote {
|
struct VLLyricsNote : VLNote {
|
||||||
VLLyricsNote() {}
|
VLLyricsNote(const VLNote & note);
|
||||||
explicit VLLyricsNote(const VLNote & note)
|
VLLyricsNote(VLFraction dur=0, int pitch = kNoPitch);
|
||||||
{ *static_cast<VLNote *>(this) = note; }
|
|
||||||
|
|
||||||
std::vector<VLSyllable> fLyrics;
|
std::vector<VLSyllable> fLyrics;
|
||||||
};
|
};
|
||||||
|
@ -250,6 +257,8 @@ struct VLMeasure {
|
||||||
|
|
||||||
bool IsEmpty() const;
|
bool IsEmpty() const;
|
||||||
bool NoChords() const;
|
bool NoChords() const;
|
||||||
|
|
||||||
|
void DecomposeNotes(const VLProperties & prop, VLNoteList & decomposed) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VLRepeat {
|
struct VLRepeat {
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) drawNote:(VLFraction)dur at:(NSPoint)p
|
- (void) drawNote:(int)visual at:(NSPoint)p
|
||||||
accidental:(VLMusicElement)accidental tied:(BOOL)tied
|
accidental:(VLMusicElement)accidental tied:(BOOL)tied
|
||||||
{
|
{
|
||||||
NSPoint s = p;
|
NSPoint s = p;
|
||||||
|
@ -147,11 +147,11 @@
|
||||||
// Draw note head
|
// Draw note head
|
||||||
//
|
//
|
||||||
NSImage * head;
|
NSImage * head;
|
||||||
switch (dur.fDenom) {
|
switch (visual & VLNote::kNoteHead) {
|
||||||
case 1:
|
case VLNote::kWhole:
|
||||||
head = [self musicElement:kMusicWholeNote];
|
head = [self musicElement:kMusicWholeNote];
|
||||||
break;
|
break;
|
||||||
case 2:
|
case VLNote::kHalf:
|
||||||
head = [self musicElement:kMusicHalfNote];
|
head = [self musicElement:kMusicHalfNote];
|
||||||
s.x -= 1.0f;
|
s.x -= 1.0f;
|
||||||
break;
|
break;
|
||||||
|
@ -190,19 +190,19 @@
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
if (dur.fDenom > 1) {
|
if (visual > 0) {
|
||||||
NSBezierPath * bz = [NSBezierPath bezierPath];
|
NSBezierPath * bz = [NSBezierPath bezierPath];
|
||||||
NSPoint s1 = NSMakePoint(s.x, s.y+kStemH);
|
NSPoint s1 = NSMakePoint(s.x, s.y+kStemH);
|
||||||
NSImage * flag = nil;
|
NSImage * flag = nil;
|
||||||
switch (dur.fDenom) {
|
switch (visual) {
|
||||||
case 8:
|
case VLNote::kEighth:
|
||||||
flag = [self musicElement:kMusicEighthFlag];
|
flag = [self musicElement:kMusicEighthFlag];
|
||||||
break;
|
break;
|
||||||
case 16:
|
case VLNote::k16th:
|
||||||
flag = [self musicElement:kMusicSixteenthFlag];
|
flag = [self musicElement:kMusicSixteenthFlag];
|
||||||
s1.y += 5.0f;
|
s1.y += 5.0f;
|
||||||
break;
|
break;
|
||||||
case 32:
|
case VLNote::k32nd:
|
||||||
flag = [self musicElement:kMusicThirtysecondthFlag];
|
flag = [self musicElement:kMusicThirtysecondthFlag];
|
||||||
s1.y += 13.0f;
|
s1.y += 13.0f;
|
||||||
break;
|
break;
|
||||||
|
@ -239,34 +239,34 @@
|
||||||
fLastNoteCenter = c;
|
fLastNoteCenter = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) drawRest:(VLFraction)dur at:(NSPoint)p
|
- (void) drawRest:(int)visual at:(NSPoint)p
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Draw rest
|
// Draw rest
|
||||||
//
|
//
|
||||||
NSImage * head = nil;
|
NSImage * head = nil;
|
||||||
switch (dur.fDenom) {
|
switch (visual) {
|
||||||
case 1:
|
case VLNote::kWhole:
|
||||||
head = [self musicElement:kMusicWholeRest];
|
head = [self musicElement:kMusicWholeRest];
|
||||||
p.y += kWholeRestY;
|
p.y += kWholeRestY;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case VLNote::kHalf:
|
||||||
head = [self musicElement:kMusicHalfRest];
|
head = [self musicElement:kMusicHalfRest];
|
||||||
p.y += kHalfRestY;
|
p.y += kHalfRestY;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case VLNote::kQuarter:
|
||||||
head = [self musicElement:kMusicQuarterRest];
|
head = [self musicElement:kMusicQuarterRest];
|
||||||
p.x -= kNoteX;
|
p.x -= kNoteX;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case VLNote::kEighth:
|
||||||
head = [self musicElement:kMusicEighthRest];
|
head = [self musicElement:kMusicEighthRest];
|
||||||
p.x -= kNoteX;
|
p.x -= kNoteX;
|
||||||
break;
|
break;
|
||||||
case 16:
|
case VLNote::k16th:
|
||||||
head = [self musicElement:kMusicSixteenthRest];
|
head = [self musicElement:kMusicSixteenthRest];
|
||||||
p.x -= kNoteX;
|
p.x -= kNoteX;
|
||||||
break;
|
break;
|
||||||
case 32:
|
case VLNote::k32nd:
|
||||||
head = [self musicElement:kMusicThirtysecondthRest];
|
head = [self musicElement:kMusicThirtysecondthRest];
|
||||||
p.x -= kNoteX;
|
p.x -= kNoteX;
|
||||||
break;
|
break;
|
||||||
|
@ -287,63 +287,48 @@
|
||||||
int measIdx = m+system*fMeasPerSystem;
|
int measIdx = m+system*fMeasPerSystem;
|
||||||
if (measIdx >= song->CountMeasures())
|
if (measIdx >= song->CountMeasures())
|
||||||
break;
|
break;
|
||||||
const VLMeasure measure = song->fMeasures[measIdx];
|
const VLMeasure & measure = song->fMeasures[measIdx];
|
||||||
const VLNoteList & melody = measure.fMelody;
|
VLNoteList melody;
|
||||||
|
measure.DecomposeNotes(song->fProperties[measure.fPropIdx], melody);
|
||||||
VLFraction at(0);
|
VLFraction at(0);
|
||||||
VLFraction prevDur(0);
|
|
||||||
bool triplet = false;
|
|
||||||
for (VLNoteList::const_iterator note = melody.begin();
|
for (VLNoteList::const_iterator note = melody.begin();
|
||||||
note != melody.end();
|
note != melody.end();
|
||||||
++note
|
++note
|
||||||
) {
|
) {
|
||||||
VLFraction dur = note->fDuration;
|
BOOL tied = (note != melody.begin() || m)
|
||||||
VLFraction nextDur(0);
|
&& note->fTied & VLNote::kTiedWithPrev;
|
||||||
VLNoteList::const_iterator next = note;
|
int pitch = note->fPitch;
|
||||||
if (++next != melody.end())
|
if (pitch != VLNote::kNoPitch) {
|
||||||
nextDur = next->fDuration;
|
[self drawLedgerLinesWithPitch:pitch
|
||||||
BOOL first = !m || !(note->fTied & VLNote::kTiedWithPrev);
|
at:NSMakePoint([self noteXInMeasure:m at:at], kSystemY)];
|
||||||
int pitch = note->fPitch;
|
VLMusicElement accidental;
|
||||||
while (dur > 0) {
|
NSPoint pos =
|
||||||
VLFraction partialDur; // Actual value of note drawn
|
NSMakePoint([self noteXInMeasure:m at:at],
|
||||||
VLFraction noteDur; // Visual value of note
|
kSystemY+[self noteYWithPitch:pitch
|
||||||
prop.PartialNote(at, dur, dur==prevDur || dur==nextDur,
|
accidental:&accidental]);
|
||||||
&partialDur);
|
VLMusicElement acc = accidental;
|
||||||
prop.VisualNote(at, partialDur, triplet, ¬eDur, &triplet);
|
int step= [self stepWithPitch:pitch];
|
||||||
prevDur = partialDur;
|
if (acc == accidentals[step])
|
||||||
if (pitch != VLNote::kNoPitch) {
|
acc = kMusicNothing; // Don't repeat accidentals
|
||||||
[self drawLedgerLinesWithPitch:pitch
|
else if (acc == kMusicNothing)
|
||||||
at:NSMakePoint([self noteXInMeasure:m at:at], kSystemY)];
|
if (accidentals[step] == kMusicNatural) // Resume signature
|
||||||
VLMusicElement accidental;
|
acc = prop.fKey < 0 ? kMusicFlat : kMusicSharp;
|
||||||
NSPoint pos =
|
else
|
||||||
NSMakePoint([self noteXInMeasure:m at:at],
|
acc = kMusicNatural;
|
||||||
kSystemY+[self noteYWithPitch:pitch
|
[self drawNote:note->fVisual & VLNote::kNoteHead
|
||||||
accidental:&accidental]);
|
at: pos
|
||||||
VLMusicElement acc = accidental;
|
accidental: acc
|
||||||
int step= [self stepWithPitch:pitch];
|
tied:tied];
|
||||||
if (acc == accidentals[step])
|
accidentals[step] = accidental;
|
||||||
acc = kMusicNothing; // Don't repeat accidentals
|
} else {
|
||||||
else if (acc == kMusicNothing)
|
VLMusicElement accidental;
|
||||||
if (accidentals[step] == kMusicNatural) // Resume signature
|
NSPoint pos =
|
||||||
acc = prop.fKey < 0 ? kMusicFlat : kMusicSharp;
|
NSMakePoint([self noteXInMeasure:m at:at],
|
||||||
else
|
kSystemY+[self noteYWithPitch:65
|
||||||
acc = kMusicNatural;
|
accidental:&accidental]);
|
||||||
[self drawNote:noteDur
|
[self drawRest:note->fVisual & VLNote::kNoteHead at: pos];
|
||||||
at: pos
|
|
||||||
accidental: acc
|
|
||||||
tied:!first];
|
|
||||||
accidentals[step] = accidental;
|
|
||||||
} else {
|
|
||||||
VLMusicElement accidental;
|
|
||||||
NSPoint pos =
|
|
||||||
NSMakePoint([self noteXInMeasure:m at:at],
|
|
||||||
kSystemY+[self noteYWithPitch:65
|
|
||||||
accidental:&accidental]);
|
|
||||||
[self drawRest:noteDur at: pos];
|
|
||||||
}
|
|
||||||
dur -= partialDur;
|
|
||||||
at += partialDur;
|
|
||||||
first = NO;
|
|
||||||
}
|
}
|
||||||
|
at += note->fDuration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fCursorPitch != VLNote::kNoPitch && fCursorMeasure/fMeasPerSystem == system)
|
if (fCursorPitch != VLNote::kNoPitch && fCursorMeasure/fMeasPerSystem == system)
|
||||||
|
|
|
@ -38,7 +38,7 @@ std::ostream & operator<<(std::ostream & s, VLFraction f)
|
||||||
s << whole;
|
s << whole;
|
||||||
f -= whole;
|
f -= whole;
|
||||||
if (f.fNum)
|
if (f.fNum)
|
||||||
s << int(f.fNum) << '/' << int(f.fDenom);
|
s << '.' << int(f.fNum) << '/' << int(f.fDenom);
|
||||||
} else if (f.fNum) {
|
} else if (f.fNum) {
|
||||||
s << int(f.fNum) << '/' << int(f.fDenom);
|
s << int(f.fNum) << '/' << int(f.fDenom);
|
||||||
} else {
|
} else {
|
||||||
|
@ -65,50 +65,44 @@ void PrintName(const VLChord & chord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class C> class Printer {
|
void ChordPrinter(const VLChord & chord)
|
||||||
VLProperties * fProp;
|
|
||||||
VLFraction fAt;
|
|
||||||
public:
|
|
||||||
Printer(VLProperties * prop) : fProp(prop) {}
|
|
||||||
|
|
||||||
void operator()(const C& obj) {
|
|
||||||
PrintName(obj);
|
|
||||||
|
|
||||||
std::cout << '@';
|
|
||||||
for (VLFraction d = obj.fDuration; d > 0; ) {
|
|
||||||
VLFraction p;
|
|
||||||
fProp->PartialNote(fAt, d, &p);
|
|
||||||
if (d < obj.fDuration)
|
|
||||||
std::cout << '+';
|
|
||||||
std::cout << p;
|
|
||||||
d -= p;
|
|
||||||
fAt += p;
|
|
||||||
}
|
|
||||||
std::cout << ' ';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void PrintMeasure(const VLMeasure & measure)
|
|
||||||
{
|
{
|
||||||
std::for_each(measure.fChords.begin(), measure.fChords.end(),
|
PrintName(chord);
|
||||||
Printer<VLChord>(measure.fProperties));
|
if (chord.fDuration != VLFraction(1,4))
|
||||||
|
std::cout << " * " << chord.fDuration;
|
||||||
|
std::cout << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
void NotePrinter(const VLLyricsNote & note)
|
||||||
|
{
|
||||||
|
if (note.fTied & VLNote::kTiedWithPrev)
|
||||||
|
std::cout << "~ ";
|
||||||
|
PrintName(note);
|
||||||
|
std::cout << ' ' << note.fDuration
|
||||||
|
<< '[' << ((note.fVisual & VLNote::kTriplet) ? "T" : "")
|
||||||
|
<< (note.fVisual & VLNote::kNoteHead)["124863"] << "] ";
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintMeasure(const VLMeasure & measure, const VLProperties & prop)
|
||||||
|
{
|
||||||
|
std::for_each(measure.fChords.begin(), measure.fChords.end(), ChordPrinter);
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
std::for_each(measure.fMelody.begin(), measure.fMelody.end(),
|
VLNoteList decomposed;
|
||||||
Printer<VLNote>(measure.fProperties));
|
measure.DecomposeNotes(prop, decomposed);
|
||||||
|
std::for_each(decomposed.begin(), decomposed.end(), NotePrinter);
|
||||||
std::cout << std::endl << std::endl;
|
std::cout << std::endl << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintSong(const VLSong & song)
|
void PrintSong(const VLSong & song)
|
||||||
{
|
{
|
||||||
std::for_each(song.fMeasures.begin(), song.fMeasures.end(), PrintMeasure);
|
for (size_t i=0; i<song.CountMeasures()-song.EmptyEnding(); ++i)
|
||||||
|
PrintMeasure(song.fMeasures[i], song.fProperties[song.fMeasures[i].fPropIdx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int, char *const [])
|
int main(int, char *const [])
|
||||||
{
|
{
|
||||||
VLSong song;
|
VLSong song;
|
||||||
|
|
||||||
song.fMeasures.resize(4);
|
|
||||||
|
|
||||||
char command;
|
char command;
|
||||||
while (std::cin >> command) {
|
while (std::cin >> command) {
|
||||||
int measure;
|
int measure;
|
||||||
|
@ -117,7 +111,7 @@ int main(int, char *const [])
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case '+':
|
case '+':
|
||||||
std::cin >> name >> measure >> at;
|
std::cin >> name >> measure >> at;
|
||||||
song.AddNote(name, measure, at);
|
song.AddNote(VLNote(name), measure, at);
|
||||||
|
|
||||||
PrintSong(song);
|
PrintSong(song);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user