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"
|
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(int tick) const
|
|
|
|
{
|
2013-08-01 10:53:14 +02:00
|
|
|
if (tick == -1)
|
|
|
|
return lastMeasure();
|
2013-02-27 14:41:04 +01:00
|
|
|
Measure* lm = 0;
|
2013-05-06 14:20:31 +02:00
|
|
|
|
2012-10-16 17:11:35 +02:00
|
|
|
for (Measure* m = firstMeasure(); m; m = m->nextMeasure()) {
|
2014-03-09 11:32:53 +01:00
|
|
|
if (tick < m->tick())
|
2013-02-27 14:41:04 +01:00
|
|
|
return lm;
|
|
|
|
lm = m;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
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;
|
2014-08-19 13:34:14 +02:00
|
|
|
qDebug("tick2measure %d (max %d) not found", tick, lm ? lm->tick() : -1);
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-31 12:49:55 +01:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2measureMM
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Measure* Score::tick2measureMM(int tick) const
|
|
|
|
{
|
|
|
|
if (tick == -1)
|
2014-04-23 14:54:21 +02:00
|
|
|
return lastMeasureMM();
|
2013-10-31 12:49:55 +01:00
|
|
|
Measure* lm = 0;
|
|
|
|
|
|
|
|
for (Measure* m = firstMeasureMM(); m; m = m->nextMeasureMM()) {
|
|
|
|
if (tick < m->tick())
|
|
|
|
return lm;
|
|
|
|
lm = m;
|
|
|
|
}
|
|
|
|
// check last measure
|
|
|
|
if (lm && (tick >= lm->tick()) && (tick <= lm->endTick()))
|
|
|
|
return lm;
|
2014-08-19 13:34:14 +02:00
|
|
|
qDebug("tick2measureMM %d (max %d) not found", tick, lm ? lm->tick() : -1);
|
2013-10-31 12:49:55 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2measureBase
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
MeasureBase* Score::tick2measureBase(int tick) const
|
|
|
|
{
|
|
|
|
for (MeasureBase* mb = first(); mb; mb = mb->next()) {
|
|
|
|
int st = mb->tick();
|
|
|
|
int l = mb->ticks();
|
|
|
|
if (tick >= st && tick < (st+l))
|
|
|
|
return mb;
|
|
|
|
}
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* Score::tick2segmentMM(int tick, bool first, Segment::Type 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
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
Segment* Score::tick2segment(int tick, bool first, Segment::Type st, bool useMMrest ) const
|
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.
|
2014-09-06 12:50:28 +02:00
|
|
|
if (m && m->isMMRest())
|
2014-06-01 20:24:29 +02:00
|
|
|
tick = m->tick();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m = tick2measure(tick);
|
2014-03-09 11:25:12 +01:00
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
if (m == 0) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug(" no segment for tick %d", tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
for (Segment* segment = m->first(st); segment;) {
|
|
|
|
int t1 = segment->tick();
|
|
|
|
Segment* nsegment = segment->next(st);
|
|
|
|
int t2 = nsegment ? nsegment->tick() : INT_MAX;
|
2013-06-10 21:13:04 +02:00
|
|
|
if ((tick == t1) && (first || (tick < t2)))
|
2012-05-26 14:26:10 +02:00
|
|
|
return segment;
|
|
|
|
segment = nsegment;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// tick2segmentEnd
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
/**
|
|
|
|
Find a segment containing a note or rest in \a track ending at \a tick
|
|
|
|
Return the segment or null
|
|
|
|
*/
|
|
|
|
|
|
|
|
Segment* Score::tick2segmentEnd(int track, int tick) const
|
|
|
|
{
|
|
|
|
Measure* m = tick2measure(tick);
|
|
|
|
if (m == 0) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("tick2segment(): not found tick %d", tick);
|
2012-05-26 14:26:10 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// loop over all segments
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* segment = m->first(Segment::Type::ChordRest); segment; segment = segment->next(Segment::Type::ChordRest)) {
|
2016-06-10 10:30:34 +02:00
|
|
|
ChordRest* cr = toChordRest(segment->element(track));
|
2013-06-19 16:25:29 +02:00
|
|
|
if (!cr)
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
|
|
|
// TODO LVI: check if following is correct, see exceptions in
|
|
|
|
// ExportMusicXml::chord() and ExportMusicXml::rest()
|
|
|
|
int endTick = cr->tick() + cr->actualTicks();
|
|
|
|
if (endTick < tick)
|
|
|
|
continue; // not found yet
|
|
|
|
else if (endTick == tick) {
|
|
|
|
return segment; // found it
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// endTick > tick (beyond the tick we are looking for)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
//---------------------------------------------------------
|
|
|
|
|
2013-09-03 16:34:56 +02:00
|
|
|
Segment* Score::tick2leftSegment(int tick) const
|
2013-06-19 16:25:29 +02:00
|
|
|
{
|
|
|
|
Measure* m = tick2measure(tick);
|
|
|
|
if (m == 0) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("tick2leftSegment(): not found tick %d", tick);
|
2013-06-19 16:25:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// loop over all segments
|
|
|
|
Segment* ps = 0;
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
|
2013-06-19 16:25:29 +02:00
|
|
|
if (tick < s->tick())
|
|
|
|
return ps;
|
|
|
|
else if (tick == s->tick())
|
|
|
|
return s;
|
|
|
|
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
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* Score::tick2rightSegment(int tick) const
|
|
|
|
{
|
|
|
|
Measure* m = tick2measure(tick);
|
|
|
|
if (m == 0) {
|
2014-03-25 13:33:47 +01:00
|
|
|
qDebug("tick2nearestSegment(): not found tick %d", tick);
|
2013-09-03 16:34:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// loop over all segments
|
2014-06-25 11:46:10 +02:00
|
|
|
for (Segment* s = m->first(Segment::Type::ChordRest); s; s = s->next(Segment::Type::ChordRest)) {
|
2013-09-03 16:34:56 +02:00
|
|
|
if (tick <= s->tick())
|
|
|
|
return s;
|
|
|
|
}
|
2013-06-19 16:25:29 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextSeg
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int Score::nextSeg(int tick, int track)
|
|
|
|
{
|
|
|
|
Segment* seg = tick2segment(tick);
|
|
|
|
while (seg) {
|
2014-06-25 11:46:10 +02:00
|
|
|
seg = seg->next1(Segment::Type::ChordRest);
|
2012-05-26 14:26:10 +02:00
|
|
|
if (seg == 0)
|
|
|
|
break;
|
|
|
|
if (seg->element(track))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return seg ? seg->tick() : -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// nextSeg1
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* nextSeg1(Segment* seg, int& track)
|
|
|
|
{
|
|
|
|
int staffIdx = track / VOICES;
|
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
2014-06-25 11:46:10 +02:00
|
|
|
while ((seg = seg->next1(Segment::Type::ChordRest))) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int t = startTrack; t < endTrack; ++t) {
|
|
|
|
if (seg->element(t)) {
|
|
|
|
track = t;
|
|
|
|
return seg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// prevSeg1
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Segment* prevSeg1(Segment* seg, int& track)
|
|
|
|
{
|
|
|
|
int staffIdx = track / VOICES;
|
|
|
|
int startTrack = staffIdx * VOICES;
|
|
|
|
int endTrack = startTrack + VOICES;
|
2014-06-25 11:46:10 +02:00
|
|
|
while ((seg = seg->prev1(Segment::Type::ChordRest))) {
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int t = startTrack; t < endTrack; ++t) {
|
|
|
|
if (seg->element(t)) {
|
|
|
|
track = t;
|
|
|
|
return seg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
//
|
|
|
|
// returns the top note of the next/previous chord. If a chord exists in the same track as note,
|
|
|
|
// 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();
|
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
|
|
|
}
|
|
|
|
seg = seg->nextCR(track, true);
|
|
|
|
}
|
|
|
|
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) {
|
|
|
|
if (seg->segmentType() == Segment::Type::ChordRest) {
|
|
|
|
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();
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
seg = seg->prev1();
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2012-05-26 14:26:10 +02:00
|
|
|
//---------------------------------------------------------
|
|
|
|
// pitchKeyAdjust
|
|
|
|
// change entered note to sounding pitch dependend
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
int pitch = pitchKeyAdjust(l, key) + octave * 12;
|
|
|
|
|
|
|
|
if (pitch > 127)
|
|
|
|
pitch = 127;
|
|
|
|
else if (pitch < 0)
|
|
|
|
pitch = 0;
|
|
|
|
return pitch;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// quantizeLen
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
int quantizeLen(int len, int raster)
|
|
|
|
{
|
2012-08-16 21:40:23 +02:00
|
|
|
if (raster == 0)
|
|
|
|
return len;
|
|
|
|
return int( ((float)len/raster) + 0.5 ) * raster; //round to the closest multiple of raster
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// selectNoteMessage
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void selectNoteMessage()
|
|
|
|
{
|
2014-02-28 12:49:24 +01:00
|
|
|
if (!MScore::noGui)
|
|
|
|
QMessageBox::information(0,
|
2014-03-06 11:32:09 +01:00
|
|
|
QMessageBox::tr("MuseScore"),
|
2014-02-28 12:49:24 +01:00
|
|
|
QMessageBox::tr("No note selected:\n"
|
2014-08-22 16:25:50 +02:00
|
|
|
"Please select a single note and retry operation\n"),
|
2014-02-28 12:49:24 +01:00
|
|
|
QMessageBox::Ok, QMessageBox::NoButton);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void selectNoteRestMessage()
|
|
|
|
{
|
2014-02-28 12:49:24 +01:00
|
|
|
if (!MScore::noGui)
|
|
|
|
QMessageBox::information(0,
|
2014-03-06 11:32:09 +01:00
|
|
|
QMessageBox::tr("MuseScore"),
|
2014-02-28 12:49:24 +01:00
|
|
|
QMessageBox::tr("No note or rest selected:\n"
|
2014-08-22 16:25:50 +02:00
|
|
|
"Please select a single note or rest and retry operation\n"),
|
2014-02-28 12:49:24 +01:00
|
|
|
QMessageBox::Ok, QMessageBox::NoButton);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void selectNoteSlurMessage()
|
|
|
|
{
|
2014-02-28 12:49:24 +01:00
|
|
|
if (!MScore::noGui)
|
|
|
|
QMessageBox::information(0,
|
2014-03-06 11:32:09 +01:00
|
|
|
QMessageBox::tr("MuseScore"),
|
2014-02-28 12:49:24 +01:00
|
|
|
QMessageBox::tr("Please select a single note or slur and retry operation\n"),
|
|
|
|
QMessageBox::Ok, QMessageBox::NoButton);
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void selectStavesMessage()
|
|
|
|
{
|
2014-02-28 12:49:24 +01:00
|
|
|
if (!MScore::noGui)
|
|
|
|
QMessageBox::information(0,
|
2014-03-06 11:32:09 +01:00
|
|
|
QMessageBox::tr("MuseScore"),
|
2014-02-28 12:49:24 +01:00
|
|
|
QMessageBox::tr("Please select one or more staves and retry operation\n"),
|
|
|
|
QMessageBox::Ok, QMessageBox::NoButton);
|
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"),
|
|
|
|
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[] = {
|
2014-02-11 13:30:18 +01:00
|
|
|
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("----");
|
|
|
|
int octave = (v / 12) - 2;
|
2014-08-16 16:53:10 +02:00
|
|
|
QString o;
|
|
|
|
o.sprintf("%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.
|
|
|
|
*/
|
|
|
|
Interval intervalList[26] = {
|
|
|
|
// diatonic - chromatic
|
|
|
|
Interval(0, 0), // 0 Perfect Unison
|
|
|
|
Interval(0, 1), // 1 Augmented Unison
|
|
|
|
|
|
|
|
Interval(1, 0), // 2 Diminished Second
|
|
|
|
Interval(1, 1), // 3 Minor Second
|
|
|
|
Interval(1, 2), // 4 Major Second
|
|
|
|
Interval(1, 3), // 5 Augmented Second
|
|
|
|
|
|
|
|
Interval(2, 2), // 6 Diminished Third
|
|
|
|
Interval(2, 3), // 7 Minor Third
|
|
|
|
Interval(2, 4), // 8 Major Third
|
|
|
|
Interval(2, 5), // 9 Augmented Third
|
|
|
|
|
|
|
|
Interval(3, 4), // 10 Diminished Fourth
|
|
|
|
Interval(3, 5), // 11 Perfect Fourth
|
|
|
|
Interval(3, 6), // 12 Augmented Fourth
|
|
|
|
|
|
|
|
Interval(4, 6), // 13 Diminished Fifth
|
|
|
|
Interval(4, 7), // 14 Perfect Fifth
|
|
|
|
Interval(4, 8), // 15 Augmented Fifth
|
|
|
|
|
|
|
|
Interval(5, 7), // 16 Diminished Sixth
|
|
|
|
Interval(5, 8), // 17 Minor Sixth
|
|
|
|
Interval(5, 9), // 18 Major Sixth
|
|
|
|
Interval(5, 10), // 19 Augmented Sixth
|
|
|
|
|
|
|
|
Interval(6, 9), // 20 Diminished Seventh
|
|
|
|
Interval(6, 10), // 21 Minor Seventh
|
|
|
|
Interval(6, 11), // 22 Major Seventh
|
|
|
|
Interval(6, 12), // 23 Augmented Seventh
|
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// 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
|
|
|
|
|
|
|
|
{ 0, 2, 4, 5, 7, 9, 11 }, // C C
|
|
|
|
|
|
|
|
{ 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
|
|
|
};
|
|
|
|
|
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;
|
|
|
|
|
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)
|
|
|
|
break;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
|
|
|
|
// neither step nor gap found
|
|
|
|
// reset to beginning
|
|
|
|
if (i == 7) {
|
|
|
|
++octave;
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
i = 0;
|
2012-05-26 14:26:10 +02:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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;
|
|
|
|
if (pitch > 127)
|
|
|
|
pitch = 128;
|
|
|
|
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;
|
|
|
|
|
2014-11-29 08:27:07 +01:00
|
|
|
if (chord->isGraceBefore()) {
|
2015-02-05 08:20:57 +01:00
|
|
|
// grace before
|
|
|
|
// try to tie to note in parent chord
|
2016-06-10 10:30:34 +02:00
|
|
|
chord = toChord(chord->parent());
|
2014-11-29 08:27:07 +01:00
|
|
|
note2 = chord->findNote(note->pitch());
|
2015-02-05 08:20:57 +01:00
|
|
|
if (note2)
|
|
|
|
return note2;
|
2014-11-29 08:27:07 +01: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());
|
2014-11-29 08:27:07 +01:00
|
|
|
}
|
2015-02-05 08:20:57 +01: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()) {
|
2015-02-05 08:20:57 +01:00
|
|
|
Chord* gc = gna[0];
|
|
|
|
note2 = gc->findNote(note->pitch());
|
|
|
|
if (note2)
|
|
|
|
return note2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 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)
|
2014-11-29 08:27:07 +01:00
|
|
|
|
2015-02-05 08:20:57 +01:00
|
|
|
// calculate end of current note duration
|
2015-01-18 18:07:45 +01:00
|
|
|
// but err on the safe side in case there is roundoff in tick count
|
|
|
|
int endTick = chord->tick() + chord->actualTicks() - 1;
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
while ((seg = seg->next1(Segment::Type::ChordRest))) {
|
2015-02-05 08:20:57 +01:00
|
|
|
// 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()))
|
|
|
|
continue;
|
2012-05-26 14:26:10 +02:00
|
|
|
for (int track = strack; track < etrack; ++track) {
|
2016-06-10 10:30:34 +02:00
|
|
|
Chord* c = toChord(seg->element(track));
|
|
|
|
if (c == 0 || !c->isChord())
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2015-02-05 08:20:57 +01: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;
|
2015-02-05 08:20:57 +01:00
|
|
|
}
|
2013-06-28 10:55:25 +02:00
|
|
|
int staffIdx = c->staffIdx() + c->staffMove();
|
2012-07-04 19:12:18 +02:00
|
|
|
if (staffIdx != chord->staffIdx() + chord->staffMove()) // cannot happen?
|
2012-05-26 14:26:10 +02:00
|
|
|
continue;
|
2013-06-28 10:55:25 +02:00
|
|
|
for (Note* n : c->notes()) {
|
2012-05-26 14:26:10 +02:00
|
|
|
if (n->pitch() == note->pitch()) {
|
2013-06-28 10:55:25 +02:00
|
|
|
if (note2 == 0 || c->track() == chord->track())
|
2013-08-21 11:59:41 +02:00
|
|
|
note2 = n;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (note2)
|
|
|
|
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();
|
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;
|
|
|
|
|
2014-06-25 11:46:10 +02:00
|
|
|
while ((seg = seg->next1(Segment::Type::ChordRest))) {
|
2013-08-21 11:59:41 +02:00
|
|
|
for (int track = strack; track < etrack; ++track) {
|
2016-06-10 10:30:34 +02:00
|
|
|
Chord* c = toChord(seg->element(track));
|
|
|
|
if (c == 0 || (!c->isChord()) || (c->track() != chord->track()))
|
2013-08-21 11:59:41 +02:00
|
|
|
continue;
|
|
|
|
int staffIdx = c->staffIdx() + c->staffMove();
|
|
|
|
if (staffIdx != chord->staffIdx() + chord->staffMove()) // cannot happen?
|
|
|
|
continue;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (note2)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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);
|
2014-04-10 13:13:37 +02:00
|
|
|
|
2012-08-07 16:05:37 +02:00
|
|
|
if (tpcPitch < 0)
|
|
|
|
line += 7;
|
|
|
|
else
|
|
|
|
line -= (tpcPitch / 12) * 7;
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2013-05-13 18:49:17 +02:00
|
|
|
}
|
|
|
|
|