MuseScore/libmscore/utils.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1094 lines
32 KiB
C++
Raw Normal View History

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"
#include "sig.h"
#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
//---------------------------------------------------------
Measure* Score::tick2measure(const Fraction& tick) const
2012-05-26 14:26:10 +02: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
}
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()) {
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;
}
// check last measure
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
}
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
//---------------------------------------------------------
Measure* Score::tick2measureMM(const Fraction& t) const
2013-10-31 12:49:55 +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
}
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()) {
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
}
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
//---------------------------------------------------------
MeasureBase* Score::tick2measureBase(const Fraction& tick) const
2012-05-26 14:26:10 +02:00
{
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
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
}
// qDebug("tick2measureBase %d not found", tick);
2012-05-26 14:26:10 +02:00
return 0;
}
//---------------------------------------------------------
// tick2segment
//---------------------------------------------------------
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
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
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
Segment* Score::tick2segment(const Fraction& t, bool first, SegmentType st, bool useMMrest) const
2020-05-26 15:54:26 +02: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.
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) {
qDebug("no measure for tick %d", tick.ticks());
2012-05-26 14:26:10 +02:00
return 0;
}
for (Segment* segment = m->first(st); segment;) {
Fraction t1 = segment->tick();
2012-05-26 14:26:10 +02:00
Segment* nsegment = segment->next(st);
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
}
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;
}
Segment* Score::tick2segment(const Fraction& tick) const
2017-03-08 13:12:26 +01:00
{
return tick2segment(tick, false, SegmentType::All, false);
}
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
//---------------------------------------------------------
// 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
//---------------------------------------------------------
Segment* Score::tick2leftSegment(const Fraction& tick, bool useMMrest) const
2013-06-19 16:25:29 +02:00
{
Measure* m = useMMrest ? tick2measureMM(tick) : tick2measure(tick);
2013-06-19 16:25:29 +02:00
if (m == 0) {
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;
}
return ps;
}
//---------------------------------------------------------
2013-09-04 10:05:40 +02:00
// tick2rightSegment
/// return the segment at this tick position if any or
/// the first segment *after* this tick position
//---------------------------------------------------------
Segment* Score::tick2rightSegment(const Fraction& tick, bool useMMrest) const
{
Measure* m = useMMrest ? tick2measureMM(tick) : tick2measure(tick);
if (m == 0) {
qDebug("tick2nearestSegment(): not found tick %d", tick.ticks());
return 0;
}
// loop over all segments
for (Segment* s = m->first(SegmentType::ChordRest); s; s = s->next1(SegmentType::ChordRest)) {
if (tick <= s->tick()) {
return s;
2020-05-26 15:54:26 +02:00
}
}
2013-06-19 16:25:29 +02:00
return 0;
}
//---------------------------------------------------------
// tick2beatType
//---------------------------------------------------------
BeatType Score::tick2beatType(const Fraction& tick)
{
Measure* m = tick2measure(tick);
Fraction msrTick = m->tick();
TimeSigFrac timeSig = sigmap()->timesig(msrTick).nominal();
int rtick = (tick - msrTick).ticks();
if (m->isAnacrusis()) { // measure is incomplete (anacrusis)
rtick += timeSig.ticksPerMeasure() - m->ticks().ticks();
2020-05-26 15:54:26 +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
//---------------------------------------------------------
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
}
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
//---------------------------------------------------------
// 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
//
// 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
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);
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
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);
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
// 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)
{
if (raster == 0) {
return len;
2020-05-26 15:54:26 +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[] = {
QT_TRANSLATE_NOOP("utils", "c"),
QT_TRANSLATE_NOOP("utils", "c♯"),
QT_TRANSLATE_NOOP("utils", "d"),
QT_TRANSLATE_NOOP("utils", "d♯"),
QT_TRANSLATE_NOOP("utils", "e"),
QT_TRANSLATE_NOOP("utils", "f"),
QT_TRANSLATE_NOOP("utils", "f♯"),
QT_TRANSLATE_NOOP("utils", "g"),
QT_TRANSLATE_NOOP("utils", "g♯"),
QT_TRANSLATE_NOOP("utils", "a"),
QT_TRANSLATE_NOOP("utils", "a♯"),
QT_TRANSLATE_NOOP("utils", "b")
};
2012-05-26 14:26:10 +02:00
static const char* valu[] = {
QT_TRANSLATE_NOOP("utils", "C"),
QT_TRANSLATE_NOOP("utils", "C♯"),
QT_TRANSLATE_NOOP("utils", "D"),
QT_TRANSLATE_NOOP("utils", "D♯"),
QT_TRANSLATE_NOOP("utils", "E"),
QT_TRANSLATE_NOOP("utils", "F"),
QT_TRANSLATE_NOOP("utils", "F♯"),
QT_TRANSLATE_NOOP("utils", "G"),
QT_TRANSLATE_NOOP("utils", "G♯"),
QT_TRANSLATE_NOOP("utils", "A"),
QT_TRANSLATE_NOOP("utils", "A♯"),
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
}
int octave = (v / 12) - 1;
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;
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.
*/
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;
}
//---------------------------------------------------------
// 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
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
{ -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
{ 0, 2, 4, 5, 7, 9, 11 }, // C C
2020-05-26 15:54:26 +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
// 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;
}
// neither step nor gap found
// reset to beginning
if (i == 7) {
++octave;
2020-05-26 15:54:26 +02:00
i = 0;
}
// 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
}
// 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;
}
}
} 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
}
}
// 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();
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
if (chord->isGraceBefore()) {
chord = toChord(chord->parent());
2020-05-26 15:54:26 +02:00
// try to tie to next grace note
2020-05-26 15:54:26 +02: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");
return note2;
}
}
2020-05-26 15:54:26 +02:00
}
// try to tie to note in parent chord
note2 = chord->findNote(note->pitch());
if (note2) {
return note2;
2020-05-26 15:54:26 +02: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
chord = toChord(chord->parent());
2020-05-26 15:54:26 +02:00
} else {
// 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()) {
Chord* gc = gna[0];
note2 = gc->findNote(note->pitch());
if (note2) {
return note2;
}
2020-05-26 15:54:26 +02: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
// 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
int idx1 = note->unisonIndex();
while ((seg = seg->next1(SegmentType::ChordRest))) {
// skip ahead to end of current note duration as calculated above
// but just in case, stop if we find element in current track
if (seg->tick() < endTick && !seg->element(chord->track())) {
continue;
2020-05-26 15:54:26 +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()) {
continue;
}
Chord* c = toChord(e);
const int staffIdx = c->staffIdx() + c->staffMove();
if (staffIdx != chord->staffIdx() + chord->staffMove()) {
// 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
}
// 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()) {
Chord* gc = gnb[0];
Note* gn2 = gc->findNote(note->pitch());
if (gn2) {
return gn2;
2020-05-26 15:54:26 +02:00
}
}
int idx2 = 0;
2013-06-28 10:55:25 +02:00
for (Note* n : c->notes()) {
if (n->pitch() == note->pitch()) {
if (idx1 == idx2) {
if (note2 == 0 || c->track() == chord->track()) {
note2 = n;
break;
}
} else {
++idx2;
2020-05-26 15:54:26 +02:00
}
}
}
2020-05-26 15:54:26 +02:00
}
if (note2) {
2020-05-26 15:54:26 +02:00
break;
}
}
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();
Part* part = chord->part();
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))) {
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())) {
continue;
2020-05-26 15:54:26 +02:00
}
2017-08-16 10:53:03 +02:00
Chord* c = toChord(e);
int staffIdx = c->staffIdx() + c->staffMove();
if (staffIdx != chord->staffIdx() + chord->staffMove()) { // cannot happen?
continue;
2012-05-26 14:26:10 +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)
{
// 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
// returns one of { 0, 1, 2, 3, 4, 5, 6 }
2012-08-08 20:46:29 +02:00
//---------------------------------------------------------
int pitch2step(int pitch)
{
// 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
// 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];
}
//---------------------------------------------------------
// 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 {
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;
}
//---------------------------------------------------------
// 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
}
return f;
}
2013-05-13 18:49:17 +02:00
}