MuseScore/libmscore/sig.cpp

415 lines
12 KiB
C++

//=============================================================================
// MuseScore
// Music Composition & Notation
//
// Copyright (C) 2002-2007 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 "sig.h"
#include "xml.h"
namespace Ms {
//---------------------------------------------------------
// ticks_beat
//---------------------------------------------------------
static int ticks_beat(int n)
{
int m = (MScore::division * 4) / n;
if ((MScore::division * 4) % n) {
qFatal("Mscore: ticks_beat(): bad divisor %d", n);
}
return m;
}
//---------------------------------------------------------
// ticks_measure
//---------------------------------------------------------
static int ticks_measure(const Fraction& f)
{
return (MScore::division * 4 * f.numerator()) / f.denominator();
}
//---------------------------------------------------------
// rtick2beatType
//---------------------------------------------------------
BeatType TimeSigFrac::rtick2beatType(int rtick)
{
if (rtick == 0)
return BeatType::DOWNBEAT;
if (rtick % dUnitTicks() != 0)
return BeatType::SUBBEAT;
if (isCompound()) {
if (rtick % beatTicks() != 0)
return BeatType::COMPOUND_SUBBEAT;
}
const int beatNum = rtick / beatTicks();
int stressBeat = 0;
if (isTriple())
stressBeat = 3;
else if (isDuple())
stressBeat = 2;
if (stressBeat && beatNum % stressBeat == 0)
return isCompound() ? BeatType::SIMPLE_STRESSED : BeatType::COMPOUND_STRESSED;
return isCompound() ? BeatType::SIMPLE_UNSTRESSED : BeatType::COMPOUND_UNSTRESSED;
}
//---------------------------------------------------------
// operator==
//---------------------------------------------------------
bool SigEvent::operator==(const SigEvent& e) const
{
return (_timesig.identical(e._timesig));
}
//---------------------------------------------------------
// add
//---------------------------------------------------------
void TimeSigMap::add(int tick, const Fraction& f)
{
if (!f.isValid()) {
qDebug("illegal signature %d/%d", f.numerator(), f.denominator());
}
(*this)[tick] = SigEvent(f);
normalize();
}
void TimeSigMap::add(int tick, const SigEvent& ev)
{
(*this)[tick] = ev;
normalize();
}
//---------------------------------------------------------
// del
//---------------------------------------------------------
void TimeSigMap::del(int tick)
{
erase(tick);
normalize();
}
//---------------------------------------------------------
// TimeSigMap::normalize
//---------------------------------------------------------
void TimeSigMap::normalize()
{
int z = 4;
int n = 4;
int tick = 0;
TimeSigFrac bar;
int tm = ticks_measure(TimeSigFrac(z, n));
for (auto i = begin(); i != end(); ++i) {
SigEvent& e = i->second;
bar += TimeSigFrac(i->first - tick, tm).reduced();
e.setBar(bar.numerator() / bar.denominator());
tick = i->first;
tm = ticks_measure(e.timesig());
}
}
//---------------------------------------------------------
// timesig
//---------------------------------------------------------
const SigEvent& TimeSigMap::timesig(int tick) const
{
static const SigEvent ev(TimeSigFrac(4, 4));
if (empty())
return ev;
auto i = upper_bound(tick);
if (i != begin())
--i;
return i->second;
}
//---------------------------------------------------------
// tickValues
// t - some time moment on timeline (in ticks)
//
// Result - values computed for this time moment:
// bar - index of bar containing time moment t
// beat - index of beat in bar containing t
// tick - position of t in beat (in ticks)
//---------------------------------------------------------
void TimeSigMap::tickValues(int t, int* bar, int* beat, int* tick) const
{
if (empty()) {
*bar = 0;
*beat = 0;
*tick = 0;
return;
}
auto e = upper_bound(t);
if (empty() || e == begin()) {
qFatal("tickValue(0x%x) not found", t);
}
--e;
int delta = t - e->first;
int ticksB = ticks_beat(e->second.timesig().denominator()); // ticks in beat
int ticksM = ticksB * e->second.timesig().numerator(); // ticks in measure (bar)
if (ticksM == 0) {
qDebug("TimeSigMap::tickValues: at %d %s", t, qPrintable(e->second.timesig().print()));
*bar = 0;
*beat = 0;
*tick = 0;
return;
}
*bar = e->second.bar() + delta / ticksM;
int rest = delta % ticksM;
*beat = rest / ticksB;
*tick = rest % ticksB;
}
//---------------------------------------------------------
// pos
// Return string representation of tick position.
// This is not reentrant and only for debugging!
//---------------------------------------------------------
QString TimeSigMap::pos(int t) const
{
int bar, beat, tick;
tickValues(t, &bar, &beat, &tick);
return QString("%1:%2:%3").arg(bar+1).arg(beat).arg(tick);
}
//---------------------------------------------------------
// bar2tick
// Returns the absolute start time (in ticks)
// of beat in bar
//---------------------------------------------------------
int TimeSigMap::bar2tick(int bar, int beat) const
{
// bar - index of current bar (terminology: bar == measure)
// beat - index of beat in current bar
auto e = begin();
for (; e != end(); ++e) {
if (bar < e->second.bar())
break;
}
if (empty() || e == begin()) {
qDebug("TimeSigMap::bar2tick(): not found(%d,%d) not found", bar, beat);
if (empty())
qDebug(" list is empty");
return 0;
}
--e; // current TimeSigMap value
int ticksB = ticks_beat(e->second.timesig().denominator()); // ticks per beat
int ticksM = ticksB * e->second.timesig().numerator(); // bar length in ticks
return e->first + (bar - e->second.bar()) * ticksM + ticksB * beat;
}
//---------------------------------------------------------
// TimeSigMap::write
//---------------------------------------------------------
void TimeSigMap::write(Xml& xml) const
{
xml.stag("siglist");
for (auto i = begin(); i != end(); ++i)
i->second.write(xml, i->first);
xml.etag();
}
//---------------------------------------------------------
// TimeSigMap::read
//---------------------------------------------------------
void TimeSigMap::read(XmlReader& e, int fileDivision)
{
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "sig") {
SigEvent t;
int tick = t.read(e, fileDivision);
(*this)[tick] = t;
}
else
e.unknown();
}
normalize();
}
//---------------------------------------------------------
// SigEvent
//---------------------------------------------------------
SigEvent::SigEvent(const SigEvent& e)
{
_timesig = e._timesig;
_nominal = e._nominal;
_bar = e._bar;
}
//---------------------------------------------------------
// SigEvent::write
//---------------------------------------------------------
void SigEvent::write(Xml& xml, int tick) const
{
xml.stag(QString("sig tick=\"%1\"").arg(tick));
xml.tag("nom", _timesig.numerator());
xml.tag("denom", _timesig.denominator());
xml.etag();
}
//---------------------------------------------------------
// SigEvent::read
//---------------------------------------------------------
int SigEvent::read(XmlReader& e, int fileDivision)
{
int tick = e.intAttribute("tick", 0);
tick = tick * MScore::division / fileDivision;
int numerator = 1;
int denominator = 1;
int denominator2 = -1;
int numerator2 = -1;
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "nom")
numerator = e.readInt();
else if (tag == "denom")
denominator = e.readInt();
else if (tag == "nom2")
numerator2 = e.readInt();
else if (tag == "denom2")
denominator2 = e.readInt();
else
e.unknown();
}
if ((numerator2 == -1) || (denominator2 == -1)) {
numerator2 = numerator;
denominator2 = denominator;
}
_timesig = TimeSigFrac(numerator, denominator);
_nominal = TimeSigFrac(numerator2, denominator2);
return tick;
}
//---------------------------------------------------------
// ticksPerMeasure
//---------------------------------------------------------
int ticksPerMeasure(int numerator, int denominator)
{
return ticks_beat(denominator) * numerator;
}
//---------------------------------------------------------
// rasterEval
//---------------------------------------------------------
unsigned rasterEval(unsigned t, int raster, int startTick,
int numerator, int denominator, int addition)
{
int delta = t - startTick;
int ticksM = ticksPerMeasure(numerator, denominator);
if (raster == 0)
raster = ticksM;
int rest = delta % ticksM;
int bb = (delta / ticksM) * ticksM;
return startTick + bb + ((rest + addition) / raster) * raster;
}
//---------------------------------------------------------
// raster
//---------------------------------------------------------
unsigned TimeSigMap::raster(unsigned t, int raster) const
{
if (raster == 1)
return t;
auto e = upper_bound(t);
if (e == end()) {
qDebug("TimeSigMap::raster(%x,)", t);
return t;
}
auto timesig = e->second.timesig();
return rasterEval(t, raster, e->first, timesig.numerator(),
timesig.denominator(), raster / 2);
}
//---------------------------------------------------------
// raster1
// round down
//---------------------------------------------------------
unsigned TimeSigMap::raster1(unsigned t, int raster) const
{
if (raster == 1)
return t;
auto e = upper_bound(t);
auto timesig = e->second.timesig();
return rasterEval(t, raster, e->first, timesig.numerator(),
timesig.denominator(), 0);
}
//---------------------------------------------------------
// raster2
// round up
//---------------------------------------------------------
unsigned TimeSigMap::raster2(unsigned t, int raster) const
{
if (raster == 1)
return t;
auto e = upper_bound(t);
auto timesig = e->second.timesig();
return rasterEval(t, raster, e->first, timesig.numerator(),
timesig.denominator(), raster - 1);
}
//---------------------------------------------------------
// rasterStep
//---------------------------------------------------------
int TimeSigMap::rasterStep(unsigned t, int raster) const
{
if (raster == 0) {
auto timesig = upper_bound(t)->second.timesig();
return ticksPerMeasure(timesig.denominator(), timesig.numerator());
}
return raster;
}
//---------------------------------------------------------
// TimeSigMap::dump
//---------------------------------------------------------
void TimeSigMap::dump() const
{
qDebug("TimeSigMap:");
for (auto i = begin(); i != end(); ++i)
qDebug("%6d timesig: %s measure: %d",
i->first, qPrintable(i->second.timesig().print()), i->second.bar());
}
}