MuseScore/libmscore/location.cpp
ws ec3be9a99a Replacd integer midi tick values by fractions.
- tick names a position on the time axis
- tick is always a Fraction()
- only Measure() and Segment() (and Tuplet?) have a tick value
- tick() for an generic element return only a sensible value if isMeasure() or isSegment() or isSegment(parent())

- "ticks" names a duration stored in a Fraction()
- the tick value for an Segment is relative to its measure

- rename "duration" to "ticks"
- rename afrac() to tick()
- rename rfrac() to rtick()
- rename some variables, changing "fraction" into "tick"
  (example: actualFraction() into actualTicks())

- Lyrics ticks are written as Fraction, on read if xmlreader sees a "/" it reads a fraction
  else midi ticks for backwards compatibility
2019-02-18 11:46:05 +01:00

293 lines
9.2 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2018 Werner Schweer
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation and appearing in
// the file LICENCE.GPL
//=============================================================================
#include "location.h"
#include "chord.h"
#include "element.h"
#include "measure.h"
#include "mscore.h"
#include "xml.h"
namespace Ms {
static constexpr Location absDefaults = Location::absolute();
static constexpr Location relDefaults = Location::relative();
//---------------------------------------------------------
// Location::track
//---------------------------------------------------------
int Location::track() const
{
if ((_staff == absDefaults._staff) || (_voice == absDefaults._voice))
return INT_MIN;
return VOICES * _staff + _voice;
}
//---------------------------------------------------------
// Location::setTrack
//---------------------------------------------------------
void Location::setTrack(int track)
{
_staff = track / VOICES;
_voice = track % VOICES;
}
//---------------------------------------------------------
// Location::write
// Only relative locations should be written
//---------------------------------------------------------
void Location::write(XmlWriter& xml) const
{
Q_ASSERT(isRelative());
xml.stag("location");
xml.tag("staves", _staff, relDefaults._staff);
xml.tag("voices", _voice, relDefaults._voice);
xml.tag("measures", _measure, relDefaults._measure);
xml.tag("fractions", _frac.reduced(), relDefaults._frac);
xml.tag("grace", _graceIndex, relDefaults._graceIndex);
xml.tag("notes", _note, relDefaults._note);
xml.etag();
}
//---------------------------------------------------------
// Location::read
//---------------------------------------------------------
void Location::read(XmlReader& e)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "staves")
_staff = e.readInt();
else if (tag == "voices")
_voice = e.readInt();
else if (tag == "measures")
_measure = e.readInt();
else if (tag == "fractions")
_frac = e.readFraction();
else if (tag == "grace")
_graceIndex = e.readInt();
else if (tag == "notes")
_note = e.readInt();
else
e.unknown();
}
}
//---------------------------------------------------------
// Location::toAbsolute
//---------------------------------------------------------
void Location::toAbsolute(const Location& ref)
{
if (isAbsolute())
return;
_staff += ref._staff;
_voice += ref._voice;
_measure += ref._measure;
_frac += ref._frac;
_note += ref._note;
_rel = false;
}
//---------------------------------------------------------
// Location::toRelative
//---------------------------------------------------------
void Location::toRelative(const Location& ref)
{
if (isRelative())
return;
_staff -= ref._staff;
_voice -= ref._voice;
_measure -= ref._measure;
_frac -= ref._frac;
_note -= ref._note;
_rel = true;
}
//---------------------------------------------------------
// Location::fillPositionForElement
// Fills default fields of Location by values relevant
// for the given Element. This function fills only
// position values, not dealing with parameters specific
// for Chords and Notes, like grace index.
//---------------------------------------------------------
void Location::fillPositionForElement(const Element* e, bool absfrac)
{
Q_ASSERT(isAbsolute());
if (!e) {
qWarning("Location::fillPositionForElement: element is nullptr");
return;
}
if (track() == absDefaults.track())
setTrack(track(e));
if (frac() == absDefaults.frac())
setFrac(absfrac ? e->tick() : e->rtick());
if (measure() == absDefaults.measure())
setMeasure(absfrac ? 0 : measure(e));
}
//---------------------------------------------------------
// Location::fillForElement
// Fills default fields of Location by values relevant
// for the given Element, including parameters specific
// for Chords and Notes.
//---------------------------------------------------------
void Location::fillForElement(const Element* e, bool absfrac)
{
Q_ASSERT(isAbsolute());
if (!e) {
qWarning("Location::fillForElement: element is nullptr");
return;
}
fillPositionForElement(e, absfrac);
setGraceIndex(graceIndex(e));
setNote(note(e));
}
//---------------------------------------------------------
// Location::forElement
//---------------------------------------------------------
Location Location::forElement(const Element* e, bool absfrac)
{
Location i = Location::absolute();
i.fillForElement(e, absfrac);
return i;
}
//---------------------------------------------------------
// Location::positionForElement
//---------------------------------------------------------
Location Location::positionForElement(const Element* e, bool absfrac)
{
Location i = Location::absolute();
i.fillPositionForElement(e, absfrac);
return i;
}
//---------------------------------------------------------
// Location::track
//---------------------------------------------------------
int Location::track(const Element* e)
{
int track = e->track();
if (track < 0) {
const MeasureBase* mb = e->findMeasureBase();
if (mb && !mb->isMeasure()) {
// Such elements are written in the first staff,
// see writeMeasure() in scorefile.cpp
track = 0;
}
}
return track;
}
//---------------------------------------------------------
// Location::measure
//---------------------------------------------------------
int Location::measure(const Element* e)
{
const Measure* m = toMeasure(e->findMeasure());
if (m)
return m->measureIndex();
qWarning("Location::measure: cannot find element's measure (%s)", e->name());
return 0;
}
//---------------------------------------------------------
// Location::graceIndex
//---------------------------------------------------------
int Location::graceIndex(const Element* e)
{
if (e->isChord() || (e->parent() && e->parent()->isChord())) {
const Chord* ch = e->isChord() ? toChord(e) : toChord(e->parent());
if (ch->isGrace())
return ch->graceIndex();
}
return absDefaults.graceIndex();
}
//---------------------------------------------------------
// Location::note
//---------------------------------------------------------
int Location::note(const Element* e)
{
if (e->isNote()) {
const Note* n = toNote(e);
const std::vector<Note*>& notes = n->chord()->notes();
if (notes.size() == 1)
return 0;
int noteIdx;
for (noteIdx = 0; noteIdx < int(notes.size()); ++noteIdx) {
if (n == notes.at(noteIdx))
break;
}
return noteIdx;
}
return absDefaults.note();
}
//---------------------------------------------------------
// Location::getLocationProperty
//---------------------------------------------------------
QVariant Location::getLocationProperty(Pid pid, const Element* start, const Element* end)
{
switch(pid) {
case Pid::LOCATION_STAVES:
return (track(start) / VOICES) - (track(end) / VOICES);
case Pid::LOCATION_VOICES:
return (track(start) % VOICES) - (track(end) / VOICES);
case Pid::LOCATION_MEASURES:
return measure(end) - measure(start);
case Pid::LOCATION_FRACTIONS:
return end->rtick() - start->rtick();
case Pid::LOCATION_GRACE:
return graceIndex(end) - graceIndex(end);
case Pid::LOCATION_NOTE:
return note(start) - note(end);
default:
return QVariant();
}
}
//---------------------------------------------------------
// Location::operator==
//---------------------------------------------------------
bool Location::operator==(const Location& pi2) const {
const Location& pi1 = *this;
return ((pi1._frac == pi2._frac)
&& (pi1._measure == pi2._measure)
&& (pi1._voice == pi2._voice)
&& (pi1._staff == pi2._staff)
&& (pi1._graceIndex == pi2._graceIndex)
&& (pi1._note == pi2._note)
);
}
}