2012-05-26 14:26:10 +02:00
|
|
|
//=============================================================================
|
|
|
|
// MuseScore
|
|
|
|
// Music Composition & Notation
|
|
|
|
//
|
|
|
|
// Copyright (C) 2002-2011 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
|
|
|
|
//=============================================================================
|
|
|
|
|
2012-07-16 15:49:24 +02:00
|
|
|
#include "config.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
#include "score.h"
|
|
|
|
#include "page.h"
|
|
|
|
#include "segment.h"
|
|
|
|
#include "clef.h"
|
|
|
|
#include "utils.h"
|
|
|
|
#include "system.h"
|
|
|
|
#include "measure.h"
|
|
|
|
#include "pitchspelling.h"
|
|
|
|
#include "chordrest.h"
|
|
|
|
#include "part.h"
|
|
|
|
#include "staff.h"
|
|
|
|
#include "note.h"
|
|
|
|
#include "chord.h"
|
2014-06-20 17:07:22 +02:00
|
|
|
#include "key.h"
|
2016-08-08 17:46:56 +02:00
|
|
|
#include "sig.h"
|
2016-11-02 08:55:54 +01:00
|
|
|
#include "tuplet.h"
|
2017-06-23 12:40:47 +02:00
|
|
|
#include "sym.h"
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
namespace Ms {
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// handleRect
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
QRectF handleRect(const QPointF& pos)
|
|
|
|
{
|
|
|
|
return QRectF(pos.x() - 4, pos.y() - 4, 8, 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2measure
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Measure* Score::tick2measure(const Fraction& tick) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick == Fraction(-1,1)) { // special number
|
2013-08-01 10:53:14 +02:00
|
|
|
return lastMeasure();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick <= Fraction(0,1)) {
|
|
|
|
return firstMeasure();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
|
2017-01-05 11:23:47 +01:00
|
|
|
Measure* lm = 0;
|
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick < m->tick()) {
|
|
|
|
Q_ASSERT(lm);
|
2013-02-27 18:16:24 +01:00
|
|
|
return lm;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
lm = m;
|
|
|
|
}
|
2013-05-11 18:59:13 +02:00
|
|
|
// check last measure
|
2013-06-25 14:29:18 +02:00
|
|
|
if (lm && (tick >= lm->tick()) && (tick <= lm->endTick())) {
|
2013-02-27 18:16:24 +01:00
|
|
|
return lm;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("tick2measure %d (max %d) not found", tick.ticks(), lm ? lm->tick().ticks() : -1);
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-31 12:49:55 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2measureMM
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Measure* Score::tick2measureMM(const Fraction& t) const
|
2013-10-31 12:49:55 +01:00
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick(t);
|
|
|
|
if (tick == Fraction(-1,1)) {
|
2014-04-23 14:54:21 +02:00
|
|
|
return lastMeasureMM();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick < Fraction(0,1)) {
|
|
|
|
tick = Fraction(0,1);
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
|
2013-10-31 12:49:55 +01:00
|
|
|
Measure* lm = 0;
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2013-10-31 12:49:55 +01:00
|
|
|
for (Measure* m = firstMeasureMM(); m; m = m->nextMeasureMM()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick < m->tick()) {
|
|
|
|
Q_ASSERT(lm);
|
2013-10-31 12:49:55 +01:00
|
|
|
return lm;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
lm = m;
|
|
|
|
}
|
2013-10-31 12:49:55 +01:00
|
|
|
// check last measure
|
|
|
|
if (lm && (tick >= lm->tick()) && (tick <= lm->endTick())) {
|
|
|
|
return lm;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("tick2measureMM %d (max %d) not found", tick.ticks(), lm ? lm->tick().ticks() : -1);
|
2013-10-31 12:49:55 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2measureBase
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
MeasureBase* Score::tick2measureBase(const Fraction& tick) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction st = mb->tick();
|
|
|
|
Fraction l = mb->ticks();
|
2012-05-26 14:26:10 +02:00
|
|
|
if (tick >= st && tick < (st + l)) {
|
|
|
|
return mb;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2014-03-25 13:33:47 +01:00
|
|
|
// qDebug("tick2measureBase %d not found", tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2segment
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Segment* Score::tick2segmentMM(const Fraction& tick, bool first, SegmentType st) const
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2014-06-01 20:24:29 +02:00
|
|
|
return tick2segment(tick,first,st,true);
|
|
|
|
}
|
2014-03-09 11:25:12 +01:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Segment* Score::tick2segmentMM(const Fraction& tick) const
|
2017-03-08 13:12:26 +01:00
|
|
|
{
|
|
|
|
return tick2segment(tick, false, SegmentType::All, true);
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Segment* Score::tick2segmentMM(const Fraction& tick, bool first) const
|
2017-03-08 13:12:26 +01:00
|
|
|
{
|
|
|
|
return tick2segment(tick, first, SegmentType::All, true);
|
2014-06-01 20:24:29 +02:00
|
|
|
}
|
2014-03-09 11:25:12 +01:00
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Segment* Score::tick2segment(const Fraction& t, bool first, SegmentType st, bool useMMrest) const
|
2020-05-26 15:54:26 +02:00
|
|
|
{
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction tick(t);
|
2014-06-01 20:24:29 +02:00
|
|
|
Measure* m;
|
|
|
|
if (useMMrest) {
|
|
|
|
m = tick2measureMM(tick);
|
|
|
|
// When mmRest force tick to the first segment of mmRest.
|
2019-12-24 08:26:03 +01:00
|
|
|
if (m && m->isMMRest() && tick != m->endTick()) {
|
2014-06-01 20:24:29 +02:00
|
|
|
tick = m->tick();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
} else {
|
2014-06-01 20:24:29 +02:00
|
|
|
m = tick2measure(tick);
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (m == 0) {
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("no measure for tick %d", tick.ticks());
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
for (Segment* segment = m->first(st); segment;) {
|
|
|
|
Fraction t1 = segment->tick();
|
2012-05-26 14:26:10 +02:00
|
|
|
Segment* nsegment = segment->next(st);
|
2019-01-30 15:13:54 +01:00
|
|
|
if (tick == t1) {
|
|
|
|
if (first) {
|
|
|
|
return segment;
|
|
|
|
} else {
|
|
|
|
if (!nsegment || tick < nsegment->tick()) {
|
|
|
|
return segment;
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
segment = nsegment;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("no segment for tick %d (start search at %d (measure %d))", tick.ticks(), t.ticks(), m->tick().ticks());
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Segment* Score::tick2segment(const Fraction& tick) const
|
2017-03-08 13:12:26 +01:00
|
|
|
{
|
|
|
|
return tick2segment(tick, false, SegmentType::All, false);
|
|
|
|
}
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Segment* Score::tick2segment(const Fraction& tick, bool first) const
|
2017-03-08 13:12:26 +01:00
|
|
|
{
|
|
|
|
return tick2segment(tick, first, SegmentType::All, false);
|
|
|
|
}
|
|
|
|
|
2013-06-19 16:25:29 +02:00
|
|
|
//---------------------------------------------------------
|
2013-09-03 16:34:56 +02:00
|
|
|
// tick2leftSegment
|
|
|
|
/// return the segment at this tick position if any or
|
|
|
|
/// the first segment *before* this tick position
|
2013-06-19 16:25:29 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2020-02-21 20:23:26 +01:00
|
|
|
Segment* Score::tick2leftSegment(const Fraction& tick, bool useMMrest) const
|
2013-06-19 16:25:29 +02:00
|
|
|
{
|
2020-02-21 20:23:26 +01:00
|
|
|
Measure* m = useMMrest ? tick2measureMM(tick) : tick2measure(tick);
|
2013-06-19 16:25:29 +02:00
|
|
|
if (m == 0) {
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("tick2leftSegment(): not found tick %d", tick.ticks());
|
2013-06-19 16:25:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// loop over all segments
|
|
|
|
Segment* ps = 0;
|
2017-03-08 13:12:26 +01:00
|
|
|
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next(SegmentType::ChordRest)) {
|
2013-06-19 16:25:29 +02:00
|
|
|
if (tick < s->tick()) {
|
|
|
|
return ps;
|
|
|
|
} else if (tick == s->tick()) {
|
|
|
|
return s;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2013-06-19 16:25:29 +02:00
|
|
|
ps = s;
|
|
|
|
}
|
2013-09-03 16:34:56 +02:00
|
|
|
return ps;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
2013-09-04 10:05:40 +02:00
|
|
|
// tick2rightSegment
|
2013-09-03 16:34:56 +02:00
|
|
|
/// return the segment at this tick position if any or
|
|
|
|
/// the first segment *after* this tick position
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2020-02-21 20:23:26 +01:00
|
|
|
Segment* Score::tick2rightSegment(const Fraction& tick, bool useMMrest) const
|
2013-09-03 16:34:56 +02:00
|
|
|
{
|
2020-02-21 20:23:26 +01:00
|
|
|
Measure* m = useMMrest ? tick2measureMM(tick) : tick2measure(tick);
|
2013-09-03 16:34:56 +02:00
|
|
|
if (m == 0) {
|
2019-01-30 15:13:54 +01:00
|
|
|
qDebug("tick2nearestSegment(): not found tick %d", tick.ticks());
|
2013-09-03 16:34:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// loop over all segments
|
2019-05-27 12:38:03 +02:00
|
|
|
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
|
2013-09-03 16:34:56 +02:00
|
|
|
if (tick <= s->tick()) {
|
|
|
|
return s;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2013-09-03 16:34:56 +02:00
|
|
|
}
|
2013-06-19 16:25:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-08 17:46:56 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2beatType
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
BeatType Score::tick2beatType(const Fraction& tick)
|
2016-08-08 17:46:56 +02:00
|
|
|
{
|
|
|
|
Measure* m = tick2measure(tick);
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction msrTick = m->tick();
|
2016-08-08 17:46:56 +02:00
|
|
|
TimeSigFrac timeSig = sigmap()->timesig(msrTick).nominal();
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
int rtick = (tick - msrTick).ticks();
|
2016-08-08 17:46:56 +02:00
|
|
|
|
|
|
|
if (m->isAnacrusis()) { // measure is incomplete (anacrusis)
|
2019-01-30 15:13:54 +01:00
|
|
|
rtick += timeSig.ticksPerMeasure() - m->ticks().ticks();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2016-08-08 17:46:56 +02:00
|
|
|
|
|
|
|
return timeSig.rtick2beatType(rtick);
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// getStaff
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int getStaff(System* system, const QPointF& p)
|
|
|
|
{
|
|
|
|
QPointF pp = p - system->page()->pos() - system->pos();
|
|
|
|
for (int i = 0; i < system->page()->score()->nstaves(); ++i) {
|
|
|
|
qreal sp = system->spatium();
|
|
|
|
QRectF r = system->bboxStaff(i).adjusted(0.0, -sp, 0.0, sp);
|
|
|
|
if (r.contains(pp)) {
|
|
|
|
return i;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextSeg
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2019-01-30 15:13:54 +01:00
|
|
|
Fraction Score::nextSeg(const Fraction& tick, int track)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
Segment* seg = tick2segment(tick);
|
|
|
|
while (seg) {
|
2017-03-08 13:12:26 +01:00
|
|
|
seg = seg->next1(SegmentType::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (seg == 0) {
|
|
|
|
break;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
if (seg->element(track)) {
|
|
|
|
break;
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-01-30 15:13:54 +01:00
|
|
|
return seg ? seg->tick() : Fraction(-1,1);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextSeg1
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* nextSeg1(Segment* seg, int& track)
|
|
|
|
{
|
|
|
|
int staffIdx = track / VOICES;
|
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
2017-03-08 13:12:26 +01:00
|
|
|
while ((seg = seg->next1(SegmentType::ChordRest))) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int t = startTrack; t < endTrack; ++t) {
|
|
|
|
if (seg->element(t)) {
|
|
|
|
track = t;
|
|
|
|
return seg;
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevSeg1
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* prevSeg1(Segment* seg, int& track)
|
|
|
|
{
|
|
|
|
int staffIdx = track / VOICES;
|
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
2017-03-08 13:12:26 +01:00
|
|
|
while ((seg = seg->prev1(SegmentType::ChordRest))) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int t = startTrack; t < endTrack; ++t) {
|
|
|
|
if (seg->element(t)) {
|
|
|
|
track = t;
|
|
|
|
return seg;
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
//---------------------------------------------------------
|
2019-01-30 15:13:54 +01:00
|
|
|
// next/prevChordNote
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
//
|
2019-01-30 15:13:54 +01:00
|
|
|
// returns the top note of the next/previous chord. If a
|
|
|
|
// chord exists in the same track as note,
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
// it is used. If not, the topmost existing chord is used.
|
|
|
|
// May return nullptr if there is no next/prev note
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* nextChordNote(Note* note)
|
|
|
|
{
|
|
|
|
int track = note->track();
|
|
|
|
int fromTrack = (track / VOICES) * VOICES;
|
|
|
|
int toTrack = fromTrack + VOICES;
|
|
|
|
// TODO : limit to same instrument, not simply to same staff!
|
|
|
|
Segment* seg = note->chord()->segment()->nextCR(track, true);
|
|
|
|
while (seg) {
|
|
|
|
Element* targetElement = seg->elementAt(track);
|
|
|
|
// if a chord exists in the same track, return its top note
|
2016-06-10 10:30:34 +02:00
|
|
|
if (targetElement && targetElement->isChord()) {
|
|
|
|
return toChord(targetElement)->upNote();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
// if not, return topmost chord in track range
|
|
|
|
for (int i = fromTrack; i < toTrack; i++) {
|
|
|
|
targetElement = seg->elementAt(i);
|
2016-06-10 10:30:34 +02:00
|
|
|
if (targetElement && targetElement->isChord()) {
|
|
|
|
return toChord(targetElement)->upNote();
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
seg = seg->nextCR(track, true);
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
Note* prevChordNote(Note* note)
|
|
|
|
{
|
|
|
|
int track = note->track();
|
|
|
|
int fromTrack = (track / VOICES) * VOICES;
|
|
|
|
int toTrack = fromTrack + VOICES;
|
|
|
|
// TODO : limit to same instrument, not simply to same staff!
|
|
|
|
Segment* seg = note->chord()->segment()->prev1();
|
|
|
|
while (seg) {
|
2017-03-08 13:12:26 +01:00
|
|
|
if (seg->segmentType() == SegmentType::ChordRest) {
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
Element* targetElement = seg->elementAt(track);
|
|
|
|
// if a chord exists in the same track, return its top note
|
2016-06-10 10:30:34 +02:00
|
|
|
if (targetElement && targetElement->isChord()) {
|
|
|
|
return toChord(targetElement)->upNote();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
// if not, return topmost chord in track range
|
|
|
|
for (int i = fromTrack; i < toTrack; i++) {
|
|
|
|
targetElement = seg->elementAt(i);
|
2016-06-10 10:30:34 +02:00
|
|
|
if (targetElement && targetElement->isChord()) {
|
|
|
|
return toChord(targetElement)->upNote();
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
seg = seg->prev1();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
Fixes #19155, #22861 (duplicate of the former) and #23100.
__References__:
Issues: https://musescore.org/en/node/19155 https://musescore.org/en/node/22861 https://musescore.org/en/node/23100
__Description__:
Allows to change the start and end note to which a glissando is anchored after it has been entered. Either anchor can be changed independently.
The user interface follows the current working of other 'snappable' lines. Once either the start or end grip is selected:
- `[Shift]+[Left]` snaps the anchor to the previous chord, defaulting to its top note.
- `[Shift]+[Right]` snaps to the next chord, defaulting to its top note.
- `[Shift]+[Up]` snaps to the note above (possibly in a chord, voice or staff above the current one).
- `[Shift]+[Down]` snaps to the note below (possibly in a chord, voice or staff below the current one).
This permits to set the anchor points of a glissando to any note in the score, allowing several glissandi between the notes of the same two chords and other complex configurations (glissandi skipping intermediate chords, start and end notes in different voices or staves, and so on).
It is possible to move the anchor to a different staff of the same instrument, but not to a different instrument; also, it is not possible to 'cross' a change of instrument in the same staff.
__Known limitations__:
- The `[Shift]+[Up]` and `[Shift]+[Down]` use the same note-finding functions as the `[Alt]+[Up]` and `[Alt]+[Down]`actions which move the selection cursor to the above and below note, even across voices or staves. Occasionally, in particular if the note immediately above or below is not time-aligned, the algorithm has little expected results; however, the behaviour is already known to the user. Improving the algorithm would benefit both uses.
__Notes__:
- Most of the added infrastructure is not specific to glissando but to any spanner anchored to notes, then it should also add after-the-fact "snap to" note support to note-anchored text line.
- When moving an anchor, the algorithm usually prefers a note in the same voice/staff of the old note if it exists; if there is none, it tries other voices of the same staff.
- The change of anchor is undoable.
- The fix corrects the management of the `Chord::_endsGlissando` flag, taking into account that a chord can be the ending point of several glissandi and removing one of them not necessarily means the chord no longer ends a glissando (another glissando may still exists).
- The fix also improved the rendering of the glissando wavy line, with better alignment with anchor notes and, with glissando text, better text-line spacing.
2015-08-06 11:11:16 +02:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// pitchKeyAdjust
|
2018-02-14 20:20:21 +01:00
|
|
|
// change entered note to sounding pitch dependent
|
2012-05-26 14:26:10 +02:00
|
|
|
// on key.
|
|
|
|
// Example: if F is entered in G-major, a Fis is played
|
|
|
|
// key -7 ... +7
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-20 17:07:22 +02:00
|
|
|
int pitchKeyAdjust(int step, Key key)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
static int ptab[15][7] = {
|
|
|
|
// c d e f g a b
|
|
|
|
{ -1, 1, 3, 4, 6, 8, 10 }, // Bes
|
|
|
|
{ -1, 1, 3, 5, 6, 8, 10 }, // Ges
|
|
|
|
{ 0, 1, 3, 5, 6, 8, 10 }, // Des
|
|
|
|
{ 0, 1, 3, 5, 7, 8, 10 }, // As
|
|
|
|
{ 0, 2, 3, 5, 7, 8, 10 }, // Es
|
|
|
|
{ 0, 2, 3, 5, 7, 9, 10 }, // B
|
|
|
|
{ 0, 2, 4, 5, 7, 9, 10 }, // F
|
|
|
|
{ 0, 2, 4, 5, 7, 9, 11 }, // C
|
|
|
|
{ 0, 2, 4, 6, 7, 9, 11 }, // G
|
|
|
|
{ 1, 2, 4, 6, 7, 9, 11 }, // D
|
|
|
|
{ 1, 2, 4, 6, 8, 9, 11 }, // A
|
|
|
|
{ 1, 3, 4, 6, 8, 9, 11 }, // E
|
|
|
|
{ 1, 3, 4, 6, 8, 10, 11 }, // H
|
|
|
|
{ 1, 3, 5, 6, 8, 10, 11 }, // Fis
|
|
|
|
{ 1, 3, 5, 6, 8, 10, 12 }, // Cis
|
|
|
|
};
|
2014-06-20 17:07:22 +02:00
|
|
|
return ptab[int(key) + 7][step];
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// y2pitch
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-09-05 16:37:49 +02:00
|
|
|
int y2pitch(qreal y, ClefType clef, qreal _spatium)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
int l = lrint(y / _spatium * 2.0);
|
2014-06-20 17:07:22 +02:00
|
|
|
return line2pitch(l, clef, Key::C);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// line2pitch
|
|
|
|
// key -7 ... +7
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-20 17:07:22 +02:00
|
|
|
int line2pitch(int line, ClefType clef, Key key)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
2013-09-05 16:37:49 +02:00
|
|
|
int l = ClefInfo::pitchOffset(clef) - line;
|
2012-05-26 14:26:10 +02:00
|
|
|
int octave = 0;
|
|
|
|
while (l < 0) {
|
|
|
|
l += 7;
|
|
|
|
octave++;
|
|
|
|
}
|
|
|
|
octave += l / 7;
|
|
|
|
l = l % 7;
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
int pitch = pitchKeyAdjust(l, key) + octave * 12;
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (pitch > 127) {
|
|
|
|
pitch = 127;
|
|
|
|
} else if (pitch < 0) {
|
|
|
|
pitch = 0;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
return pitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// quantizeLen
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int quantizeLen(int len, int raster)
|
|
|
|
{
|
2012-08-16 21:40:23 +02:00
|
|
|
if (raster == 0) {
|
|
|
|
return len;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-08-16 21:40:23 +02:00
|
|
|
return int(((float)len / raster) + 0.5) * raster; //round to the closest multiple of raster
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static const char* vall[] = {
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "c"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "c♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "d"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "d♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "e"),
|
|
|
|
QT_TRANSLATE_NOOP("utils", "f"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "f♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "g"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "g♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "a"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "a♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "b")
|
|
|
|
};
|
2012-05-26 14:26:10 +02:00
|
|
|
static const char* valu[] = {
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "C"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "C♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "D"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "D♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "E"),
|
|
|
|
QT_TRANSLATE_NOOP("utils", "F"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "F♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "G"),
|
2019-02-18 11:41:58 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "G♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "A"),
|
2019-02-17 19:35:39 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "A♯"),
|
2014-02-11 13:30:18 +01:00
|
|
|
QT_TRANSLATE_NOOP("utils", "B")
|
|
|
|
};
|
2012-05-26 14:26:10 +02:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* Returns the string representation of the given pitch.
|
|
|
|
*
|
|
|
|
* Returns the latin letter name, accidental, and octave numeral.
|
|
|
|
* Uses upper case only for pitches 0-24.
|
|
|
|
*
|
|
|
|
* @param v
|
|
|
|
* The pitch number of the note.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* The string representation of the note.
|
|
|
|
*/
|
|
|
|
QString pitch2string(int v)
|
|
|
|
{
|
|
|
|
if (v < 0 || v > 127) {
|
|
|
|
return QString("----");
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2018-02-04 20:50:14 +01:00
|
|
|
int octave = (v / 12) - 1;
|
2014-08-16 16:53:10 +02:00
|
|
|
QString o;
|
Fix Deprecation warnings with Qt 5.15 (and 5.14)
* fix 383 warnings C4996 (deprecated) with one single line change
* fix 5 'empty' warnings about invalid slots, not sure the remaining
slots serve any purpose though
* disable warnings C4127 and C4996 for thirdparty/google_analytics
* fix 30 warnings C4996 using the suggested alternatives available since
at least Qt 5.9, so won't break building against that
* change as requested in code review plus some more fixes using the same
idea
* fixing yet more warnings
* disable deprecation warnings in qoogle_analytics for MinGW too
although I believe maxOS and Linux may use those too. Easy to extend to
those, if need be.
* Fix qml runtime warnings exactly following the advice given by that
warning, but without really know what I'm doing here or whether that is
still backwards compatible to Qt 5.9.
* Use replacements as suggested, if available and no `endl` neded with
`qDebug()`
* fix 24 more
* 2 more
* 7 more (one only seen in DEBUG mode)
* Fix the `endl` warnings
* maybe more changes this way?
* Add all warnings C5999 or bigger to the ignore list for telemetry
with Qt 5.15 Beta 1 as avoiding only C26439, C26444,C 26452, C26495,
C26498, C26812 isn't possible
* fix 2 deprecation warning new with Qt 5.15 beta 1
* fix 4 new warnings and also Qt 5.12 builds, disable some dead code
* fix deprecation warnings new with Qt 5.15's MSVC 2019 integration and
revert some (`QNetworkReply::networkError()`) that Qt 5.15 beta 2
reverted too
* fix warning reg. obsolete operator < for QVariants
* revert changes needed prior to beta 3 And clarifying some earlier
changes
* mark ToDos
* fix warning reg QString()::null
* fix a 'regression' from a revert of an earlier change
2020-06-03 12:59:19 +02:00
|
|
|
o = QString::asprintf("%d", octave);
|
2012-05-26 14:26:10 +02:00
|
|
|
int i = v % 12;
|
2014-08-16 16:53:10 +02:00
|
|
|
return qApp->translate("utils", octave < 0 ? valu[i] : vall[i]) + o;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* An array of all supported interval sorted by size.
|
|
|
|
*
|
|
|
|
* Because intervals can be spelled differently, this array
|
|
|
|
* tracks all the different valid intervals. They are arranged
|
|
|
|
* in diatonic then chromatic order.
|
|
|
|
*/
|
2020-01-13 11:00:52 +01:00
|
|
|
Interval intervalList[intervalListSize] = {
|
2012-05-26 14:26:10 +02:00
|
|
|
// diatonic - chromatic
|
|
|
|
Interval(0, 0), // 0 Perfect Unison
|
|
|
|
Interval(0, 1), // 1 Augmented Unison
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(1, 0), // 2 Diminished Second
|
|
|
|
Interval(1, 1), // 3 Minor Second
|
|
|
|
Interval(1, 2), // 4 Major Second
|
|
|
|
Interval(1, 3), // 5 Augmented Second
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(2, 2), // 6 Diminished Third
|
|
|
|
Interval(2, 3), // 7 Minor Third
|
|
|
|
Interval(2, 4), // 8 Major Third
|
|
|
|
Interval(2, 5), // 9 Augmented Third
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(3, 4), // 10 Diminished Fourth
|
|
|
|
Interval(3, 5), // 11 Perfect Fourth
|
|
|
|
Interval(3, 6), // 12 Augmented Fourth
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(4, 6), // 13 Diminished Fifth
|
|
|
|
Interval(4, 7), // 14 Perfect Fifth
|
|
|
|
Interval(4, 8), // 15 Augmented Fifth
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(5, 7), // 16 Diminished Sixth
|
|
|
|
Interval(5, 8), // 17 Minor Sixth
|
|
|
|
Interval(5, 9), // 18 Major Sixth
|
|
|
|
Interval(5, 10), // 19 Augmented Sixth
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(6, 9), // 20 Diminished Seventh
|
|
|
|
Interval(6, 10), // 21 Minor Seventh
|
|
|
|
Interval(6, 11), // 22 Major Seventh
|
|
|
|
Interval(6, 12), // 23 Augmented Seventh
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
Interval(7, 11), // 24 Diminshed Octave
|
|
|
|
Interval(7, 12) // 25 Perfect Octave
|
|
|
|
};
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Finds the most likely diatonic interval for a semitone distance.
|
|
|
|
*
|
|
|
|
* Uses the most common diatonic intervals.
|
|
|
|
*
|
|
|
|
* @param
|
|
|
|
* The number of semitones in the chromatic interval.
|
|
|
|
* Negative semitones will simply be made positive.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* The number of diatonic steps in the interval.
|
|
|
|
*/
|
|
|
|
|
|
|
|
int chromatic2diatonic(int semitones)
|
|
|
|
{
|
|
|
|
static int il[12] = {
|
|
|
|
0, // Perfect Unison
|
|
|
|
3, // Minor Second
|
|
|
|
4, // Major Second
|
|
|
|
7, // Minor Third
|
|
|
|
8, // Major Third
|
|
|
|
11, // Perfect Fourth
|
|
|
|
12, // Augmented Fourth
|
|
|
|
14, // Perfect Fifth
|
|
|
|
17, // Minor Sixth
|
|
|
|
18, // Major Sixth
|
|
|
|
21, // Minor Seventh
|
|
|
|
22, // Major Seventh
|
|
|
|
// 25 Perfect Octave
|
|
|
|
};
|
|
|
|
bool down = semitones < 0;
|
|
|
|
if (down) {
|
|
|
|
semitones = -semitones;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
int val = semitones % 12;
|
|
|
|
int octave = semitones / 12;
|
|
|
|
int intervalIndex = il[val];
|
|
|
|
int steps = intervalList[intervalIndex].diatonic;
|
|
|
|
steps = steps + octave * 7;
|
|
|
|
return down ? -steps : steps;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchInterval
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int searchInterval(int steps, int semitones)
|
|
|
|
{
|
|
|
|
unsigned n = sizeof(intervalList) / sizeof(*intervalList);
|
|
|
|
for (unsigned i = 0; i < n; ++i) {
|
|
|
|
if ((intervalList[i].diatonic == steps) && (intervalList[i].chromatic == semitones)) {
|
|
|
|
return i;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int _majorVersion, _minorVersion, _updateVersion;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Returns the program version
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
* Version in the format: MMmmuu
|
|
|
|
* Where M=Major, m=minor, and u=update
|
|
|
|
*/
|
|
|
|
|
|
|
|
int version()
|
|
|
|
{
|
|
|
|
QRegExp re("(\\d+)\\.(\\d+)\\.(\\d+)");
|
|
|
|
if (re.indexIn(VERSION) != -1) {
|
|
|
|
QStringList sl = re.capturedTexts();
|
|
|
|
if (sl.size() == 4) {
|
|
|
|
_majorVersion = sl[1].toInt();
|
|
|
|
_minorVersion = sl[2].toInt();
|
|
|
|
_updateVersion = sl[3].toInt();
|
|
|
|
return _majorVersion * 10000 + _minorVersion * 100 + _updateVersion;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// majorVersion
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int majorVersion()
|
|
|
|
{
|
|
|
|
version();
|
|
|
|
return _majorVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// minorVersion
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int minorVersion()
|
|
|
|
{
|
|
|
|
version();
|
|
|
|
return _minorVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// updateVersion
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int updateVersion()
|
|
|
|
{
|
|
|
|
version();
|
|
|
|
return _updateVersion;
|
|
|
|
}
|
|
|
|
|
2018-06-05 20:17:21 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// updateVersion
|
|
|
|
/// Up to 4 digits X.X.X.X
|
|
|
|
/// Each digit can be double XX.XX.XX.XX
|
|
|
|
/// return true if v1 < v2
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool compareVersion(QString v1, QString v2)
|
|
|
|
{
|
|
|
|
auto v1l = v1.split(".");
|
|
|
|
auto v2l = v2.split(".");
|
|
|
|
int ma = qPow(100,qMax(v1l.size(), v2l.size()));
|
|
|
|
int m = ma;
|
|
|
|
int vv1 = 0;
|
|
|
|
for (int i = 0; i < v1l.size(); i++) {
|
|
|
|
vv1 += (m * v1l[i].toInt());
|
|
|
|
m /= 100;
|
|
|
|
}
|
|
|
|
m = ma;
|
|
|
|
int vv2 = 0;
|
|
|
|
for (int i = 0; i < v2l.size(); i++) {
|
|
|
|
vv2 += (m * v2l[i].toInt());
|
|
|
|
m /= 100;
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2018-06-05 20:17:21 +02:00
|
|
|
return vv1 < vv2;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// diatonicUpDown
|
|
|
|
// used to find the second note of a trill, mordent etc.
|
|
|
|
// key -7 ... +7
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-20 17:07:22 +02:00
|
|
|
int diatonicUpDown(Key k, int pitch, int steps)
|
2012-05-26 14:26:10 +02:00
|
|
|
{
|
|
|
|
static int ptab[15][7] = {
|
|
|
|
// c c# d d# e f f# g g# a a# b
|
2015-04-30 13:57:46 +02:00
|
|
|
{ -1, 1, 3, 4, 6, 8, 10 }, // Cb Ces
|
|
|
|
{ -1, 1, 3, 5, 6, 8, 10 }, // Gb Ges
|
|
|
|
{ 0, 1, 3, 5, 6, 8, 10 }, // Db Des
|
|
|
|
{ 0, 1, 3, 5, 7, 8, 10 }, // Ab As
|
|
|
|
{ 0, 2, 3, 5, 7, 8, 10 }, // Eb Es
|
|
|
|
{ 0, 2, 3, 5, 7, 9, 10 }, // Bb B
|
|
|
|
{ 0, 2, 4, 5, 7, 9, 10 }, // F F
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2015-04-30 13:57:46 +02:00
|
|
|
{ 0, 2, 4, 5, 7, 9, 11 }, // C C
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2015-04-30 13:57:46 +02:00
|
|
|
{ 0, 2, 4, 6, 7, 9, 11 }, // G G
|
|
|
|
{ 1, 2, 4, 6, 7, 9, 11 }, // D D
|
|
|
|
{ 1, 2, 4, 6, 8, 9, 11 }, // A A
|
|
|
|
{ 1, 3, 4, 6, 8, 9, 11 }, // E E
|
|
|
|
{ 1, 3, 4, 6, 8, 10, 11 }, // B H
|
|
|
|
{ 1, 3, 5, 6, 8, 10, 11 }, // F# Fis
|
|
|
|
{ 1, 3, 5, 6, 8, 10, 12 }, // C# Cis
|
2012-05-26 14:26:10 +02:00
|
|
|
};
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2014-06-20 17:07:22 +02:00
|
|
|
int key = int(k) + 7;
|
2012-05-26 14:26:10 +02:00
|
|
|
int step = pitch % 12;
|
|
|
|
int octave = pitch / 12;
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2014-11-22 15:55:28 +01:00
|
|
|
// loop through the diatonic steps of the key looking for the given note
|
|
|
|
// or the gap where it would fit
|
|
|
|
int i = 0;
|
|
|
|
while (i < 7) {
|
|
|
|
if (ptab[key][i] >= step) {
|
2020-05-26 15:54:26 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
2014-11-22 15:55:28 +01:00
|
|
|
// neither step nor gap found
|
|
|
|
// reset to beginning
|
|
|
|
if (i == 7) {
|
|
|
|
++octave;
|
2020-05-26 15:54:26 +02:00
|
|
|
i = 0;
|
|
|
|
}
|
2014-11-22 15:55:28 +01:00
|
|
|
// if given step not found (gap found instead), and we are stepping up
|
|
|
|
// then we've already accounted for one step
|
|
|
|
if (ptab[key][i] > step && steps > 0) {
|
|
|
|
--steps;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
|
2014-11-22 15:55:28 +01:00
|
|
|
// now start counting diatonic steps up or down
|
|
|
|
if (steps > 0) {
|
|
|
|
// count up
|
|
|
|
while (steps--) {
|
|
|
|
++i;
|
|
|
|
if (i == 7) {
|
|
|
|
// hit last step; reset to beginning
|
|
|
|
++octave;
|
2020-05-26 15:54:26 +02:00
|
|
|
i = 0;
|
2014-11-22 15:55:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (steps < 0) {
|
|
|
|
// count down
|
|
|
|
while (steps++) {
|
|
|
|
--i;
|
|
|
|
if (i < 0) {
|
|
|
|
// hit first step; reset to end
|
|
|
|
--octave;
|
|
|
|
i = 6;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-22 15:55:28 +01:00
|
|
|
// convert step to pitch
|
|
|
|
step = ptab[key][i];
|
|
|
|
pitch = octave * 12 + step;
|
2012-05-26 14:26:10 +02:00
|
|
|
if (pitch < 0) {
|
|
|
|
pitch = 0;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
if (pitch > 127) {
|
|
|
|
pitch = 128;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
return pitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchTieNote
|
|
|
|
// search Note to tie to "note"
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* searchTieNote(Note* note)
|
|
|
|
{
|
|
|
|
Note* note2 = 0;
|
|
|
|
Chord* chord = note->chord();
|
|
|
|
Segment* seg = chord->segment();
|
2015-03-13 11:16:43 +01:00
|
|
|
Part* part = chord->part();
|
2012-05-26 14:26:10 +02:00
|
|
|
int strack = part->staves()->front()->idx() * VOICES;
|
|
|
|
int etrack = strack + part->staves()->size() * VOICES;
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2014-11-29 08:27:07 +01:00
|
|
|
if (chord->isGraceBefore()) {
|
2016-06-10 10:30:34 +02:00
|
|
|
chord = toChord(chord->parent());
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2017-12-21 14:03:45 +01:00
|
|
|
// try to tie to next grace note
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2017-12-21 14:03:45 +01:00
|
|
|
int index = chord->graceIndex();
|
|
|
|
for (Chord* c : chord->graceNotes()) {
|
|
|
|
if (c->graceIndex() == index + 1) {
|
|
|
|
note2 = c->findNote(note->pitch());
|
|
|
|
if (note2) {
|
2018-12-18 14:55:54 +01:00
|
|
|
//printf("found grace-grace tie\n");
|
2017-12-21 14:03:45 +01:00
|
|
|
return note2;
|
|
|
|
}
|
2014-11-29 08:27:07 +01:00
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
|
2017-12-21 14:03:45 +01:00
|
|
|
// try to tie to note in parent chord
|
2014-11-29 08:27:07 +01:00
|
|
|
note2 = chord->findNote(note->pitch());
|
2019-12-01 22:39:06 +01:00
|
|
|
if (note2) {
|
2015-02-05 08:20:57 +01:00
|
|
|
return note2;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2015-02-05 08:20:57 +01:00
|
|
|
} else if (chord->isGraceAfter()) {
|
|
|
|
// grace after
|
|
|
|
// we will try to tie to note in next normal chord, below
|
|
|
|
// meanwhile, set chord to parent chord so the endTick calculation will make sense
|
2016-06-10 10:30:34 +02:00
|
|
|
chord = toChord(chord->parent());
|
2020-05-26 15:54:26 +02:00
|
|
|
} else {
|
2015-02-05 08:20:57 +01:00
|
|
|
// normal chord
|
|
|
|
// try to tie to grace note after if present
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<Chord*> gna = chord->graceNotesAfter();
|
|
|
|
if (!gna.empty()) {
|
2015-02-05 08:20:57 +01:00
|
|
|
Chord* gc = gna[0];
|
|
|
|
note2 = gc->findNote(note->pitch());
|
2019-12-01 22:39:06 +01:00
|
|
|
if (note2) {
|
2013-08-21 11:59:41 +02:00
|
|
|
return note2;
|
2014-11-29 08:27:07 +01:00
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
2015-02-05 08:20:57 +01:00
|
|
|
// at this point, chord is a regular chord, not a grace chord
|
|
|
|
// and we are looking for a note in the *next* chord (grace or regular)
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2015-02-05 08:20:57 +01:00
|
|
|
// calculate end of current note duration
|
|
|
|
// but err on the safe side in case there is roundoff in tick count
|
2016-02-06 22:03:43 +01:00
|
|
|
Fraction endTick = chord->tick() + chord->actualTicks() - Fraction(1, 4 * 480);
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2015-02-05 08:20:57 +01:00
|
|
|
int idx1 = note->unisonIndex();
|
|
|
|
while ((seg = seg->next1(SegmentType::ChordRest))) {
|
|
|
|
// skip ahead to end of current note duration as calculated above
|
2015-01-18 18:07:45 +01:00
|
|
|
// but just in case, stop if we find element in current track
|
|
|
|
if (seg->tick() < endTick && !seg->element(chord->track())) {
|
2018-10-11 14:12:52 +02:00
|
|
|
continue;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2015-02-05 08:20:57 +01:00
|
|
|
for (int track = strack; track < etrack; ++track) {
|
2017-08-16 10:53:03 +02:00
|
|
|
Element* e = seg->element(track);
|
|
|
|
if (e == 0 || !e->isChord()) {
|
2018-10-11 14:12:52 +02:00
|
|
|
continue;
|
2015-02-05 08:20:57 +01:00
|
|
|
}
|
|
|
|
Chord* c = toChord(e);
|
2015-01-18 18:07:45 +01:00
|
|
|
const int staffIdx = c->staffIdx() + c->staffMove();
|
2018-10-11 14:12:52 +02:00
|
|
|
if (staffIdx != chord->staffIdx() + chord->staffMove()) {
|
2015-01-18 18:07:45 +01:00
|
|
|
// this check is needed as we are iterating over all staves to capture cross-staff chords
|
|
|
|
continue;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2018-10-11 14:12:52 +02:00
|
|
|
// if there are grace notes before, try to tie to first one
|
2016-02-06 22:03:43 +01:00
|
|
|
QVector<Chord*> gnb = c->graceNotesBefore();
|
|
|
|
if (!gnb.empty()) {
|
2015-02-05 08:20:57 +01:00
|
|
|
Chord* gc = gnb[0];
|
2015-10-18 17:31:53 +02:00
|
|
|
Note* gn2 = gc->findNote(note->pitch());
|
|
|
|
if (gn2) {
|
|
|
|
return gn2;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2015-02-05 08:20:57 +01:00
|
|
|
}
|
2019-12-01 22:39:06 +01:00
|
|
|
int idx2 = 0;
|
2013-06-28 10:55:25 +02:00
|
|
|
for (Note* n : c->notes()) {
|
2019-12-01 22:39:06 +01:00
|
|
|
if (n->pitch() == note->pitch()) {
|
|
|
|
if (idx1 == idx2) {
|
|
|
|
if (note2 == 0 || c->track() == chord->track()) {
|
|
|
|
note2 = n;
|
|
|
|
break;
|
2013-08-21 11:59:41 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
++idx2;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
2013-08-21 11:59:41 +02:00
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2013-08-21 11:59:41 +02:00
|
|
|
if (note2) {
|
2020-05-26 15:54:26 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-08-21 11:59:41 +02:00
|
|
|
return note2;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// searchTieNote114
|
|
|
|
// search Note to tie to "note", tie to next note in
|
|
|
|
// same voice
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Note* searchTieNote114(Note* note)
|
|
|
|
{
|
|
|
|
Note* note2 = 0;
|
|
|
|
Chord* chord = note->chord();
|
|
|
|
Segment* seg = chord->segment();
|
2015-03-13 11:16:43 +01:00
|
|
|
Part* part = chord->part();
|
2013-08-21 11:59:41 +02:00
|
|
|
int strack = part->staves()->front()->idx() * VOICES;
|
|
|
|
int etrack = strack + part->staves()->size() * VOICES;
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2017-03-08 13:12:26 +01:00
|
|
|
while ((seg = seg->next1(SegmentType::ChordRest))) {
|
2013-08-21 11:59:41 +02:00
|
|
|
for (int track = strack; track < etrack; ++track) {
|
2017-08-16 10:53:03 +02:00
|
|
|
Element* e = seg->element(track);
|
|
|
|
if (e == 0 || (!e->isChord()) || (e->track() != chord->track())) {
|
2013-08-21 11:59:41 +02:00
|
|
|
continue;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2017-08-16 10:53:03 +02:00
|
|
|
Chord* c = toChord(e);
|
2013-08-21 11:59:41 +02:00
|
|
|
int staffIdx = c->staffIdx() + c->staffMove();
|
|
|
|
if (staffIdx != chord->staffIdx() + chord->staffMove()) { // cannot happen?
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
2013-08-21 11:59:41 +02:00
|
|
|
for (Note* n : c->notes()) {
|
|
|
|
if (n->pitch() == note->pitch()) {
|
|
|
|
if (note2 == 0 || c->track() == chord->track()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
note2 = n;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
if (note2) {
|
2020-05-26 15:54:26 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
return note2;
|
|
|
|
}
|
|
|
|
|
2012-08-07 16:05:37 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// absStep
|
|
|
|
/// Compute absolute step.
|
|
|
|
/// C D E F G A B ....
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int absStep(int tpc, int pitch)
|
|
|
|
{
|
|
|
|
int line = tpc2step(tpc) + (pitch / 12) * 7;
|
|
|
|
int tpcPitch = tpc2pitch(tpc);
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2012-08-07 16:05:37 +02:00
|
|
|
if (tpcPitch < 0) {
|
|
|
|
line += 7;
|
|
|
|
} else {
|
|
|
|
line -= (tpcPitch / 12) * 7;
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2012-08-07 16:05:37 +02:00
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
int absStep(int pitch)
|
|
|
|
{
|
2013-02-12 12:39:35 +01:00
|
|
|
// TODO - does this need to be key-aware?
|
2014-06-20 17:07:22 +02:00
|
|
|
int tpc = pitch2tpc(pitch, Key::C, Prefer::NEAREST);
|
2012-08-07 16:05:37 +02:00
|
|
|
return absStep(tpc, pitch);
|
|
|
|
}
|
|
|
|
|
|
|
|
int absStep(int line, ClefType clef)
|
|
|
|
{
|
2013-09-05 16:37:49 +02:00
|
|
|
return ClefInfo::pitchOffset(clef) - line;
|
2012-08-07 16:05:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// relStep
|
|
|
|
/// Compute relative step from absolute step
|
|
|
|
/// which depends on actual clef. Step 0 starts on the
|
|
|
|
/// first (top) staff line.
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int relStep(int line, ClefType clef)
|
|
|
|
{
|
2013-09-05 16:37:49 +02:00
|
|
|
return ClefInfo::pitchOffset(clef) - line;
|
2012-08-07 16:05:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int relStep(int pitch, int tpc, ClefType clef)
|
|
|
|
{
|
|
|
|
return relStep(absStep(tpc, pitch), clef);
|
|
|
|
}
|
2012-05-26 14:26:10 +02:00
|
|
|
|
2012-08-08 20:46:29 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// pitch2step
|
2015-04-30 13:57:46 +02:00
|
|
|
// returns one of { 0, 1, 2, 3, 4, 5, 6 }
|
2012-08-08 20:46:29 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int pitch2step(int pitch)
|
|
|
|
{
|
2015-04-30 13:57:46 +02:00
|
|
|
// C C# D D# E F F# G G# A A# B
|
2012-08-08 20:46:29 +02:00
|
|
|
static const char tab[12] = { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 };
|
|
|
|
return tab[pitch % 12];
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// step2pitch
|
2015-04-30 13:57:46 +02:00
|
|
|
// returns one of { 0, 2, 4, 5, 7, 9, 11 }
|
2012-08-08 20:46:29 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int step2pitch(int step)
|
|
|
|
{
|
|
|
|
static const char tab[7] = { 0, 2, 4, 5, 7, 9, 11 };
|
|
|
|
return tab[step % 7];
|
|
|
|
}
|
|
|
|
|
2016-11-02 08:55:54 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// skipTuplet
|
|
|
|
// return segment of rightmost chord/rest in a
|
|
|
|
// (possible nested) tuplet
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* skipTuplet(Tuplet* tuplet)
|
|
|
|
{
|
|
|
|
DurationElement* nde = tuplet->elements().back();
|
|
|
|
while (nde->isTuplet()) {
|
|
|
|
tuplet = toTuplet(nde);
|
|
|
|
nde = tuplet->elements().back();
|
|
|
|
}
|
|
|
|
return toChordRest(nde)->segment();
|
|
|
|
}
|
2017-06-23 12:40:47 +02:00
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// toTimeSigString
|
|
|
|
// replace ascii with bravura symbols
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
std::vector<SymId> toTimeSigString(const QString& s)
|
|
|
|
{
|
|
|
|
struct Dict {
|
2017-06-23 14:20:40 +02:00
|
|
|
QChar code;
|
2017-06-23 12:40:47 +02:00
|
|
|
SymId id;
|
|
|
|
};
|
|
|
|
static const std::vector<Dict> dict = {
|
|
|
|
{ 43, SymId::timeSigPlusSmall }, // '+'
|
|
|
|
{ 48, SymId::timeSig0 }, // '0'
|
|
|
|
{ 49, SymId::timeSig1 }, // '1'
|
|
|
|
{ 50, SymId::timeSig2 }, // '2'
|
|
|
|
{ 51, SymId::timeSig3 }, // '3'
|
|
|
|
{ 52, SymId::timeSig4 }, // '4'
|
|
|
|
{ 53, SymId::timeSig5 }, // '5'
|
|
|
|
{ 54, SymId::timeSig6 }, // '6'
|
|
|
|
{ 55, SymId::timeSig7 }, // '7'
|
|
|
|
{ 56, SymId::timeSig8 }, // '8'
|
|
|
|
{ 57, SymId::timeSig9 }, // '9'
|
|
|
|
{ 67, SymId::timeSigCommon }, // 'C'
|
|
|
|
{ 40, SymId::timeSigParensLeftSmall }, // '('
|
|
|
|
{ 41, SymId::timeSigParensRightSmall }, // ')'
|
|
|
|
{ 162, SymId::timeSigCutCommon }, // '¢'
|
|
|
|
{ 59664, SymId::mensuralProlation1 },
|
|
|
|
{ 79, SymId::mensuralProlation2 }, // 'O'
|
|
|
|
{ 59665, SymId::mensuralProlation2 },
|
|
|
|
{ 216, SymId::mensuralProlation3 }, // 'Ø'
|
|
|
|
{ 59666, SymId::mensuralProlation3 },
|
|
|
|
{ 59667, SymId::mensuralProlation4 },
|
|
|
|
{ 59668, SymId::mensuralProlation5 },
|
|
|
|
{ 59670, SymId::mensuralProlation7 },
|
|
|
|
{ 59671, SymId::mensuralProlation8 },
|
|
|
|
{ 59673, SymId::mensuralProlation10 },
|
|
|
|
{ 59674, SymId::mensuralProlation11 },
|
|
|
|
};
|
2020-05-26 15:54:26 +02:00
|
|
|
|
2017-06-23 12:40:47 +02:00
|
|
|
std::vector<SymId> d;
|
|
|
|
for (auto c : s) {
|
|
|
|
for (const Dict& e : dict) {
|
|
|
|
if (c == e.code) {
|
|
|
|
d.push_back(e.id);
|
|
|
|
break;
|
|
|
|
}
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
|
|
|
}
|
2017-06-23 12:40:47 +02:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2019-11-21 22:05:06 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// actualTicks
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Fraction actualTicks(Fraction duration, Tuplet* tuplet, Fraction timeStretch)
|
|
|
|
{
|
|
|
|
Fraction f = duration / timeStretch;
|
|
|
|
for (Tuplet* t = tuplet; t; t = t->tuplet()) {
|
|
|
|
f /= t->ratio();
|
2020-05-26 15:54:26 +02:00
|
|
|
}
|
2019-11-21 22:05:06 +01:00
|
|
|
return f;
|
|
|
|
}
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|