MuseScore/libmscore/note.cpp

2958 lines
117 KiB
C++
Raw Normal View History

2012-05-26 14:26:10 +02:00
//=============================================================================
// MuseScore
// Music Composition & Notation
//
2012-11-19 09:29:46 +01:00
// Copyright (C) 2002-2012 Werner Schweer
2012-05-26 14:26:10 +02:00
//
// 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
//=============================================================================
2015-02-19 10:28:25 +01:00
2012-05-26 14:26:10 +02:00
/**
\file
Implementation of classes Note and ShadowNote.
*/
#include <assert.h>
#include "note.h"
#include "score.h"
#include "chord.h"
#include "sym.h"
#include "xml.h"
#include "slur.h"
2013-08-22 12:18:14 +02:00
#include "tie.h"
2012-05-26 14:26:10 +02:00
#include "text.h"
#include "clef.h"
#include "staff.h"
#include "pitchspelling.h"
#include "arpeggio.h"
#include "tremolo.h"
#include "utils.h"
#include "image.h"
#include "system.h"
#include "tuplet.h"
#include "articulation.h"
#include "drumset.h"
#include "segment.h"
#include "measure.h"
#include "undo.h"
#include "part.h"
#include "stafftype.h"
#include "stringdata.h"
2012-05-26 14:26:10 +02:00
#include "fret.h"
#include "harmony.h"
#include "fingering.h"
#include "bend.h"
#include "accidental.h"
#include "page.h"
#include "icon.h"
#include "notedot.h"
2012-09-12 16:19:03 +02:00
#include "spanner.h"
#include "glissando.h"
2013-08-01 22:24:36 +02:00
#include "bagpembell.h"
#include "hairpin.h"
#include "textline.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
//---------------------------------------------------------
// noteHeads
// notehead groups
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
static const SymId noteHeads[2][int(NoteHead::Group::HEAD_GROUPS)][int(NoteHead::Type::HEAD_TYPES)] = {
{ // down stem
2016-09-30 18:07:19 +02:00
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWhole },
{ SymId::noteheadXWhole, SymId::noteheadXHalf, SymId::noteheadXBlack, SymId::noteheadXDoubleWhole },
{ SymId::noteheadPlusWhole, SymId::noteheadPlusHalf, SymId::noteheadPlusBlack, SymId::noteheadPlusDoubleWhole },
2016-09-30 18:07:19 +02:00
{ SymId::noteheadCircleXWhole, SymId::noteheadCircleXHalf, SymId::noteheadCircleX, SymId::noteheadCircleXDoubleWhole},
{ SymId::noteheadWholeWithX, SymId::noteheadHalfWithX, SymId::noteheadVoidWithX, SymId::noteheadDoubleWholeWithX},
{ SymId::noteheadTriangleUpWhole, SymId::noteheadTriangleUpHalf, SymId::noteheadTriangleUpBlack, SymId::noteheadTriangleUpDoubleWhole },
{ SymId::noteheadTriangleDownWhole, SymId::noteheadTriangleDownHalf, SymId::noteheadTriangleDownBlack, SymId::noteheadTriangleDownDoubleWhole },
{ SymId::noteheadSlashedWhole1, SymId::noteheadSlashedHalf1, SymId::noteheadSlashedBlack1, SymId::noteheadSlashedDoubleWhole1 },
{ SymId::noteheadSlashedWhole2, SymId::noteheadSlashedHalf2, SymId::noteheadSlashedBlack2, SymId::noteheadSlashedDoubleWhole2 },
{ SymId::noteheadDiamondWhole, SymId::noteheadDiamondHalf, SymId::noteheadDiamondBlack, SymId::noteheadDiamondDoubleWhole },
{ SymId::noteheadDiamondWholeOld, SymId::noteheadDiamondHalfOld, SymId::noteheadDiamondBlackOld, SymId::noteheadDiamondDoubleWholeOld },
{ SymId::noteheadCircledWhole, SymId::noteheadCircledHalf, SymId::noteheadCircledBlack, SymId::noteheadCircledDoubleWhole },
{ SymId::noteheadCircledWholeLarge, SymId::noteheadCircledHalfLarge, SymId::noteheadCircledBlackLarge, SymId::noteheadCircledDoubleWholeLarge },
{ SymId::noteheadLargeArrowUpWhole, SymId::noteheadLargeArrowUpHalf, SymId::noteheadLargeArrowUpBlack, SymId::noteheadLargeArrowUpDoubleWhole },
2016-09-30 18:07:19 +02:00
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWholeSquare },
2016-11-19 11:51:21 +01:00
{ SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds, SymId::noteheadSlashWhiteWhole},
2016-11-19 11:51:21 +01:00
{ SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack, SymId::noteShapeRoundDoubleWhole },
{ SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack, SymId::noteShapeSquareDoubleWhole },
{ SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightWhite, SymId::noteShapeTriangleRightBlack, SymId::noteShapeTriangleRightDoubleWhole },
{ SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack, SymId::noteShapeDiamondDoubleWhole },
{ SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack, SymId::noteShapeTriangleUpDoubleWhole },
{ SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack, SymId::noteShapeMoonDoubleWhole },
{ SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack, SymId::noteShapeTriangleRoundDoubleWhole },
2016-11-19 11:51:21 +01:00
{ SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneBlack, SymId::noteShapeKeystoneDoubleWhole },
{ SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonBlack, SymId::noteShapeQuarterMoonDoubleWhole },
{ SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleBlack, SymId::noteShapeIsoscelesTriangleDoubleWhole },
{ SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftBlack, SymId::noteShapeMoonLeftDoubleWhole },
{ SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftBlack, SymId::noteShapeArrowheadLeftDoubleWhole },
{ SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftBlack, SymId::noteShapeTriangleRoundLeftDoubleWhole },
2016-11-19 11:51:21 +01:00
{ SymId::noteDoWhole, SymId::noteDoHalf, SymId::noteDoBlack, SymId::noSym },
{ SymId::noteReWhole, SymId::noteReHalf, SymId::noteReBlack, SymId::noSym },
{ SymId::noteMiWhole, SymId::noteMiHalf, SymId::noteMiBlack, SymId::noSym },
{ SymId::noteFaWhole, SymId::noteFaHalf, SymId::noteFaBlack, SymId::noSym },
{ SymId::noteSoWhole, SymId::noteSoHalf, SymId::noteSoBlack, SymId::noSym },
{ SymId::noteLaWhole, SymId::noteLaHalf, SymId::noteLaBlack, SymId::noSym },
{ SymId::noteTiWhole, SymId::noteTiHalf, SymId::noteTiBlack, SymId::noSym },
{ SymId::noteSiWhole, SymId::noteSiHalf, SymId::noteSiBlack, SymId::noSym },
2016-11-19 11:51:21 +01:00
{ SymId::noteASharpWhole, SymId::noteASharpHalf, SymId::noteASharpBlack, SymId::noSym },
{ SymId::noteAWhole, SymId::noteAHalf, SymId::noteABlack, SymId::noSym },
{ SymId::noteAFlatWhole, SymId::noteAFlatHalf, SymId::noteAFlatBlack, SymId::noSym },
{ SymId::noteBSharpWhole, SymId::noteBSharpHalf, SymId::noteBSharpBlack, SymId::noSym },
{ SymId::noteBWhole, SymId::noteBHalf, SymId::noteBBlack, SymId::noSym },
{ SymId::noteBFlatWhole, SymId::noteBFlatHalf, SymId::noteBFlatBlack, SymId::noSym },
{ SymId::noteCSharpWhole, SymId::noteCSharpHalf, SymId::noteCSharpBlack, SymId::noSym },
{ SymId::noteCWhole, SymId::noteCHalf, SymId::noteCBlack, SymId::noSym },
{ SymId::noteCFlatWhole, SymId::noteCFlatHalf, SymId::noteCFlatBlack, SymId::noSym },
{ SymId::noteDSharpWhole, SymId::noteDSharpHalf, SymId::noteDSharpBlack, SymId::noSym },
{ SymId::noteDWhole, SymId::noteDHalf, SymId::noteDBlack, SymId::noSym },
{ SymId::noteDFlatWhole, SymId::noteDFlatHalf, SymId::noteDFlatBlack, SymId::noSym },
{ SymId::noteESharpWhole, SymId::noteESharpHalf, SymId::noteESharpBlack, SymId::noSym },
{ SymId::noteEWhole, SymId::noteEHalf, SymId::noteEBlack, SymId::noSym },
{ SymId::noteEFlatWhole, SymId::noteEFlatHalf, SymId::noteEFlatBlack, SymId::noSym },
{ SymId::noteFSharpWhole, SymId::noteFSharpHalf, SymId::noteFSharpBlack, SymId::noSym },
{ SymId::noteFWhole, SymId::noteFHalf, SymId::noteFBlack, SymId::noSym },
{ SymId::noteFFlatWhole, SymId::noteFFlatHalf, SymId::noteFFlatBlack, SymId::noSym },
{ SymId::noteGSharpWhole, SymId::noteGSharpHalf, SymId::noteGSharpBlack, SymId::noSym },
{ SymId::noteGWhole, SymId::noteGHalf, SymId::noteGBlack, SymId::noSym },
{ SymId::noteGFlatWhole, SymId::noteGFlatHalf, SymId::noteGFlatBlack, SymId::noSym },
{ SymId::noteHWhole, SymId::noteHHalf, SymId::noteHBlack, SymId::noSym },
{ SymId::noteHSharpWhole, SymId::noteHSharpHalf, SymId::noteHSharpBlack, SymId::noSym }
},
{ // up stem
2016-09-30 18:07:19 +02:00
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWhole },
{ SymId::noteheadXWhole, SymId::noteheadXHalf, SymId::noteheadXBlack, SymId::noteheadXDoubleWhole },
{ SymId::noteheadPlusWhole, SymId::noteheadPlusHalf, SymId::noteheadPlusBlack, SymId::noteheadPlusDoubleWhole },
2016-09-30 18:07:19 +02:00
{ SymId::noteheadCircleXWhole, SymId::noteheadCircleXHalf, SymId::noteheadCircleX, SymId::noteheadCircleXDoubleWhole},
{ SymId::noteheadWholeWithX, SymId::noteheadHalfWithX, SymId::noteheadVoidWithX, SymId::noteheadDoubleWholeWithX},
{ SymId::noteheadTriangleUpWhole, SymId::noteheadTriangleUpHalf, SymId::noteheadTriangleUpBlack, SymId::noteheadTriangleUpDoubleWhole },
{ SymId::noteheadTriangleDownWhole, SymId::noteheadTriangleDownHalf, SymId::noteheadTriangleDownBlack, SymId::noteheadTriangleDownDoubleWhole },
{ SymId::noteheadSlashedWhole1, SymId::noteheadSlashedHalf1, SymId::noteheadSlashedBlack1, SymId::noteheadSlashedDoubleWhole1 },
{ SymId::noteheadSlashedWhole2, SymId::noteheadSlashedHalf2, SymId::noteheadSlashedBlack2, SymId::noteheadSlashedDoubleWhole2 },
{ SymId::noteheadDiamondWhole, SymId::noteheadDiamondHalf, SymId::noteheadDiamondBlack, SymId::noteheadDiamondDoubleWhole },
{ SymId::noteheadDiamondWholeOld, SymId::noteheadDiamondHalfOld, SymId::noteheadDiamondBlackOld, SymId::noteheadDiamondDoubleWholeOld },
{ SymId::noteheadCircledWhole, SymId::noteheadCircledHalf, SymId::noteheadCircledBlack, SymId::noteheadCircledDoubleWhole },
{ SymId::noteheadCircledWholeLarge, SymId::noteheadCircledHalfLarge, SymId::noteheadCircledBlackLarge, SymId::noteheadCircledDoubleWholeLarge },
// different from down, find source?
{ SymId::noteheadLargeArrowDownWhole, SymId::noteheadLargeArrowDownHalf, SymId::noteheadLargeArrowDownBlack, SymId::noteheadLargeArrowDownDoubleWhole },
2016-09-30 18:07:19 +02:00
{ SymId::noteheadWhole, SymId::noteheadHalf, SymId::noteheadBlack, SymId::noteheadDoubleWholeSquare },
2016-11-19 11:51:21 +01:00
{ SymId::noteheadSlashWhiteWhole, SymId::noteheadSlashWhiteHalf, SymId::noteheadSlashHorizontalEnds, SymId::noteheadSlashWhiteDoubleWhole},
2016-11-19 11:51:21 +01:00
{ SymId::noteShapeRoundWhite, SymId::noteShapeRoundWhite, SymId::noteShapeRoundBlack, SymId::noteShapeRoundDoubleWhole },
{ SymId::noteShapeSquareWhite, SymId::noteShapeSquareWhite, SymId::noteShapeSquareBlack, SymId::noteShapeSquareDoubleWhole },
// different from down
{ SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftWhite, SymId::noteShapeTriangleLeftBlack, SymId::noteShapeTriangleLeftDoubleWhole },
{ SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondWhite, SymId::noteShapeDiamondBlack, SymId::noteShapeDiamondDoubleWhole },
{ SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpWhite, SymId::noteShapeTriangleUpBlack, SymId::noteShapeTriangleUpDoubleWhole },
{ SymId::noteShapeMoonWhite, SymId::noteShapeMoonWhite, SymId::noteShapeMoonBlack, SymId::noteShapeMoonDoubleWhole },
{ SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundWhite, SymId::noteShapeTriangleRoundBlack, SymId::noteShapeTriangleRoundDoubleWhole },
2016-11-19 11:51:21 +01:00
{ SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneWhite, SymId::noteShapeKeystoneBlack, SymId::noteShapeKeystoneDoubleWhole },
{ SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonWhite, SymId::noteShapeQuarterMoonBlack, SymId::noteShapeQuarterMoonDoubleWhole },
{ SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleWhite, SymId::noteShapeIsoscelesTriangleBlack, SymId::noteShapeIsoscelesTriangleDoubleWhole },
{ SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftWhite, SymId::noteShapeMoonLeftBlack, SymId::noteShapeMoonLeftDoubleWhole },
{ SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftWhite, SymId::noteShapeArrowheadLeftBlack, SymId::noteShapeArrowheadLeftDoubleWhole },
{ SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftWhite, SymId::noteShapeTriangleRoundLeftBlack, SymId::noteShapeTriangleRoundLeftDoubleWhole },
2016-11-19 11:51:21 +01:00
{ SymId::noteDoWhole, SymId::noteDoHalf, SymId::noteDoBlack, SymId::noSym },
{ SymId::noteReWhole, SymId::noteReHalf, SymId::noteReBlack, SymId::noSym },
{ SymId::noteMiWhole, SymId::noteMiHalf, SymId::noteMiBlack, SymId::noSym },
{ SymId::noteFaWhole, SymId::noteFaHalf, SymId::noteFaBlack, SymId::noSym },
{ SymId::noteSoWhole, SymId::noteSoHalf, SymId::noteSoBlack, SymId::noSym },
{ SymId::noteLaWhole, SymId::noteLaHalf, SymId::noteLaBlack, SymId::noSym },
{ SymId::noteTiWhole, SymId::noteTiHalf, SymId::noteTiBlack, SymId::noSym },
{ SymId::noteSiWhole, SymId::noteSiHalf, SymId::noteSiBlack, SymId::noSym },
2016-11-19 11:51:21 +01:00
{ SymId::noteASharpWhole, SymId::noteASharpHalf, SymId::noteASharpBlack, SymId::noSym },
{ SymId::noteAWhole, SymId::noteAHalf, SymId::noteABlack, SymId::noSym },
{ SymId::noteAFlatWhole, SymId::noteAFlatHalf, SymId::noteAFlatBlack, SymId::noSym },
{ SymId::noteBSharpWhole, SymId::noteBSharpHalf, SymId::noteBSharpBlack, SymId::noSym },
{ SymId::noteBWhole, SymId::noteBHalf, SymId::noteBBlack, SymId::noSym },
{ SymId::noteBFlatWhole, SymId::noteBFlatHalf, SymId::noteBFlatBlack, SymId::noSym },
{ SymId::noteCSharpWhole, SymId::noteCSharpHalf, SymId::noteCSharpBlack, SymId::noSym },
{ SymId::noteCWhole, SymId::noteCHalf, SymId::noteCBlack, SymId::noSym },
{ SymId::noteCFlatWhole, SymId::noteCFlatHalf, SymId::noteCFlatBlack, SymId::noSym },
{ SymId::noteDSharpWhole, SymId::noteDSharpHalf, SymId::noteDSharpBlack, SymId::noSym },
{ SymId::noteDWhole, SymId::noteDHalf, SymId::noteDBlack, SymId::noSym },
{ SymId::noteDFlatWhole, SymId::noteDFlatHalf, SymId::noteDFlatBlack, SymId::noSym },
{ SymId::noteESharpWhole, SymId::noteESharpHalf, SymId::noteESharpBlack, SymId::noSym },
{ SymId::noteEWhole, SymId::noteEHalf, SymId::noteEBlack, SymId::noSym },
{ SymId::noteEFlatWhole, SymId::noteEFlatHalf, SymId::noteEFlatBlack, SymId::noSym },
{ SymId::noteFSharpWhole, SymId::noteFSharpHalf, SymId::noteFSharpBlack, SymId::noSym },
{ SymId::noteFWhole, SymId::noteFHalf, SymId::noteFBlack, SymId::noSym },
{ SymId::noteFFlatWhole, SymId::noteFFlatHalf, SymId::noteFFlatBlack, SymId::noSym },
{ SymId::noteGSharpWhole, SymId::noteGSharpHalf, SymId::noteGSharpBlack, SymId::noSym },
{ SymId::noteGWhole, SymId::noteGHalf, SymId::noteGBlack, SymId::noSym },
{ SymId::noteGFlatWhole, SymId::noteGFlatHalf, SymId::noteGFlatBlack, SymId::noSym },
{ SymId::noteHWhole, SymId::noteHHalf, SymId::noteHBlack, SymId::noSym },
{ SymId::noteHSharpWhole, SymId::noteHSharpHalf, SymId::noteHSharpBlack, SymId::noSym }
2016-11-19 11:51:21 +01:00
}
};
2012-05-26 14:26:10 +02:00
struct NoteHeadName {
const char* name;
const char* username;
};
// same order as NoteHead::Group
static NoteHeadName noteHeadGroupNames[] = {
{"normal", QT_TRANSLATE_NOOP("noteheadnames", "Normal") },
{"cross", QT_TRANSLATE_NOOP("noteheadnames", "Cross") },
{"plus", QT_TRANSLATE_NOOP("noteheadnames", "Plus") },
{"xcircle", QT_TRANSLATE_NOOP("noteheadnames", "XCircle") },
{"withx", QT_TRANSLATE_NOOP("noteheadnames", "With X") },
{"triangle-up", QT_TRANSLATE_NOOP("noteheadnames", "Triangle Up") },
{"triangle-down", QT_TRANSLATE_NOOP("noteheadnames", "Triangle Down") },
{"slashed1", QT_TRANSLATE_NOOP("noteheadnames", "Slashed (Forwards)") },
{"slashed2", QT_TRANSLATE_NOOP("noteheadnames", "Slashed (Backwards)") },
{"diamond", QT_TRANSLATE_NOOP("noteheadnames", "Diamond") },
{"diamond-old", QT_TRANSLATE_NOOP("noteheadnames", "Diamond (Old)") },
{"circled", QT_TRANSLATE_NOOP("noteheadnames", "Circled") },
{"circled-large", QT_TRANSLATE_NOOP("noteheadnames", "Circled Large") },
{"large-arrow", QT_TRANSLATE_NOOP("noteheadnames", "Large Arrow") },
{"altbrevis", QT_TRANSLATE_NOOP("noteheadnames", "Alt. Brevis") },
2016-11-19 11:51:21 +01:00
{"slash", QT_TRANSLATE_NOOP("noteheadnames", "Slash") },
2016-11-19 11:51:21 +01:00
// shape notes
{"sol", QT_TRANSLATE_NOOP("noteheadnames", "Sol") },
{"la", QT_TRANSLATE_NOOP("noteheadnames", "La") },
{"fa", QT_TRANSLATE_NOOP("noteheadnames", "Fa") },
{"mi", QT_TRANSLATE_NOOP("noteheadnames", "Mi") },
{"do", QT_TRANSLATE_NOOP("noteheadnames", "Do") },
{"re", QT_TRANSLATE_NOOP("noteheadnames", "Re") },
{"ti", QT_TRANSLATE_NOOP("noteheadnames", "Ti") },
2016-11-19 11:51:21 +01:00
// not exposed
{"do-walker", QT_TRANSLATE_NOOP("noteheadnames", "Do (Walker)") },
{"re-walker", QT_TRANSLATE_NOOP("noteheadnames", "Re (Walker)") },
{"ti-walker", QT_TRANSLATE_NOOP("noteheadnames", "Ti (Walker)") },
{"do-funk", QT_TRANSLATE_NOOP("noteheadnames", "Do (Funk)") },
{"re-funk", QT_TRANSLATE_NOOP("noteheadnames", "Re (Funk)") },
{"ti-funk", QT_TRANSLATE_NOOP("noteheadnames", "Ti (Funk)") },
2016-11-19 11:51:21 +01:00
// note name
{"do-name", QT_TRANSLATE_NOOP("noteheadnames", "Do (Name)") },
{"re-name", QT_TRANSLATE_NOOP("noteheadnames", "Re (Name)") },
{"mi-name", QT_TRANSLATE_NOOP("noteheadnames", "Mi (Name)") },
{"fa-name", QT_TRANSLATE_NOOP("noteheadnames", "Fa (Name)") },
{"sol-name", QT_TRANSLATE_NOOP("noteheadnames", "Sol (Name)") },
{"la-name", QT_TRANSLATE_NOOP("noteheadnames", "La (Name)") },
{"ti-name", QT_TRANSLATE_NOOP("noteheadnames", "Ti (Name)") },
{"si-name", QT_TRANSLATE_NOOP("noteheadnames", "Si (Name)") },
2016-11-19 11:51:21 +01:00
{"a-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "A Sharp (Name)") },
{"a-name", QT_TRANSLATE_NOOP("noteheadnames", "A (Name)") },
{"a-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "A Flat (Name)") },
{"b-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "B Sharp (Name)") },
{"b-name", QT_TRANSLATE_NOOP("noteheadnames", "B (Name)") },
{"b-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "B Flat (Name)") },
{"c-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "C Sharp (Name)") },
{"c-name", QT_TRANSLATE_NOOP("noteheadnames", "C (Name)") },
{"c-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "C Flat (Name)") },
{"d-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "D Sharp (Name)") },
{"d-name", QT_TRANSLATE_NOOP("noteheadnames", "D (Name)") },
{"d-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "D Flat (Name)") },
{"e-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "E Sharp (Name)") },
{"e-name", QT_TRANSLATE_NOOP("noteheadnames", "E (Name)") },
{"e-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "E Flat (Name)") },
{"f-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "F Sharp (Name)") },
{"f-name", QT_TRANSLATE_NOOP("noteheadnames", "F (Name)") },
{"f-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "F Flat (Name)") },
{"g-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "G Sharp (Name)") },
{"g-name", QT_TRANSLATE_NOOP("noteheadnames", "G (Name)") },
{"g-flat-name", QT_TRANSLATE_NOOP("noteheadnames", "G Flat (Name)") },
{"h-name", QT_TRANSLATE_NOOP("noteheadnames", "H (Name)") },
{"h-sharp-name", QT_TRANSLATE_NOOP("noteheadnames", "H Sharp (Name)") }
};
// same order as NoteHead::Type
static NoteHeadName noteHeadTypeNames[] = {
{"auto", QT_TRANSLATE_NOOP("noteheadnames", "Auto") },
{"whole", QT_TRANSLATE_NOOP("noteheadnames", "Whole") },
{"half", QT_TRANSLATE_NOOP("noteheadnames", "Half") },
{"quarter", QT_TRANSLATE_NOOP("noteheadnames", "Quarter") },
{"breve", QT_TRANSLATE_NOOP("noteheadnames", "Breve") },
2016-09-30 18:07:19 +02:00
};
//---------------------------------------------------------
// group2userName
//---------------------------------------------------------
QString NoteHead::group2userName(NoteHead::Group group)
{
return qApp->translate("noteheadnames", noteHeadGroupNames[int(group)].username);
}
//---------------------------------------------------------
// type2userName
//---------------------------------------------------------
QString NoteHead::type2userName(NoteHead::Type type)
{
return qApp->translate("noteheadnames", noteHeadTypeNames[int(type) + 1].username);
}
//---------------------------------------------------------
// group2name
//---------------------------------------------------------
QString NoteHead::group2name(NoteHead::Group group)
{
return noteHeadGroupNames[int(group)].name;
}
//---------------------------------------------------------
// type2name
//---------------------------------------------------------
QString NoteHead::type2name(NoteHead::Type type)
{
return noteHeadTypeNames[int(type) + 1].name;
}
//---------------------------------------------------------
// name2group
//---------------------------------------------------------
NoteHead::Group NoteHead::name2group(QString s)
{
for (int i = 0; i < int(NoteHead::Group::HEAD_GROUPS); ++i) {
if (noteHeadGroupNames[i].name == s)
return NoteHead::Group(i);
}
return NoteHead::Group::HEAD_NORMAL;
}
//---------------------------------------------------------
// name2type
//---------------------------------------------------------
NoteHead::Type NoteHead::name2type(QString s)
{
for (int i = 0; i <= int(NoteHead::Type::HEAD_TYPES); ++i) {
if (noteHeadTypeNames[i].name == s)
return NoteHead::Type(i - 1);
}
return NoteHead::Type::HEAD_AUTO;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-11-25 15:42:40 +01:00
// noteHead
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
SymId Note::noteHead(int direction, NoteHead::Group group, NoteHead::Type t)
{
return noteHeads[direction][int(group)][int(t)];
}
SymId Note::noteHead(int direction, NoteHead::Group group, NoteHead::Type t, int tpc, Key key, NoteHeadScheme scheme)
2013-11-25 15:42:40 +01:00
{
// shortcut
if (scheme == NoteHeadScheme::HEAD_NORMAL)
return noteHeads[direction][int(group)][int(t)];
// other schemes
if (scheme == NoteHeadScheme::HEAD_PITCHNAME || scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN) {
if (tpc == Tpc::TPC_A)
group = NoteHead::Group::HEAD_A;
else if (tpc == Tpc::TPC_B) {
if (scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN)
group = NoteHead::Group::HEAD_H;
else
group = NoteHead::Group::HEAD_B;
}
else if (tpc == Tpc::TPC_C)
group = NoteHead::Group::HEAD_C;
else if (tpc == Tpc::TPC_D)
group = NoteHead::Group::HEAD_D;
else if (tpc == Tpc::TPC_E)
group = NoteHead::Group::HEAD_E;
else if (tpc == Tpc::TPC_F)
group = NoteHead::Group::HEAD_F;
else if (tpc == Tpc::TPC_G)
group = NoteHead::Group::HEAD_G;
else if (tpc == Tpc::TPC_A_S)
group = NoteHead::Group::HEAD_A_SHARP;
else if (tpc == Tpc::TPC_B_S)
if (scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN)
group = NoteHead::Group::HEAD_H_SHARP;
else
group = NoteHead::Group::HEAD_B_SHARP;
else if (tpc == Tpc::TPC_C_S)
group = NoteHead::Group::HEAD_C_SHARP;
else if (tpc == Tpc::TPC_D_S)
group = NoteHead::Group::HEAD_D_SHARP;
else if (tpc == Tpc::TPC_E_S)
group = NoteHead::Group::HEAD_E_SHARP;
else if (tpc == Tpc::TPC_F_S)
group = NoteHead::Group::HEAD_F_SHARP;
else if (tpc == Tpc::TPC_G_S)
group = NoteHead::Group::HEAD_G_SHARP;
else if (tpc == Tpc::TPC_A_B)
group = NoteHead::Group::HEAD_A_FLAT;
else if (tpc == Tpc::TPC_B_B)
if (scheme == NoteHeadScheme::HEAD_PITCHNAME_GERMAN)
group = NoteHead::Group::HEAD_B;
else
group = NoteHead::Group::HEAD_B_FLAT;
else if (tpc == Tpc::TPC_C_B)
group = NoteHead::Group::HEAD_C_FLAT;
else if (tpc == Tpc::TPC_D_B)
group = NoteHead::Group::HEAD_D_FLAT;
else if (tpc == Tpc::TPC_E_B)
group = NoteHead::Group::HEAD_E_FLAT;
else if (tpc == Tpc::TPC_F_B)
group = NoteHead::Group::HEAD_F_FLAT;
else if (tpc == Tpc::TPC_G_B)
group = NoteHead::Group::HEAD_G_FLAT;
}
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_4) {
int degree = tpc2degree(tpc, key);
switch (degree) {
case 0:
case 3:
group = NoteHead::Group::HEAD_FA; break;
case 1:
case 4:
group = NoteHead::Group::HEAD_SOL; break;
case 2:
case 5:
group = NoteHead::Group::HEAD_LA; break;
case 6:
group = NoteHead::Group::HEAD_MI; break;
}
}
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN
|| scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK
|| scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER) {
int degree = tpc2degree(tpc, key);
switch (degree) {
case 0:
if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN)
group = NoteHead::Group::HEAD_DO;
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK)
group = NoteHead::Group::HEAD_DO_FUNK;
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER)
group = NoteHead::Group::HEAD_DO_WALKER;
break;
case 1:
if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN)
group = NoteHead::Group::HEAD_RE;
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK)
group = NoteHead::Group::HEAD_RE_FUNK;
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER)
group = NoteHead::Group::HEAD_RE_WALKER;
break;
case 2:
group = NoteHead::Group::HEAD_MI; break;
case 3:
group = NoteHead::Group::HEAD_FA; break;
case 4:
group = NoteHead::Group::HEAD_SOL; break;
case 5:
group = NoteHead::Group::HEAD_LA; break;
case 6:
if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_AIKIN)
group = NoteHead::Group::HEAD_TI;
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_FUNK)
group = NoteHead::Group::HEAD_TI_FUNK;
else if (scheme == NoteHeadScheme::HEAD_SHAPE_NOTE_7_WALKER)
group = NoteHead::Group::HEAD_TI_WALKER;
break;
}
}
else if (scheme == NoteHeadScheme::HEAD_SOLFEGE) {
int degree = tpc2degree(tpc, key);
switch (degree) {
case 0:
group = NoteHead::Group::HEAD_DO_NAME; break;
case 1:
group = NoteHead::Group::HEAD_RE_NAME; break;
case 2:
group = NoteHead::Group::HEAD_MI_NAME; break;
case 3:
group = NoteHead::Group::HEAD_FA_NAME; break;
case 4:
group = NoteHead::Group::HEAD_SOL_NAME; break;
case 5:
group = NoteHead::Group::HEAD_LA_NAME; break;
case 6:
group = NoteHead::Group::HEAD_TI_NAME; break;
}
}
else if (scheme == NoteHeadScheme::HEAD_SOLFEGE_FIXED) {
QString stepName = tpc2stepName(tpc);
if (stepName == "C")
group = NoteHead::Group::HEAD_DO_NAME;
else if (stepName == "D")
group = NoteHead::Group::HEAD_RE_NAME;
else if (stepName == "E")
group = NoteHead::Group::HEAD_MI_NAME;
else if (stepName == "F")
group = NoteHead::Group::HEAD_FA_NAME;
else if (stepName == "G")
group = NoteHead::Group::HEAD_SOL_NAME;
else if (stepName == "A")
group = NoteHead::Group::HEAD_LA_NAME;
else if (stepName == "B")
group = NoteHead::Group::HEAD_SI_NAME;
}
return noteHeads[direction][int(group)][int(t)];
2013-11-25 15:42:40 +01:00
};
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// headGroup
// used only when dropping a notehead from the palette
// they are either half note, either double whole
//---------------------------------------------------------
NoteHead::Group NoteHead::headGroup() const
{
Group group = Group::HEAD_INVALID;
for (int i = 0; i < int(Group::HEAD_DO_WALKER); ++i) {
if (noteHeads[0][i][1] == _sym || noteHeads[0][i][3] == _sym) {
group = (Group)i;
break;
}
}
return group;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// Note
//---------------------------------------------------------
Note::Note(Score* s)
: Element(s)
{
setFlags(ElementFlag::MOVABLE | ElementFlag::SELECTABLE);
2012-11-19 09:29:46 +01:00
_playEvents.append(NoteEvent()); // add default play event
_cachedNoteheadSym = SymId::noSym;
_cachedSymNull = SymId::noSym;
2012-05-26 14:26:10 +02:00
}
Note::~Note()
{
delete _accidental;
2012-11-19 09:29:46 +01:00
qDeleteAll(_el);
2012-05-26 14:26:10 +02:00
delete _tieFor;
2015-02-19 10:28:25 +01:00
qDeleteAll(_dots);
2012-05-26 14:26:10 +02:00
}
2014-07-10 18:59:44 +02:00
Note::Note(const Note& n, bool link)
2012-05-26 14:26:10 +02:00
: Element(n)
{
2014-07-10 18:59:44 +02:00
if (link)
score()->undo(new Link(const_cast<Note*>(&n), this));
2012-05-26 14:26:10 +02:00
_subchannel = n._subchannel;
_line = n._line;
_fret = n._fret;
_string = n._string;
_fretConflict = n._fretConflict;
_ghost = n._ghost;
dragMode = n.dragMode;
_pitch = n._pitch;
_tpc[0] = n._tpc[0];
_tpc[1] = n._tpc[1];
2014-08-13 16:36:09 +02:00
_dotsHidden = n._dotsHidden;
2012-05-26 14:26:10 +02:00
_hidden = n._hidden;
_play = n._play;
2012-05-26 14:26:10 +02:00
_tuning = n._tuning;
_veloType = n._veloType;
_veloOffset = n._veloOffset;
_headGroup = n._headGroup;
_headType = n._headType;
_mirror = n._mirror;
_userMirror = n._userMirror;
_small = n._small;
2014-04-02 20:31:37 +02:00
_userDotPosition = n._userDotPosition;
_fixed = n._fixed;
_fixedLine = n._fixedLine;
2012-05-26 14:26:10 +02:00
_accidental = 0;
2012-05-26 14:26:10 +02:00
if (n._accidental)
add(new Accidental(*(n._accidental)));
2014-07-10 18:59:44 +02:00
// types in _el: SYMBOL, IMAGE, FINGERING, TEXT, BEND
for (Element* e : n._el) {
if (e->isFingering() && staff()->isTabStaff(tick())) // tablature has no fingering
continue;
2014-07-10 18:59:44 +02:00
Element* ce = e->clone();
add(ce);
if (link)
score()->undo(new Link(const_cast<Element*>(e), ce));
2014-07-10 18:59:44 +02:00
}
2013-03-25 16:27:20 +01:00
2012-11-19 09:29:46 +01:00
_playEvents = n._playEvents;
2012-05-26 14:26:10 +02:00
if (n._tieFor) {
_tieFor = new Tie(*n._tieFor);
_tieFor->setStartNote(this);
_tieFor->setEndNote(0);
}
else
_tieFor = 0;
_tieBack = 0;
2016-01-04 14:48:58 +01:00
for (NoteDot* dot : n._dots)
add(new NoteDot(*dot));
2012-05-26 14:26:10 +02:00
_lineOffset = n._lineOffset;
2013-02-20 17:53:15 +01:00
_mark = n._mark;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// concertPitchIdx
//---------------------------------------------------------
inline int Note::concertPitchIdx() const
{
return concertPitch() ? 0 : 1;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setPitch
//---------------------------------------------------------
void Note::setPitch(int val)
{
2014-04-09 16:09:21 +02:00
Q_ASSERT(val >= 0 && val <= 127);
if (_pitch != val) {
_pitch = val;
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-05-26 14:26:10 +02:00
}
}
void Note::setPitch(int pitch, int tpc1, int tpc2)
2012-05-26 14:26:10 +02:00
{
2014-04-09 11:11:02 +02:00
Q_ASSERT(tpcIsValid(tpc1));
Q_ASSERT(tpcIsValid(tpc2));
_tpc[0] = tpc1;
_tpc[1] = tpc2;
2014-04-09 11:11:02 +02:00
setPitch(pitch);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// undoSetPitch
//---------------------------------------------------------
void Note::undoSetPitch(int p)
{
2016-06-09 09:26:13 +02:00
undoChangeProperty(P_ID::PITCH, p);
}
2014-04-10 13:13:37 +02:00
//---------------------------------------------------------
// tpc1default
//---------------------------------------------------------
int Note::tpc1default(int p) const
{
2014-06-20 17:07:22 +02:00
Key key = Key::C;
2014-04-10 13:13:37 +02:00
if (staff() && chord()) {
int tick = chord()->tick();
key = staff()->key(tick);
2014-04-10 13:13:37 +02:00
if (!concertPitch()) {
Interval interval = part()->instrument(tick)->transpose();
2014-04-10 13:13:37 +02:00
if (!interval.isZero()) {
interval.flip();
key = transposeKey(key, interval);
}
}
}
return pitch2tpc(p, key, Prefer::NEAREST);
2014-04-10 13:13:37 +02:00
}
//---------------------------------------------------------
// tpc2default
//---------------------------------------------------------
int Note::tpc2default(int p) const
{
2014-06-20 17:07:22 +02:00
Key key = Key::C;
2014-04-10 13:13:37 +02:00
if (staff() && chord()) {
int tick = chord()->tick();
key = staff()->key(tick);
2014-04-10 13:13:37 +02:00
if (concertPitch()) {
Interval interval = part()->instrument(tick)->transpose();
2014-04-10 13:13:37 +02:00
if (!interval.isZero())
key = transposeKey(key, interval);
}
}
return pitch2tpc(p - transposition(), key, Prefer::NEAREST);
2014-04-10 13:13:37 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setTpcFromPitch
//---------------------------------------------------------
void Note::setTpcFromPitch()
{
2015-01-17 23:32:14 +01:00
// works best if note is already added to score, otherwise we can't determine transposition or key
int tick = chord() ? chord()->tick() : -1;
Interval v = staff() ? part()->instrument(tick)->transpose() : Interval();
2014-06-20 17:07:22 +02:00
Key key = (staff() && chord()) ? staff()->key(chord()->tick()) : Key::C;
// convert key to concert pitch
if (!concertPitch() && !v.isZero())
key = transposeKey(key, v);
// set concert pitch tpc
_tpc[0] = pitch2tpc(_pitch, key, Prefer::NEAREST);
// set transposed tpc
if (v.isZero())
_tpc[1] = _tpc[0];
else {
v.flip();
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
}
2014-04-09 11:11:02 +02:00
Q_ASSERT(tpcIsValid(_tpc[0]));
Q_ASSERT(tpcIsValid(_tpc[1]));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setTpc
//---------------------------------------------------------
void Note::setTpc(int v)
{
2014-04-02 18:11:56 +02:00
if (!tpcIsValid(v))
qFatal("Note::setTpc: bad tpc %d", v);
_tpc[concertPitchIdx()] = v;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// undoSetTpc
// change the current tpc
//---------------------------------------------------------
void Note::undoSetTpc(int v)
{
if (concertPitch()) {
if (v != tpc1())
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::TPC1, v);
}
else {
if (v != tpc2())
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::TPC2, v);
}
}
//---------------------------------------------------------
// tpc
//---------------------------------------------------------
int Note::tpc() const
{
return _tpc[concertPitchIdx()];
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// tpcUserName
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Note::tpcUserName(bool explicitAccidental) const
{
QString pitchName = tpc2name(tpc(), NoteSpellingType::STANDARD, NoteCaseType::AUTO, explicitAccidental);
2014-10-13 23:32:53 +02:00
QString octaveName = QString::number((pitch() / 12) - 1);
return pitchName + (explicitAccidental ? " " : "") + octaveName;
}
2014-04-23 11:08:51 +02:00
//---------------------------------------------------------
// transposeTpc
// return transposed tpc
// If in concertPitch mode return tpc for transposed view
// else return tpc for concert pitch view.
//---------------------------------------------------------
int Note::transposeTpc(int tpc)
{
int tick = chord() ? chord()->tick() : -1;
Interval v = part()->instrument(tick)->transpose();
2014-04-23 11:08:51 +02:00
if (v.isZero())
return tpc;
if (concertPitch()) {
v.flip();
return Ms::transposeTpc(tpc, v, true);
2014-04-23 11:08:51 +02:00
}
else
return Ms::transposeTpc(tpc, v, true);
2014-04-23 11:08:51 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// noteHead
//---------------------------------------------------------
2013-11-06 15:58:05 +01:00
SymId Note::noteHead() const
2012-05-26 14:26:10 +02:00
{
int up;
NoteHead::Type ht;
2012-05-26 14:26:10 +02:00
if (chord()) {
up = chord()->up();
2012-05-26 14:26:10 +02:00
ht = chord()->durationType().headType();
}
2013-01-02 20:13:58 +01:00
else {
up = 1;
ht = NoteHead::Type::HEAD_QUARTER;
2012-05-26 14:26:10 +02:00
}
if (_headType != NoteHead::Type::HEAD_AUTO)
2013-01-28 21:45:36 +01:00
ht = _headType;
Key key = Key::C;
NoteHeadScheme scheme = NoteHeadScheme::HEAD_NORMAL;
2016-12-18 14:31:13 +01:00
if (chord() && chord()->staff()){
int tick = chord()->tick();
if (tick >= 0) {
key = chord()->staff()->key(tick);
scheme = chord()->staff()->staffType(tick)->noteHeadScheme();
}
}
SymId t = noteHead(up, _headGroup, ht, tpc(), key, scheme);
2013-11-06 15:58:05 +01:00
if (t == SymId::noSym) {
qDebug("invalid notehead %d/%d", int(_headGroup), int(ht));
t = noteHead(up, NoteHead::Group::HEAD_NORMAL, ht);
2013-01-28 21:45:36 +01:00
}
return t;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// headWidth
//
// returns the width of the notehead symbol
// or the width of the string representation of the fret mark
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Note::headWidth() const
{
2013-11-11 15:11:28 +01:00
return symWidth(noteHead());
2012-05-26 14:26:10 +02:00
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// tabHeadWidth
//---------------------------------------------------------
2014-04-28 18:38:50 +02:00
qreal Note::tabHeadWidth(StaffType* tab) const
{
qreal val;
if (tab && _fret != FRET_NONE && _string != STRING_NONE) {
QFont f = tab->fretFont();
f.setPointSizeF(tab->fretFontSize());
QFontMetricsF fm(f, MScore::paintDevice());
QString s;
if (fixed())
s = "/";
else
TAB: Support for input and display of bass string notations Supports 'standard' configuration for bass string notations in historic tablatures (lutes and other plucked instruments, as well as viols), both of the French and of the Italian style. This should fill the last 'big hole' in historic TAB support. Bass strings (or bourdons) are extra strings in addition to the 6 'standard' strings, which are not represented by tab lines and were indicated by other typograhic devices in historic sources. Among the innumerable variations shown in sources, this implementation supports the following styles, chosen to be general enough to suit the majority of cases, without requiring new parameters in the TAB style dialogue box: - French: the first 4 bass courses are indicated by a fret mark in the 'seventh' TAB position (below bottom string) with 0, 1, 2 or 3 slashes prefixed; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 4 on) and cannot contain a fret mark (as they didn't in historic sources). - Italian: the first 2 bass courses are indicated by a fret mark in the 'seventh' TAB position (abover top string) with 1 or 2 'ledger lines' underneath; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 9 on) and cannot contain a fret mark (as they didn't in historic sources). Rhythm marks above these indication are raised to leave room for them. Both styles do not blindly assume that French style is top-to-bottom and Italian is 'upside-down' -- as historic sources are -- but adapt to the actual string order of the TAB. The choice between the two styles depends on the TAB using numbers or letters for the fret marks. The implementation does not try to detect if the TAB is really of a historic style and applies either bass string notation whenever more strings are used than there are TAB lines. If this proves unsuitable to modern usage, some better heuristics can probably be found. For a discussion and some screen shots, see: https://musescore.org/en/node/67261 **Note entry** During TAB note entry, if the instruments has more strings than the TAB has lines, the string cursor can be moved outside of the TAB body, one position below for 'top-to-bottom' TAB's and one position above for 'upside-down' TAB's. Further up or down movements add, to the 'blue cursor rectangle', markers indicating which is the actual target string (the cursor does not actually move), equal to the marks a note in that string will receive (slashes, ledger lines or string ordinal, according to the style and the string); during input the user will then receive the same info as when reading entered notes. Other Notes: - the `InputStatus::_string` variable, holding the current target TAB string in TAB note entry, changed meaning from the __visual__ string index to the __physical__ string index: this allows a better containment of the peculiarities of the individual TAB styles within the `StaffStyle` class, leaving other classes somehow freer of concern about TAB visual order and other peculiarities. As this variable is only used with TAB's, this change should not affect other functions. - Some calculation for rhythm symbols have been moved from `TabDurationSymbol::draw()` to `TabDurationSymbol::layout()`, hopefully speeding up the drawing process. - In fonts for historic styles, '10' has been replaced by 'X' both in fret numbers and in string ordinals, as this is more common in historic sources. Currently, this is not configurable; an additional style parameter could be added in future, if there will be enough request for it.
2015-07-06 17:40:02 +02:00
s = tab->fretString(_fret, _string, _ghost);
val = fm.width(s) * magS();
2013-05-23 16:58:22 +02:00
}
else
val = headWidth();
return val;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// headHeight
//
// returns the height of the notehead symbol
// or the height of the string representation of the fret mark
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
qreal Note::headHeight() const
{
2013-11-11 15:11:28 +01:00
return symHeight(noteHead());
2012-05-26 14:26:10 +02:00
}
2014-04-28 18:38:50 +02:00
//---------------------------------------------------------
// tabHeadHeight
//---------------------------------------------------------
qreal Note::tabHeadHeight(StaffType* tab) const
{
2015-02-19 10:28:25 +01:00
if (tab && _fret != FRET_NONE && _string != STRING_NONE)
return tab->fretBoxH() * magS();
return headHeight();
}
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
// stemDownNW
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
QPointF Note::stemDownNW() const
2013-01-02 09:29:17 +01:00
{
return symStemDownNW(noteHead());
}
//---------------------------------------------------------
// stemUpSE
//---------------------------------------------------------
QPointF Note::stemUpSE() const
{
return symStemUpSE(noteHead());
2013-01-02 09:29:17 +01:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// playTicks
2013-05-12 12:43:22 +02:00
/// Return total tick len of tied notes
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
int Note::playTicks() const
{
int stick = firstTiedNote()->chord()->tick();
const Note* note = lastTiedNote();
2013-05-12 12:43:22 +02:00
return note->chord()->tick() + note->chord()->actualTicks() - stick;
2012-05-26 14:26:10 +02:00
}
2012-09-12 16:19:03 +02:00
//---------------------------------------------------------
// addSpanner
//---------------------------------------------------------
void Note::addSpanner(Spanner* l)
{
Element* e = l->endElement();
if (e && e->isNote()) {
Note* note = toNote(e);
note->addSpannerBack(l);
if (l->isGlissando())
note->chord()->setEndsGlissando(true);
}
addSpannerFor(l);
2012-09-12 16:19:03 +02:00
}
//---------------------------------------------------------
// removeSpanner
//---------------------------------------------------------
void Note::removeSpanner(Spanner* l)
{
Note* e = static_cast<Note*>(l->endElement());
if (e && e->isNote()) {
if (!e->removeSpannerBack(l)) {
qDebug("Note::removeSpanner(%p): cannot remove spannerBack %s %p", this, l->name(), l);
// abort();
}
if (l->isGlissando())
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
e->chord()->updateEndsGlissando();
2012-09-12 16:19:03 +02:00
}
if (!removeSpannerFor(l)) {
qDebug("Note(%p): cannot remove spannerFor %s %p", this, l->name(), l);
2012-09-12 16:19:03 +02:00
// abort();
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// add
//---------------------------------------------------------
void Note::add(Element* e)
{
e->setParent(this);
e->setTrack(track());
2012-05-26 14:26:10 +02:00
switch(e->type()) {
2017-01-18 14:16:33 +01:00
case ElementType::NOTEDOT:
_dots.append(toNoteDot(e));
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::SYMBOL:
case ElementType::IMAGE:
case ElementType::FINGERING:
case ElementType::TEXT:
case ElementType::BEND:
2013-03-25 16:27:20 +01:00
_el.push_back(e);
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::TIE:
2012-05-26 14:26:10 +02:00
{
Tie* tie = toTie(e);
2014-05-30 13:35:44 +02:00
tie->setStartNote(this);
2012-05-26 14:26:10 +02:00
tie->setTrack(track());
2014-05-30 13:35:44 +02:00
setTieFor(tie);
2012-05-26 14:26:10 +02:00
if (tie->endNote())
tie->endNote()->setTieBack(tie);
2013-01-30 17:50:01 +01:00
int n = tie->spannerSegments().size();
for (int i = 0; i < n; ++i) {
SpannerSegment* ss = tie->spannerSegments().at(i);
2012-05-26 14:26:10 +02:00
if (ss->system())
ss->system()->add(ss);
}
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::ACCIDENTAL:
_accidental = toAccidental(e);
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::TEXTLINE:
case ElementType::GLISSANDO:
2012-09-12 16:19:03 +02:00
addSpanner(static_cast<Spanner*>(e));
break;
2012-05-26 14:26:10 +02:00
default:
qDebug("Note::add() not impl. %s", e->name());
2012-05-26 14:26:10 +02:00
break;
}
2016-03-02 13:20:19 +01:00
score()->setLayout(tick());
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// remove
//---------------------------------------------------------
void Note::remove(Element* e)
{
switch(e->type()) {
2017-01-18 14:16:33 +01:00
case ElementType::NOTEDOT:
2016-01-04 14:48:58 +01:00
_dots.takeLast();
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::TEXT:
case ElementType::SYMBOL:
case ElementType::IMAGE:
case ElementType::FINGERING:
case ElementType::BEND:
2012-05-26 14:26:10 +02:00
if (!_el.remove(e))
qDebug("Note::remove(): cannot find %s", e->name());
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::TIE:
2012-05-26 14:26:10 +02:00
{
Tie* tie = toTie(e);
2012-05-26 14:26:10 +02:00
setTieFor(0);
if (tie->endNote())
2012-05-26 14:26:10 +02:00
tie->endNote()->setTieBack(0);
for (SpannerSegment* ss : tie->spannerSegments()) {
2012-05-26 14:26:10 +02:00
Q_ASSERT(ss->spanner() == tie);
if (ss->system())
ss->system()->remove(ss);
}
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::ACCIDENTAL:
2012-05-26 14:26:10 +02:00
_accidental = 0;
break;
2017-01-18 14:16:33 +01:00
case ElementType::TEXTLINE:
case ElementType::GLISSANDO:
2012-09-12 16:19:03 +02:00
removeSpanner(static_cast<Spanner*>(e));
break;
2012-05-26 14:26:10 +02:00
default:
qDebug("Note::remove() not impl. %s", e->name());
2012-05-26 14:26:10 +02:00
break;
}
2016-03-02 13:20:19 +01:00
score()->setLayout(tick());
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// isNoteName
//---------------------------------------------------------
bool Note::isNoteName() const
{
if (chord() && chord()->staff()) {
2016-12-13 13:16:17 +01:00
NoteHeadScheme s = chord()->staff()->staffType(tick())->noteHeadScheme();
return s == NoteHeadScheme::HEAD_PITCHNAME || s == NoteHeadScheme::HEAD_PITCHNAME_GERMAN || s == NoteHeadScheme::HEAD_SOLFEGE || s == NoteHeadScheme::HEAD_SOLFEGE_FIXED;
}
return false;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// draw
//---------------------------------------------------------
void Note::draw(QPainter* painter) const
{
if (_hidden)
return;
2013-02-20 17:53:15 +01:00
2013-03-07 21:00:22 +01:00
QColor c(curColor());
2013-02-20 17:53:15 +01:00
painter->setPen(c);
2016-12-13 13:16:17 +01:00
bool tablature = staff() && staff()->isTabStaff(chord()->tick());
// tablature
if (tablature) {
2016-12-13 13:16:17 +01:00
StaffType* tab = staff()->staffType(tick());
if (tieBack() && !tab->showBackTied()) // skip back-tied notes if not shown
return;
QString s;
if (fixed())
s = "/";
else
TAB: Support for input and display of bass string notations Supports 'standard' configuration for bass string notations in historic tablatures (lutes and other plucked instruments, as well as viols), both of the French and of the Italian style. This should fill the last 'big hole' in historic TAB support. Bass strings (or bourdons) are extra strings in addition to the 6 'standard' strings, which are not represented by tab lines and were indicated by other typograhic devices in historic sources. Among the innumerable variations shown in sources, this implementation supports the following styles, chosen to be general enough to suit the majority of cases, without requiring new parameters in the TAB style dialogue box: - French: the first 4 bass courses are indicated by a fret mark in the 'seventh' TAB position (below bottom string) with 0, 1, 2 or 3 slashes prefixed; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 4 on) and cannot contain a fret mark (as they didn't in historic sources). - Italian: the first 2 bass courses are indicated by a fret mark in the 'seventh' TAB position (abover top string) with 1 or 2 'ledger lines' underneath; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 9 on) and cannot contain a fret mark (as they didn't in historic sources). Rhythm marks above these indication are raised to leave room for them. Both styles do not blindly assume that French style is top-to-bottom and Italian is 'upside-down' -- as historic sources are -- but adapt to the actual string order of the TAB. The choice between the two styles depends on the TAB using numbers or letters for the fret marks. The implementation does not try to detect if the TAB is really of a historic style and applies either bass string notation whenever more strings are used than there are TAB lines. If this proves unsuitable to modern usage, some better heuristics can probably be found. For a discussion and some screen shots, see: https://musescore.org/en/node/67261 **Note entry** During TAB note entry, if the instruments has more strings than the TAB has lines, the string cursor can be moved outside of the TAB body, one position below for 'top-to-bottom' TAB's and one position above for 'upside-down' TAB's. Further up or down movements add, to the 'blue cursor rectangle', markers indicating which is the actual target string (the cursor does not actually move), equal to the marks a note in that string will receive (slashes, ledger lines or string ordinal, according to the style and the string); during input the user will then receive the same info as when reading entered notes. Other Notes: - the `InputStatus::_string` variable, holding the current target TAB string in TAB note entry, changed meaning from the __visual__ string index to the __physical__ string index: this allows a better containment of the peculiarities of the individual TAB styles within the `StaffStyle` class, leaving other classes somehow freer of concern about TAB visual order and other peculiarities. As this variable is only used with TAB's, this change should not affect other functions. - Some calculation for rhythm symbols have been moved from `TabDurationSymbol::draw()` to `TabDurationSymbol::layout()`, hopefully speeding up the drawing process. - In fonts for historic styles, '10' has been replaced by 'X' both in fret numbers and in string ordinals, as this is more common in historic sources. Currently, this is not configurable; an additional style parameter could be added in future, if there will be enough request for it.
2015-07-06 17:40:02 +02:00
s = tab->fretString(_fret, _string, _ghost);
TAB: Support for input and display of bass string notations Supports 'standard' configuration for bass string notations in historic tablatures (lutes and other plucked instruments, as well as viols), both of the French and of the Italian style. This should fill the last 'big hole' in historic TAB support. Bass strings (or bourdons) are extra strings in addition to the 6 'standard' strings, which are not represented by tab lines and were indicated by other typograhic devices in historic sources. Among the innumerable variations shown in sources, this implementation supports the following styles, chosen to be general enough to suit the majority of cases, without requiring new parameters in the TAB style dialogue box: - French: the first 4 bass courses are indicated by a fret mark in the 'seventh' TAB position (below bottom string) with 0, 1, 2 or 3 slashes prefixed; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 4 on) and cannot contain a fret mark (as they didn't in historic sources). - Italian: the first 2 bass courses are indicated by a fret mark in the 'seventh' TAB position (abover top string) with 1 or 2 'ledger lines' underneath; other bass courses are indicated, also in the 'seventh' TAB position, by the string number (from 9 on) and cannot contain a fret mark (as they didn't in historic sources). Rhythm marks above these indication are raised to leave room for them. Both styles do not blindly assume that French style is top-to-bottom and Italian is 'upside-down' -- as historic sources are -- but adapt to the actual string order of the TAB. The choice between the two styles depends on the TAB using numbers or letters for the fret marks. The implementation does not try to detect if the TAB is really of a historic style and applies either bass string notation whenever more strings are used than there are TAB lines. If this proves unsuitable to modern usage, some better heuristics can probably be found. For a discussion and some screen shots, see: https://musescore.org/en/node/67261 **Note entry** During TAB note entry, if the instruments has more strings than the TAB has lines, the string cursor can be moved outside of the TAB body, one position below for 'top-to-bottom' TAB's and one position above for 'upside-down' TAB's. Further up or down movements add, to the 'blue cursor rectangle', markers indicating which is the actual target string (the cursor does not actually move), equal to the marks a note in that string will receive (slashes, ledger lines or string ordinal, according to the style and the string); during input the user will then receive the same info as when reading entered notes. Other Notes: - the `InputStatus::_string` variable, holding the current target TAB string in TAB note entry, changed meaning from the __visual__ string index to the __physical__ string index: this allows a better containment of the peculiarities of the individual TAB styles within the `StaffStyle` class, leaving other classes somehow freer of concern about TAB visual order and other peculiarities. As this variable is only used with TAB's, this change should not affect other functions. - Some calculation for rhythm symbols have been moved from `TabDurationSymbol::draw()` to `TabDurationSymbol::layout()`, hopefully speeding up the drawing process. - In fonts for historic styles, '10' has been replaced by 'X' both in fret numbers and in string ordinals, as this is more common in historic sources. Currently, this is not configurable; an additional style parameter could be added in future, if there will be enough request for it.
2015-07-06 17:40:02 +02:00
// draw background, if required (to hide a segment of string line or to show a fretting conflict)
if (!tab->linesThrough() || fretConflict()) {
qreal d = spatium() * .1;
QRectF bb = QRectF(bbox().x()-d, tab->fretMaskY()*magS(), bbox().width() + 2*d, tab->fretMaskH()*magS());
// we do not know which viewer did this draw() call
// so update all:
2016-03-19 11:41:38 +01:00
for (MuseScoreView* view : score()->getViewer())
view->drawBackground(painter, bb);
if (fretConflict() && !score()->printing()) { //on fret conflict, draw on red background
painter->save();
painter->setPen(Qt::red);
painter->setBrush(QBrush(QColor(Qt::red)));
painter->drawRect(bb);
painter->restore();
2012-05-26 14:26:10 +02:00
}
}
QFont f(tab->fretFont());
f.setPointSizeF(f.pointSizeF() * spatium() * MScore::pixelRatio / SPATIUM20);
painter->setFont(f);
2013-03-07 21:00:22 +01:00
painter->setPen(c);
painter->drawText(QPointF(bbox().x(), tab->fretFontYOffset()), s);
}
// NOT tablature
else {
2013-05-12 12:51:42 +02:00
// skip drawing, if second note of a cross-measure value
if (chord()->crossMeasure() == CrossMeasure::SECOND)
2013-05-12 12:51:42 +02:00
return;
// warn if pitch extends usable range of instrument
// by coloring the notehead
if (chord() && chord()->segment() && staff() && !selected()
&& !score()->printing() && MScore::warnPitchRange) {
const Instrument* in = part()->instrument(chord()->tick());
int i = ppitch();
if (i < in->minPitchP() || i > in->maxPitchP())
painter->setPen(Qt::red);
else if (i < in->minPitchA() || i > in->maxPitchA())
painter->setPen(Qt::darkYellow);
2012-05-26 14:26:10 +02:00
}
// draw blank notehead to avoid staff and ledger lines
if (_cachedSymNull != SymId::noSym) {
painter->save();
painter->setPen(Qt::white);
drawSymbol(_cachedSymNull, painter);
painter->restore();
}
drawSymbol(_cachedNoteheadSym, painter);
2012-05-26 14:26:10 +02:00
}
}
//--------------------------------------------------
// Note::write
//---------------------------------------------------------
2016-11-19 11:51:21 +01:00
void Note::write(XmlWriter& xml) const
2012-05-26 14:26:10 +02:00
{
xml.stag("Note");
Element::writeProperties(xml);
if (_accidental)
_accidental->write(xml);
_el.write(xml);
2016-01-04 14:48:58 +01:00
for (NoteDot* dot : _dots) {
if (!dot->userOff().isNull() || !dot->visible() || dot->color() != Qt::black || dot->visible() != visible()) {
dot->write(xml);
break;
2012-05-26 14:26:10 +02:00
}
}
2014-07-21 13:24:21 +02:00
if (_tieFor)
2012-05-26 14:26:10 +02:00
_tieFor->write(xml);
2014-07-21 13:24:21 +02:00
if (_tieBack) {
int id = xml.spannerId(_tieBack);
xml.tagE(QString("endSpanner id=\"%1\"").arg(id));
}
2016-02-06 22:03:43 +01:00
if ((chord() == 0 || chord()->playEventType() != PlayEventType::Auto) && !_playEvents.empty()) {
xml.stag("Events");
2016-03-19 11:41:38 +01:00
for (const NoteEvent& e : _playEvents)
e.write(xml);
xml.etag();
2012-05-26 14:26:10 +02:00
}
2016-11-25 11:46:28 +01:00
for (P_ID id : { P_ID::PITCH, P_ID::TPC1, P_ID::TPC2, P_ID::SMALL, P_ID::MIRROR_HEAD, P_ID::DOT_POSITION,
P_ID::HEAD_GROUP, P_ID::VELO_OFFSET, P_ID::PLAY, P_ID::TUNING, P_ID::FRET, P_ID::STRING,
P_ID::GHOST, P_ID::HEAD_TYPE, P_ID::VELO_TYPE, P_ID::FIXED, P_ID::FIXED_LINE
}) {
writeProperty(xml, id);
}
2012-08-10 17:01:35 +02:00
2016-03-19 11:41:38 +01:00
for (Spanner* e : _spannerFor)
2012-09-12 16:19:03 +02:00
e->write(xml);
2016-03-19 11:41:38 +01:00
for (Spanner* e : _spannerBack)
2014-07-21 13:24:21 +02:00
xml.tagE(QString("endSpanner id=\"%1\"").arg(xml.spannerId(e)));
2012-05-26 14:26:10 +02:00
xml.etag();
}
//---------------------------------------------------------
// Note::read
//---------------------------------------------------------
2013-01-11 18:10:18 +01:00
void Note::read(XmlReader& e)
2012-05-26 14:26:10 +02:00
{
2016-09-22 12:02:27 +02:00
setTpc1(Tpc::TPC_INVALID);
setTpc2(Tpc::TPC_INVALID);
2016-02-04 11:27:47 +01:00
2013-01-11 18:10:18 +01:00
while (e.readNextStartElement()) {
2016-09-22 12:02:27 +02:00
if (readProperties(e))
2012-05-26 14:26:10 +02:00
;
else
2013-01-11 18:10:18 +01:00
e.unknown();
2012-05-26 14:26:10 +02:00
}
// ensure sane values:
_pitch = limit(_pitch, 0, 127);
2014-04-14 13:16:46 +02:00
if (!tpcIsValid(_tpc[0]) && !tpcIsValid(_tpc[1])) {
2014-06-20 17:07:22 +02:00
Key key = (staff() && chord()) ? staff()->key(chord()->tick()) : Key::C;
2014-06-05 11:37:21 +02:00
int tpc = pitch2tpc(_pitch, key, Prefer::NEAREST);
2014-04-14 13:16:46 +02:00
if (concertPitch())
_tpc[0] = tpc;
else
_tpc[1] = tpc;
}
if (!(tpcIsValid(_tpc[0]) && tpcIsValid(_tpc[1]))) {
int tick = chord() ? chord()->tick() : -1;
Interval v = staff() ? part()->instrument(tick)->transpose() : Interval();
2014-04-14 13:16:46 +02:00
if (tpcIsValid(_tpc[0])) {
v.flip();
if (v.isZero())
_tpc[1] = _tpc[0];
else
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
2014-04-14 13:16:46 +02:00
}
else {
if (v.isZero())
_tpc[0] = _tpc[1];
else
_tpc[0] = Ms::transposeTpc(_tpc[1], v, true);
}
}
2016-01-04 14:48:58 +01:00
// check consistency of pitch, tpc1, tpc2, and transposition
// see note in InstrumentChange::read() about a known case of tpc corruption produced in 2.0.x
// but since there are other causes of tpc corruption (eg, https://musescore.org/en/node/74746)
// including perhaps some we don't know about yet,
// we will attempt to fix some problems here regardless of version
2016-01-04 14:48:58 +01:00
if (!e.pasteMode() && !MScore::testMode) {
int tpc1Pitch = (tpc2pitch(_tpc[0]) + 12) % 12;
int tpc2Pitch = (tpc2pitch(_tpc[1]) + 12) % 12;
int concertPitch = _pitch % 12;
if (tpc1Pitch != concertPitch) {
qDebug("bad tpc1 - concertPitch = %d, tpc1 = %d", concertPitch, tpc1Pitch);
_pitch += tpc1Pitch - concertPitch;
}
Interval v = staff()->part()->instrument(e.tick())->transpose();
int transposedPitch = (_pitch - v.chromatic) % 12;
if (tpc2Pitch != transposedPitch) {
qDebug("bad tpc2 - transposedPitch = %d, tpc2 = %d", transposedPitch, tpc2Pitch);
2016-09-22 12:02:27 +02:00
}
}
}
//---------------------------------------------------------
// readProperties
//---------------------------------------------------------
bool Note::readProperties(XmlReader& e)
{
const QStringRef& tag(e.name());
if (tag == "pitch")
_pitch = e.readInt();
else if (tag == "tpc") {
_tpc[0] = e.readInt();
_tpc[1] = _tpc[0];
}
else if (tag == "track") // for performance
setTrack(e.readInt());
else if (tag == "Accidental") {
Accidental* a = new Accidental(score());
a->setTrack(track());
a->read(e);
add(a);
}
else if (tag == "Tie") {
Tie* tie = new Tie(score());
tie->setParent(this);
tie->setTrack(track());
tie->read(e);
tie->setStartNote(this);
_tieFor = tie;
}
else if (tag == "tpc2")
_tpc[1] = e.readInt();
else if (tag == "small")
setSmall(e.readInt());
else if (tag == "mirror")
setProperty(P_ID::MIRROR_HEAD, Ms::getProperty(P_ID::MIRROR_HEAD, e));
else if (tag == "dotPosition")
setProperty(P_ID::DOT_POSITION, Ms::getProperty(P_ID::DOT_POSITION, e));
else if (tag == "fixed")
setFixed(e.readBool());
else if (tag == "fixedLine")
setFixedLine(e.readInt());
else if (tag == "head")
setProperty(P_ID::HEAD_GROUP, Ms::getProperty(P_ID::HEAD_GROUP, e));
else if (tag == "velocity")
setVeloOffset(e.readInt());
else if (tag == "play")
setPlay(e.readInt());
else if (tag == "tuning")
setTuning(e.readDouble());
else if (tag == "fret")
setFret(e.readInt());
else if (tag == "string")
setString(e.readInt());
else if (tag == "ghost")
setGhost(e.readInt());
else if (tag == "headType")
setProperty(P_ID::HEAD_TYPE, Ms::getProperty(P_ID::HEAD_TYPE, e));
2016-09-22 12:02:27 +02:00
else if (tag == "veloType")
setProperty(P_ID::VELO_TYPE, Ms::getProperty(P_ID::VELO_TYPE, e));
else if (tag == "line")
_line = e.readInt();
else if (tag == "Fingering") {
Fingering* f = new Fingering(score());
f->read(e);
add(f);
}
else if (tag == "Symbol") {
Symbol* s = new Symbol(score());
s->setTrack(track());
s->read(e);
add(s);
}
else if (tag == "Image") {
if (MScore::noImages)
e.skipCurrentElement();
else {
Image* image = new Image(score());
image->setTrack(track());
image->read(e);
add(image);
}
}
else if (tag == "Bend") {
Bend* b = new Bend(score());
b->setTrack(track());
b->read(e);
add(b);
}
else if (tag == "NoteDot") {
NoteDot* dot = new NoteDot(score());
dot->read(e);
add(dot);
}
else if (tag == "Events") {
_playEvents.clear(); // remove default event
while (e.readNextStartElement()) {
const QStringRef& tag(e.name());
if (tag == "Event") {
NoteEvent ne;
ne.read(e);
_playEvents.append(ne);
}
else
e.unknown();
}
if (chord())
chord()->setPlayEventType(PlayEventType::User);
}
else if (tag == "endSpanner") {
int id = e.intAttribute("id");
Spanner* sp = e.findSpanner(id);
if (sp) {
sp->setEndElement(this);
if (sp->isTie())
_tieBack = toTie(sp);
else {
if (sp->isGlissando() && parent() && parent()->isChord())
toChord(parent())->setEndsGlissando(true);
addSpannerBack(sp);
}
2016-09-22 12:02:27 +02:00
e.removeSpanner(sp);
}
else {
// End of a spanner whose start element will appear later;
// may happen for cross-staff spanner from a lower to a higher staff
// (for instance a glissando from bass to treble staff of piano).
// Create a place-holder spanner with end data
// (a TextLine is used only because both Spanner or SLine are abstract,
// the actual class does not matter, as long as it is derived from Spanner)
int id = e.intAttribute("id", -1);
if (id != -1 &&
// DISABLE if pasting into a staff with linked staves
// because the glissando is not properly cloned into the linked staves
(!e.pasteMode() || !staff()->linkedStaves() || staff()->linkedStaves()->empty())) {
Spanner* placeholder = new TextLine(score());
placeholder->setAnchor(Spanner::Anchor::NOTE);
placeholder->setEndElement(this);
placeholder->setTrack2(track());
placeholder->setTick(0);
placeholder->setTick2(e.tick());
e.addSpanner(id, placeholder);
}
}
e.readNext();
}
else if (tag == "TextLine"
|| tag == "Glissando") {
Spanner* sp = static_cast<Spanner*>(Element::name2Element(tag, score()));
// check this is not a lower-to-higher cross-staff spanner we already got
int id = e.intAttribute("id");
Spanner* placeholder = e.findSpanner(id);
if (placeholder) {
// if it is, fill end data from place-holder
sp->setAnchor(Spanner::Anchor::NOTE); // make sure we can set a Note as end element
sp->setEndElement(placeholder->endElement());
sp->setTrack2(placeholder->track2());
sp->setTick(e.tick()); // make sure tick2 will be correct
sp->setTick2(placeholder->tick2());
static_cast<Note*>(placeholder->endElement())->addSpannerBack(sp);
// remove no longer needed place-holder before reading the new spanner,
// as reading it also adds it to XML reader list of spanners,
// which would overwrite the place-holder
e.removeSpanner(placeholder);
delete placeholder;
}
sp->setTrack(track());
sp->read(e);
// DISABLE pasting of glissandi into staves with other lionked staves
// because the glissando is not properly cloned into the linked staves
if (e.pasteMode() && staff()->linkedStaves() && !staff()->linkedStaves()->empty()) {
e.removeSpanner(sp); // read() added the element to the XMLReader: remove it
delete sp;
}
else {
sp->setAnchor(Spanner::Anchor::NOTE);
sp->setStartElement(this);
sp->setTick(e.tick());
addSpannerFor(sp);
sp->setParent(this);
}
}
2016-09-22 12:02:27 +02:00
else if (tag == "offset")
Element::readProperties(e);
else if (Element::readProperties(e))
;
else
return false;
return true;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// transposition
//---------------------------------------------------------
int Note::transposition() const
{
int tick = chord() ? chord()->tick() : -1;
return staff() ? part()->instrument(tick)->transpose().chromatic : 0;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// drag
//---------------------------------------------------------
2013-10-22 12:05:31 +02:00
QRectF Note::drag(EditData* data)
2012-05-26 14:26:10 +02:00
{
2016-12-13 13:16:17 +01:00
if (staff()->isDrumStaff(tick()))
2013-03-22 18:30:05 +01:00
return QRect();
// dragMode = true;
2012-05-26 14:26:10 +02:00
QRectF bb(chord()->bbox());
qreal _spatium = spatium();
2016-12-13 13:16:17 +01:00
bool tab = staff()->isTabStaff(chord()->tick());
qreal step = _spatium * (tab ? staff()->staffType(tick())->lineDistance().val() : 0.5);
_lineOffset = lrint(data->delta.y() / step);
triggerLayout();
2012-05-26 14:26:10 +02:00
return bb.translated(chord()->pagePos());
}
//---------------------------------------------------------
// endDrag
//---------------------------------------------------------
void Note::endDrag()
{
dragMode = false;
2012-05-26 14:26:10 +02:00
if (_lineOffset == 0)
return;
Staff* staff = score()->staff(chord()->vStaffIdx());
int tick = chord()->tick();
2016-12-13 13:16:17 +01:00
if (staff->isTabStaff(tick)) {
// on TABLATURE staves, dragging a note keeps same pitch on a different string (if possible)
// determine new string of dragged note (if tablature is upside down, invert _lineOffset)
// and fret for the same pitch on the new string
const StringData* strData = staff->part()->instrument()->stringData();
2016-12-13 13:16:17 +01:00
int nString = _string + (staff->staffType(tick)->upsideDown() ? -_lineOffset : _lineOffset);
int nFret = strData->fret(_pitch, nString, staff, tick);
if (nFret < 0) // no fret?
return; // no party!
// move the note together with all notes tied to it
for (Note* nn : tiedNotes()) {
bool refret = false;
if (nn->fret() != nFret) {
nn->undoChangeProperty(P_ID::FRET, nFret);
refret = true;
}
if (nn->string() != nString) {
nn->undoChangeProperty(P_ID::STRING, nString);
refret = true;
}
if (refret)
strData->fretChords(nn->chord());
}
}
else {
// on PITCHED / PERCUSSION staves, dragging a note changes the note pitch
// determine new pitch of dragged note
Key key = staff->key(tick);
int nPitch = line2pitch(_line + _lineOffset, staff->clef(tick), key);
if (!concertPitch()) {
Interval interval = staff->part()->instrument(tick)->transpose();
nPitch += interval.chromatic;
2014-07-15 11:01:24 +02:00
}
int tpc1 = pitch2tpc(nPitch, key, Prefer::NEAREST);
int tpc2 = pitch2tpc(nPitch - transposition(), key, Prefer::NEAREST);
// undefined for non-tablature staves
for (Note* nn : tiedNotes()) {
2016-09-03 17:45:59 +02:00
// score()->undoChangePitch(nn, nPitch, tpc1, tpc2);
nn->undoChangeProperty(P_ID::PITCH, nPitch);
nn->undoChangeProperty(P_ID::TPC1, tpc1);
nn->undoChangeProperty(P_ID::TPC2, tpc2);
}
2012-05-26 14:26:10 +02:00
}
_lineOffset = 0;
score()->select(this, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// acceptDrop
//---------------------------------------------------------
2014-08-13 21:01:21 +02:00
bool Note::acceptDrop(const DropData& data) const
2012-05-26 14:26:10 +02:00
{
2014-08-13 21:01:21 +02:00
Element* e = data.element;
2017-01-18 14:16:33 +01:00
ElementType type = e->type();
if (type == ElementType::GLISSANDO) {
2015-02-25 11:43:55 +01:00
for (auto e : _spannerFor)
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::GLISSANDO) {
2015-02-25 11:43:55 +01:00
return false;
}
return true;
}
bool isTablature = staff()->isTabStaff(tick());
2017-01-18 14:16:33 +01:00
return (type == ElementType::ARTICULATION
|| type == ElementType::CHORDLINE
|| type == ElementType::TEXT
|| type == ElementType::REHEARSAL_MARK
|| (type == ElementType::FINGERING && !isTablature)
2017-01-18 14:16:33 +01:00
|| type == ElementType::ACCIDENTAL
|| type == ElementType::BREATH
|| type == ElementType::ARPEGGIO
|| type == ElementType::NOTEHEAD
|| type == ElementType::NOTE
|| type == ElementType::TREMOLO
|| type == ElementType::STAFF_STATE
|| type == ElementType::INSTRUMENT_CHANGE
|| type == ElementType::IMAGE
|| type == ElementType::CHORD
|| type == ElementType::HARMONY
|| type == ElementType::DYNAMIC
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::ACCIACCATURA)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::APPOGGIATURA)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::GRACE4)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::GRACE16)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::GRACE32)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::GRACE8_AFTER)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::GRACE16_AFTER)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::GRACE32_AFTER)
|| (noteType() == NoteType::NORMAL && type == ElementType::BAGPIPE_EMBELLISHMENT)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::SBEAM)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::MBEAM)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::NBEAM)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::BEAM32)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::BEAM64)
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::AUTOBEAM)
2017-01-31 19:30:49 +01:00
|| (type == ElementType::ICON && toIcon(e)->iconType() == IconType::PARENTHESES)
2017-01-18 14:16:33 +01:00
|| (type == ElementType::SYMBOL)
|| (type == ElementType::CLEF)
|| (type == ElementType::KEYSIG)
|| (type == ElementType::TIMESIG)
|| (type == ElementType::BAR_LINE)
|| (type == ElementType::SLUR)
|| (type == ElementType::HAIRPIN)
|| (type == ElementType::STAFF_TEXT)
2017-01-23 10:54:57 +01:00
|| (type == ElementType::SYSTEM_TEXT)
2017-01-18 14:16:33 +01:00
|| (type == ElementType::TEMPO_TEXT)
|| (type == ElementType::BEND)
|| (type == ElementType::TREMOLOBAR)
|| (type == ElementType::FRET_DIAGRAM)
|| (type == ElementType::FIGURED_BASS)
|| (type == ElementType::LYRICS));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// drop
//---------------------------------------------------------
Element* Note::drop(const DropData& data)
{
Element* e = data.element;
bool isTablature = staff()->isTabStaff(tick());
2012-05-26 14:26:10 +02:00
Chord* ch = chord();
2012-05-26 14:26:10 +02:00
switch(e->type()) {
2017-01-18 14:16:33 +01:00
case ElementType::REHEARSAL_MARK:
2012-05-26 14:26:10 +02:00
return ch->drop(data);
2017-01-18 14:16:33 +01:00
case ElementType::SYMBOL:
case ElementType::IMAGE:
2012-10-25 16:21:07 +02:00
e->setParent(this);
score()->undoAddElement(e);
return e;
2017-01-18 14:16:33 +01:00
case ElementType::FINGERING:
if (!isTablature) {
e->setParent(this);
score()->undoAddElement(e);
return e;
}
else
delete e;
return 0;
2012-05-26 14:26:10 +02:00
2017-01-18 14:16:33 +01:00
case ElementType::SLUR:
2012-05-26 14:26:10 +02:00
delete e;
data.view->cmdAddSlur(this, 0);
return 0;
2017-01-18 14:16:33 +01:00
case ElementType::HAIRPIN:
{
Hairpin* hairpin = toHairpin(e);
data.view->cmdAddHairpin(hairpin->hairpinType());
delete e;
}
return 0;
2017-01-18 14:16:33 +01:00
case ElementType::LYRICS:
e->setParent(ch);
e->setTrack(track());
2012-05-26 14:26:10 +02:00
score()->undoAddElement(e);
return e;
2017-01-18 14:16:33 +01:00
case ElementType::ACCIDENTAL:
score()->changeAccidental(this, static_cast<Accidental*>(e)->accidentalType());
2012-05-26 14:26:10 +02:00
break;
2017-01-18 14:16:33 +01:00
case ElementType::BEND:
e->setParent(this);
e->setTrack(track());
score()->undoAddElement(e);
2012-05-26 14:26:10 +02:00
return e;
2017-01-18 14:16:33 +01:00
case ElementType::NOTEHEAD:
2012-05-26 14:26:10 +02:00
{
NoteHead* s = toNoteHead(e);
NoteHead::Group group = s->headGroup();
if (group == NoteHead::Group::HEAD_INVALID) {
qDebug("unknown notehead");
group = NoteHead::Group::HEAD_NORMAL;
2012-05-26 14:26:10 +02:00
}
delete s;
if (group != _headGroup) {
if (links()) {
2016-03-19 11:41:38 +01:00
for (ScoreElement* e : *links()) {
2016-06-09 09:26:13 +02:00
e->undoChangeProperty(P_ID::HEAD_GROUP, int(group));
2012-05-26 14:26:10 +02:00
Note* note = static_cast<Note*>(e);
2016-12-13 13:16:17 +01:00
if (note->staff() && note->staff()->isTabStaff(ch->tick()) && group == NoteHead::Group::HEAD_CROSS)
2016-06-09 09:26:13 +02:00
e->undoChangeProperty(P_ID::GHOST, true);
2012-05-26 14:26:10 +02:00
}
}
2016-11-07 09:59:06 +01:00
else {
2016-06-09 09:26:13 +02:00
undoChangeProperty(P_ID::HEAD_GROUP, int(group));
2016-11-07 09:59:06 +01:00
}
2012-05-26 14:26:10 +02:00
}
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::ICON:
2012-05-26 14:26:10 +02:00
{
switch (toIcon(e)->iconType()) {
case IconType::ACCIACCATURA:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::ACCIACCATURA, MScore::division/2);
2012-05-26 14:26:10 +02:00
break;
case IconType::APPOGGIATURA:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::APPOGGIATURA, MScore::division/2);
2012-05-26 14:26:10 +02:00
break;
case IconType::GRACE4:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE4, MScore::division);
2012-05-26 14:26:10 +02:00
break;
case IconType::GRACE16:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE16, MScore::division/4);
2012-05-26 14:26:10 +02:00
break;
case IconType::GRACE32:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE32, MScore::division/8);
2014-04-23 18:07:38 +02:00
break;
case IconType::GRACE8_AFTER:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE8_AFTER, MScore::division/2);
2014-04-23 18:07:38 +02:00
break;
case IconType::GRACE16_AFTER:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE16_AFTER, MScore::division/4);
2014-04-23 18:07:38 +02:00
break;
case IconType::GRACE32_AFTER:
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, pitch(), NoteType::GRACE32_AFTER, MScore::division/8);
2012-05-26 14:26:10 +02:00
break;
case IconType::SBEAM:
case IconType::MBEAM:
case IconType::NBEAM:
case IconType::BEAM32:
case IconType::BEAM64:
case IconType::AUTOBEAM:
2012-05-26 14:26:10 +02:00
return ch->drop(data);
break;
2017-01-31 19:30:49 +01:00
case IconType::PARENTHESES:
addParentheses();
break;
default:
break;
2012-05-26 14:26:10 +02:00
}
}
delete e;
break;
2017-01-18 14:16:33 +01:00
case ElementType::BAGPIPE_EMBELLISHMENT:
2013-08-01 22:24:36 +02:00
{
2013-08-04 10:35:46 +02:00
BagpipeEmbellishment* b = static_cast<BagpipeEmbellishment*>(e);
noteList nl = b->getNoteList();
// add grace notes in reverse order, as setGraceNote adds a grace note
// before the current note
for (int i = nl.size() - 1; i >= 0; --i) {
int p = BagpipeEmbellishment::BagpipeNoteInfoList[nl.at(i)].pitch;
2014-05-27 10:35:28 +02:00
score()->setGraceNote(ch, p, NoteType::GRACE32, MScore::division/8);
2013-08-01 22:24:36 +02:00
}
}
delete e;
break;
2012-05-26 14:26:10 +02:00
2017-01-18 14:16:33 +01:00
case ElementType::NOTE:
2012-05-26 14:26:10 +02:00
{
Chord* ch = chord();
2014-05-27 10:35:28 +02:00
if (ch->noteType() != NoteType::NORMAL) {
2012-05-26 14:26:10 +02:00
delete e;
return 0;
}
// calculate correct transposed tpc
Note* n = toNote(e);
Interval v = part()->instrument(ch->tick())->transpose();
v.flip();
n->setTpc2(Ms::transposeTpc(n->tpc1(), v, true));
// replace this note with new note
n->setParent(ch);
2012-05-26 14:26:10 +02:00
score()->undoRemoveElement(this);
score()->undoAddElement(n);
2012-05-26 14:26:10 +02:00
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::GLISSANDO:
2012-05-26 14:26:10 +02:00
{
2015-02-25 11:43:55 +01:00
for (auto e : _spannerFor) {
2017-01-18 14:16:33 +01:00
if (e->type() == ElementType::GLISSANDO) {
2015-02-25 19:53:10 +01:00
qDebug("there is already a glissando");
2015-02-25 11:43:55 +01:00
delete e;
return 0;
}
}
// this is the glissando initial note, look for a suitable final note
Note* finalNote = Glissando::guessFinalNote(chord());
2016-12-13 13:16:17 +01:00
if (finalNote) {
// init glissando data
Glissando* gliss = toGlissando(e);
gliss->setAnchor(Spanner::Anchor::NOTE);
gliss->setStartElement(this);
gliss->setEndElement(finalNote);
gliss->setTick(ch->tick());
gliss->setTick2(finalNote->chord()->tick());
gliss->setTrack(track());
gliss->setTrack2(finalNote->track());
// in TAB, use straight line with no text
2016-12-13 13:16:17 +01:00
if (staff()->isTabStaff(finalNote->chord()->tick())) {
gliss->setGlissandoType(Glissando::Type::STRAIGHT);
gliss->setShowText(false);
}
gliss->setParent(this);
score()->undoAddElement(e);
2012-05-26 14:26:10 +02:00
}
else {
qDebug("no segment for second note of glissando found");
2012-05-26 14:26:10 +02:00
delete e;
return 0;
}
}
break;
2017-01-18 14:16:33 +01:00
case ElementType::CHORD:
2012-05-26 14:26:10 +02:00
{
Chord* c = toChord(e);
2012-05-26 14:26:10 +02:00
Note* n = c->upNote();
2016-03-02 13:20:19 +01:00
Direction dir = c->stemDirection();
2013-01-02 20:13:58 +01:00
int t = (staff2track(staffIdx()) + n->voice());
score()->select(0, SelectType::SINGLE, 0);
2012-05-26 14:26:10 +02:00
NoteVal nval;
nval.pitch = n->pitch();
nval.headGroup = n->headGroup();
Segment* seg = score()->setNoteRest(chord()->segment(), t, nval,
score()->inputState().duration().fraction(), dir);
ChordRest* cr = toChordRest(seg->element(t));
2012-05-26 14:26:10 +02:00
if (cr)
score()->nextInputPos(cr, true);
delete e;
}
break;
default:
return ch->drop(data);
}
return 0;
}
//---------------------------------------------------------
2017-01-31 19:30:49 +01:00
// addParentheses
//---------------------------------------------------------
2014-07-18 18:38:14 +02:00
2017-01-31 19:30:49 +01:00
void Note::addParentheses()
{
Symbol* s = new Symbol(score());
s->setSym(SymId::noteheadParenthesisLeft);
s->setParent(this);
score()->undoAddElement(s);
s = new Symbol(score());
s->setSym(SymId::noteheadParenthesisRight);
s->setParent(this);
score()->undoAddElement(s);
}
2014-07-18 18:38:14 +02:00
2014-04-02 20:31:37 +02:00
//---------------------------------------------------------
// setDotY
2014-04-02 20:31:37 +02:00
//---------------------------------------------------------
2016-03-02 13:20:19 +01:00
void Note::setDotY(Direction pos)
2014-04-02 20:31:37 +02:00
{
bool onLine = false;
qreal y = 0;
2016-12-13 13:16:17 +01:00
if (staff()->isTabStaff(chord()->tick())) {
2014-04-02 20:31:37 +02:00
// with TAB's, dotPosX is not set:
// get dot X from width of fret text and use TAB default spacing
2016-12-13 13:16:17 +01:00
StaffType* tab = staff()->staffType(tick());
2014-04-02 20:31:37 +02:00
if (tab->stemThrough() ) {
// if fret mark on lines, use standard processing
if (tab->onLines())
onLine = true;
else
// if fret marks above lines, raise the dots by half line distance
y = -0.5;
}
// if stems beside staff, do nothing
else
return;
}
else
onLine = !(line() & 1);
bool oddVoice = voice() & 1;
if (onLine) {
// displace dots by half spatium up or down according to voice
2016-03-02 13:20:19 +01:00
if (pos == Direction::AUTO)
2014-04-02 20:31:37 +02:00
y = oddVoice ? 0.5 : -0.5;
2016-03-02 13:20:19 +01:00
else if (pos == Direction::UP)
2014-04-02 20:31:37 +02:00
y = -0.5;
else
y = 0.5;
}
else {
2016-03-02 13:20:19 +01:00
if (pos == Direction::UP && !oddVoice)
2014-04-02 20:31:37 +02:00
y -= 1.0;
2016-03-02 13:20:19 +01:00
else if (pos == Direction::DOWN && oddVoice)
2014-04-02 20:31:37 +02:00
y += 1.0;
}
2016-12-13 13:16:17 +01:00
y *= spatium() * staff()->lineDistance(tick());
2014-04-02 20:31:37 +02:00
// apply to dots
2016-01-04 14:48:58 +01:00
int cdots = chord()->dots();
int ndots = _dots.size();
int n = cdots - ndots;
for (int i = 0; i < n; ++i) {
NoteDot* dot = new NoteDot(score());
dot->setParent(this);
dot->setTrack(track()); // needed to know the staff it belongs to (and detect tablature)
dot->setVisible(visible());
score()->undoAddElement(dot);
}
if (n < 0) {
for (int i = 0; i < -n; ++i)
score()->undoRemoveElement(_dots.back());
2014-04-02 20:31:37 +02:00
}
2016-05-12 16:51:42 +02:00
for (NoteDot* dot : _dots) {
dot->layout();
dot->rypos() = y;
}
2014-04-02 20:31:37 +02:00
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// layout
//---------------------------------------------------------
void Note::layout()
{
2016-12-13 13:16:17 +01:00
bool useTablature = staff() && staff()->isTabStaff(chord()->tick());
2012-05-26 14:26:10 +02:00
if (useTablature) {
2016-12-13 13:16:17 +01:00
StaffType* tab = staff()->staffType(tick());
2012-05-26 14:26:10 +02:00
qreal mags = magS();
qreal w = tabHeadWidth(tab);
bbox().setRect(0.0, tab->fretBoxY() * mags, w, tab->fretBoxH() * mags);
2012-05-26 14:26:10 +02:00
}
2012-08-01 18:00:27 +02:00
else {
SymId nh = noteHead();
_cachedNoteheadSym = nh;
if (isNoteName()) {
_cachedSymNull = SymId::noteEmptyBlack;
NoteHead::Type ht = _headType == NoteHead::Type::HEAD_AUTO ? chord()->durationType().headType() : _headType;
if (ht == NoteHead::Type::HEAD_WHOLE)
_cachedSymNull = SymId::noteEmptyWhole;
else if (ht == NoteHead::Type::HEAD_HALF)
_cachedSymNull = SymId::noteEmptyHalf;
}
else
_cachedSymNull = SymId::noSym;
setbbox(symBbox(nh));
}
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// layout2
// called after final position of note is set
//---------------------------------------------------------
void Note::layout2()
{
// for standard staves this is done in Score::layoutChords3()
// so that the results are available there
2016-12-23 12:05:18 +01:00
2016-12-13 13:16:17 +01:00
if (staff()->isTabStaff(chord()->tick()))
adjustReadPos();
2012-05-26 14:26:10 +02:00
int dots = chord()->dots();
if (dots) {
2014-08-11 06:57:11 +02:00
qreal d = score()->point(score()->styleS(StyleIdx::dotNoteDistance)) * mag();
qreal dd = score()->point(score()->styleS(StyleIdx::dotDotDistance)) * mag();
2012-05-26 14:26:10 +02:00
qreal x = chord()->dotPosX() - pos().x() - chord()->pos().x();
// if TAB and stems through staff
2016-12-13 13:16:17 +01:00
if (staff()->isTabStaff(chord()->tick())) {
StaffType* tab = staff()->staffType(tick());
if (tab->stemThrough()) {
// with TAB's, dot Y is not calculated during layoutChords3(),
// as layoutChords3() is not even called for TAB's;
// setDotY() actually also manages creation/deletion of NoteDot's
2016-03-02 13:20:19 +01:00
setDotY(Direction::AUTO);
// use TAB default note-to-dot spacing
dd = STAFFTYPE_TAB_DEFAULTDOTDIST_X * spatium();
d = dd * 0.5;
}
}
2016-02-06 11:41:16 +01:00
// apply to dots
qreal xx = x + d;
for (NoteDot* dot : _dots) {
dot->rxpos() = xx;
dot->adjustReadPos();
xx += dd;
2012-05-26 14:26:10 +02:00
}
}
// layout elements attached to note
2013-03-25 16:27:20 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
if (!score()->tagIsValid(e->tag()))
continue;
e->setMag(mag());
if (e->isSymbol()) {
qreal w = headWidth();
Symbol* sym = toSymbol(e);
2014-07-15 15:05:36 +02:00
QPointF rp = e->readPos();
e->layout();
if (sym->sym() == SymId::noteheadParenthesisRight) {
2016-12-13 13:16:17 +01:00
if (staff()->isTabStaff(chord()->tick())) {
StaffType* tab = staff()->staffType(tick());
w = tabHeadWidth(tab);
}
e->rxpos() += w;
}
else if (sym->sym() == SymId::noteheadParenthesisLeft) {
e->rxpos() -= symWidth(SymId::noteheadParenthesisLeft);
}
if (sym->sym() == SymId::noteheadParenthesisLeft || sym->sym() == SymId::noteheadParenthesisRight) {
// adjustReadPos() was called too early in layout(), adjust:
if (!rp.isNull()) {
e->setUserOff(QPointF());
e->setReadPos(rp);
e->adjustReadPos();
}
2014-07-15 15:05:36 +02:00
}
}
2014-07-15 15:05:36 +02:00
else
e->layout();
2012-05-26 14:26:10 +02:00
}
}
//---------------------------------------------------------
// dotIsUp
//---------------------------------------------------------
bool Note::dotIsUp() const
{
2016-02-06 22:03:43 +01:00
if (_dots.empty())
2012-05-26 14:26:10 +02:00
return true;
2016-03-02 13:20:19 +01:00
if (_userDotPosition == Direction::AUTO)
2014-04-02 22:37:13 +02:00
return _dots[0]->y() < spatium() * .1;
else
2016-03-02 13:20:19 +01:00
return (_userDotPosition == Direction::UP);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateAccidental
// set _accidental and _line depending on tpc
//---------------------------------------------------------
void Note::updateAccidental(AccidentalState* as)
{
int relLine = absStep(tpc(), epitch());
// don't touch accidentals that don't concern tpc such as
// quarter tones
if (!(_accidental && Accidental::isMicrotonal(_accidental->accidentalType()))) {
// calculate accidental
2015-04-02 10:33:53 +02:00
AccidentalType acci = AccidentalType::NONE;
AccidentalVal accVal = tpc2alter(tpc());
bool error = false;
AccidentalVal relLineAccVal = as->accidentalVal(relLine, error);
if (error)
return;
if ((accVal != relLineAccVal) || hidden() || as->tieContext(relLine)) {
as->setAccidentalVal(relLine, accVal, _tieBack != 0);
acci = Accidental::value2subtype(accVal);
// if previous tied note has same tpc, don't show accidental
if (_tieBack && _tieBack->startNote()->tpc1() == tpc1())
2015-04-02 10:33:53 +02:00
acci = AccidentalType::NONE;
else if (acci == AccidentalType::NONE)
acci = AccidentalType::NATURAL;
}
if (acci != AccidentalType::NONE && !_hidden) {
if (_accidental == 0) {
Accidental* a = new Accidental(score());
a->setParent(this);
a->setAccidentalType(acci);
score()->undoAddElement(a);
}
else if (_accidental->accidentalType() != acci) {
Accidental* a = _accidental->clone();
a->setParent(this);
a->setAccidentalType(acci);
score()->undoChangeElement(_accidental, a);
}
}
else {
if (_accidental) {
// remove this if it was AUTO:
2015-04-02 10:33:53 +02:00
if (_accidental->role() == AccidentalRole::AUTO)
score()->undoRemoveElement(_accidental);
else {
// keep it, but update type if needed
acci = Accidental::value2subtype(accVal);
2015-04-02 10:33:53 +02:00
if (acci == AccidentalType::NONE)
acci = AccidentalType::NATURAL;
if (_accidental->accidentalType() != acci) {
Accidental* a = _accidental->clone();
a->setParent(this);
a->setAccidentalType(acci);
score()->undoChangeElement(_accidental, a);
}
}
}
}
}
else {
// microtonal accidentals playback as naturals
// in 1.X, they had no effect on accidental state of measure
// ultimetely, they should probably get their own state
// for now, at least change state to natural, so subsequent notes playback as might be expected
// this is an incompatible change, but better to break it for 2.0 than wait until later
as->setAccidentalVal(relLine, AccidentalVal::NATURAL, _tieBack != 0);
}
updateRelLine(relLine, true);
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// noteType
//---------------------------------------------------------
NoteType Note::noteType() const
{
return chord()->noteType();
}
//---------------------------------------------------------
// noteTypeUserName
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Note::noteTypeUserName() const
{
switch (noteType()) {
case NoteType::ACCIACCATURA:
return QObject::tr("Acciaccatura");
case NoteType::APPOGGIATURA:
return QObject::tr("Appoggiatura");
case NoteType::GRACE8_AFTER:
case NoteType::GRACE16_AFTER:
case NoteType::GRACE32_AFTER:
return QObject::tr("Grace note after");
case NoteType::GRACE4:
case NoteType::GRACE16:
2014-08-20 09:50:42 +02:00
case NoteType::GRACE32:
return QObject::tr("Grace note before");
default:
return QObject::tr("Note");
}
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// scanElements
//---------------------------------------------------------
void Note::scanElements(void* data, void (*func)(void*, Element*), bool all)
{
func(data, this);
// tie segments are collected from System
2016-12-13 13:16:17 +01:00
// if (_tieFor && !staff()->isTabStaff(chord->tick())) // no ties in tablature
2012-05-26 14:26:10 +02:00
// _tieFor->scanElements(data, func, all);
2013-03-25 16:27:20 +01:00
for (Element* e : _el) {
2012-05-26 14:26:10 +02:00
if (score()->tagIsValid(e->tag()))
e->scanElements(data, func, all);
}
2013-07-04 13:40:25 +02:00
for (Spanner* sp : _spannerFor)
sp->scanElements(data, func, all);
2012-05-26 14:26:10 +02:00
if (!dragMode && _accidental)
func(data, _accidental);
for (NoteDot* dot : _dots)
func(data, dot);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setTrack
//---------------------------------------------------------
void Note::setTrack(int val)
{
Element::setTrack(val);
if (_tieFor) {
_tieFor->setTrack(val);
2016-03-19 11:41:38 +01:00
for (SpannerSegment* seg : _tieFor->spannerSegments())
2012-05-26 14:26:10 +02:00
seg->setTrack(val);
}
for (Spanner* s : _spannerFor) {
s->setTrack(val);
}
for (Spanner* s : _spannerBack) {
s->setTrack2(val);
}
2016-03-19 11:41:38 +01:00
for (Element* e : _el)
2012-05-26 14:26:10 +02:00
e->setTrack(val);
if (_accidental)
_accidental->setTrack(val);
if (!chord()) // if note is dragged with shift+ctrl
return;
2016-01-04 14:48:58 +01:00
for (NoteDot* dot : _dots)
dot->setTrack(val);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
// reset
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2012-11-19 10:08:15 +01:00
void Note::reset()
2012-05-26 14:26:10 +02:00
{
2016-06-09 09:26:13 +02:00
undoChangeProperty(P_ID::USER_OFF, QPointF());
chord()->undoChangeProperty(P_ID::USER_OFF, QPointF());
chord()->undoChangeProperty(P_ID::STEM_DIRECTION, Direction(Direction::AUTO));
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
2013-05-28 15:42:02 +02:00
// mag
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
2013-05-28 15:42:02 +02:00
qreal Note::mag() const
2012-05-26 14:26:10 +02:00
{
2013-05-28 15:42:02 +02:00
qreal m = chord()->mag();
if (_small)
2014-05-26 15:31:36 +02:00
m *= score()->styleD(StyleIdx::smallNoteMag);
2013-05-28 15:42:02 +02:00
return m;
2012-05-26 14:26:10 +02:00
}
2013-01-02 09:29:17 +01:00
//---------------------------------------------------------
// setSmall
//---------------------------------------------------------
void Note::setSmall(bool val)
{
_small = val;
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// line
//---------------------------------------------------------
int Note::line() const
{
if (_fixed)
return _fixedLine;
else
return _line + _lineOffset;
}
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
// setString
//---------------------------------------------------------
void Note::setString(int val)
{
_string = val;
rypos() = _string * spatium() * 1.5;
}
//---------------------------------------------------------
// setHeadGroup
//---------------------------------------------------------
void Note::setHeadGroup(NoteHead::Group val)
2012-05-26 14:26:10 +02:00
{
Q_ASSERT(int(val) >= 0 && int(val) < int(NoteHead::Group::HEAD_GROUPS));
2012-05-26 14:26:10 +02:00
_headGroup = val;
}
//---------------------------------------------------------
// ppitch
// playback pitch
//---------------------------------------------------------
int Note::ppitch() const
{
return _pitch + staff()->pitchOffset(chord()->segment()->tick());
2014-04-02 18:11:56 +02:00
}
//---------------------------------------------------------
// epitch
// effective pitch
// honours transposing instruments
//---------------------------------------------------------
int Note::epitch() const
{
return _pitch - (concertPitch() ? 0 : transposition());
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// customizeVelocity
// Input is the global velocity determined by dynamic
// signs and crescende/decrescendo etc.
// Returns the actual play velocity for this note
// modified by veloOffset
//---------------------------------------------------------
int Note::customizeVelocity(int velo) const
{
2014-05-07 18:09:01 +02:00
if (veloType() == ValueType::OFFSET_VAL)
2012-05-26 14:26:10 +02:00
velo = velo + (velo * veloOffset()) / 100;
2014-05-07 18:09:01 +02:00
else if (veloType() == ValueType::USER_VAL)
2012-05-26 14:26:10 +02:00
velo = veloOffset();
return limit(velo, 1, 127);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// endEdit
//---------------------------------------------------------
void Note::endEdit()
{
Chord* ch = chord();
if (ch->notes().size() == 1) {
2016-06-09 09:26:13 +02:00
ch->undoChangeProperty(P_ID::USER_OFF, ch->userOff() + userOff());
2012-05-26 14:26:10 +02:00
setUserOff(QPointF());
triggerLayout();
2012-05-26 14:26:10 +02:00
}
}
2014-04-09 16:09:21 +02:00
//---------------------------------------------------------
// updateRelLine
// calculate the real note line depending on clef,
// _line is the absolute line
//---------------------------------------------------------
2014-04-22 17:02:03 +02:00
void Note::updateRelLine(int relLine, bool undoable)
2014-04-09 16:09:21 +02:00
{
2016-12-23 12:05:18 +01:00
if (!staff())
return;
int idx = staffIdx() + chord()->staffMove();
Staff* stf = score()->staff(idx);
StaffType* st = stf->staffType(tick());
if (chord()->staffMove()) {
// check that destination staff makes sense (might have been deleted)
2016-12-23 12:05:18 +01:00
idx += chord()->staffMove();
int minStaff = part()->startTrack() / VOICES;
int maxStaff = part()->endTrack() / VOICES;
2016-12-23 12:05:18 +01:00
if (idx < minStaff || idx >= maxStaff || st->group() != staff()->staffType(tick())->group())
chord()->undoChangeProperty(P_ID::STAFF_MOVE, 0);
}
2014-08-20 09:50:42 +02:00
2016-12-23 12:05:18 +01:00
ClefType clef = stf->clef(chord()->tick());
2016-03-18 09:29:16 +01:00
int line = relStep(relLine, clef);
2016-01-04 14:48:58 +01:00
2016-12-23 12:05:18 +01:00
if (undoable && _line != INVALID_LINE)
undoChangeProperty(P_ID::LINE, line);
else
setLine(line);
int off = st->stepOffset();
qreal ld = st->lineDistance().val();
rypos() = (_line + off * 2.0) * spatium() * .5 * ld;
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// updateLine
//---------------------------------------------------------
void Note::updateLine()
{
2014-04-22 17:02:03 +02:00
int relLine = absStep(tpc(), epitch());
updateRelLine(relLine, false);
2012-05-26 14:26:10 +02:00
}
//---------------------------------------------------------
// setNval
2014-09-22 14:31:28 +02:00
// set note properties from NoteVal
2012-05-26 14:26:10 +02:00
//---------------------------------------------------------
void Note::setNval(const NoteVal& nval, int tick)
2012-05-26 14:26:10 +02:00
{
setPitch(nval.pitch);
2014-06-26 11:41:18 +02:00
_fret = nval.fret;
_string = nval.string;
2014-09-22 14:31:28 +02:00
_tpc[0] = nval.tpc1;
_tpc[1] = nval.tpc2;
if (tick == -1 && chord())
tick = chord()->tick();
Interval v = part()->instrument(tick)->transpose();
2014-09-22 14:31:28 +02:00
if (nval.tpc1 == Tpc::TPC_INVALID) {
Key key = staff()->key(tick);
if (!concertPitch() && !v.isZero())
key = transposeKey(key, v);
_tpc[0] = pitch2tpc(nval.pitch, key, Prefer::NEAREST);
2014-09-22 14:31:28 +02:00
}
if (nval.tpc2 == Tpc::TPC_INVALID) {
2014-04-17 12:32:10 +02:00
if (v.isZero())
_tpc[1] = _tpc[0];
else {
v.flip();
_tpc[1] = Ms::transposeTpc(_tpc[0], v, true);
2014-04-17 12:32:10 +02:00
}
2014-04-11 14:50:53 +02:00
}
_headGroup = NoteHead::Group(nval.headGroup);
2012-05-26 14:26:10 +02:00
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// getProperty
//---------------------------------------------------------
QVariant Note::getProperty(P_ID propertyId) const
{
2016-05-12 16:51:42 +02:00
switch (propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::PITCH:
2012-08-10 17:01:35 +02:00
return pitch();
2014-05-26 18:18:01 +02:00
case P_ID::TPC1:
return _tpc[0];
2014-05-26 18:18:01 +02:00
case P_ID::TPC2:
return _tpc[1];
2014-05-26 18:18:01 +02:00
case P_ID::SMALL:
2012-08-10 17:01:35 +02:00
return small();
2014-05-26 18:18:01 +02:00
case P_ID::MIRROR_HEAD:
2014-05-07 18:09:01 +02:00
return int(userMirror());
2014-05-26 18:18:01 +02:00
case P_ID::DOT_POSITION:
return userDotPosition();
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_GROUP:
return int(headGroup());
2014-05-26 18:18:01 +02:00
case P_ID::VELO_OFFSET:
2012-08-10 17:01:35 +02:00
return veloOffset();
2014-05-26 18:18:01 +02:00
case P_ID::TUNING:
2012-08-10 17:01:35 +02:00
return tuning();
2014-05-26 18:18:01 +02:00
case P_ID::FRET:
2012-08-10 17:01:35 +02:00
return fret();
2014-05-26 18:18:01 +02:00
case P_ID::STRING:
2012-08-10 17:01:35 +02:00
return string();
2014-05-26 18:18:01 +02:00
case P_ID::GHOST:
2012-08-10 17:01:35 +02:00
return ghost();
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_TYPE:
return int(headType());
2014-05-26 18:18:01 +02:00
case P_ID::VELO_TYPE:
2014-05-07 18:09:01 +02:00
return int(veloType());
2014-05-26 18:18:01 +02:00
case P_ID::PLAY:
return play();
2014-05-26 18:18:01 +02:00
case P_ID::LINE:
2014-04-22 17:02:03 +02:00
return _line;
case P_ID::FIXED:
return fixed();
case P_ID::FIXED_LINE:
return fixedLine();
2012-08-10 17:01:35 +02:00
default:
break;
}
return Element::getProperty(propertyId);
}
//---------------------------------------------------------
// setProperty
//---------------------------------------------------------
bool Note::setProperty(P_ID propertyId, const QVariant& v)
{
2015-01-05 15:15:48 +01:00
Measure* m = chord() ? chord()->measure() : nullptr;
2012-08-10 17:01:35 +02:00
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::PITCH:
2012-08-10 17:01:35 +02:00
setPitch(v.toInt());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::TPC1:
_tpc[0] = v.toInt();
break;
2014-05-26 18:18:01 +02:00
case P_ID::TPC2:
_tpc[1] = v.toInt();
2014-04-22 17:02:03 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::LINE:
2016-01-04 14:48:58 +01:00
setLine(v.toInt());
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::SMALL:
2012-08-10 17:01:35 +02:00
setSmall(v.toBool());
break;
2014-05-26 18:18:01 +02:00
case P_ID::MIRROR_HEAD:
setUserMirror(MScore::DirectionH(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::DOT_POSITION:
2016-03-02 13:20:19 +01:00
setUserDotPosition(v.value<Direction>());
2016-05-12 16:51:42 +02:00
score()->setLayout(tick());
return true;
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_GROUP:
setHeadGroup(NoteHead::Group(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::VELO_OFFSET:
2012-08-10 17:01:35 +02:00
setVeloOffset(v.toInt());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::TUNING:
2012-08-10 17:01:35 +02:00
setTuning(v.toDouble());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::FRET:
2012-08-10 17:01:35 +02:00
setFret(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::STRING:
2012-08-10 17:01:35 +02:00
setString(v.toInt());
break;
2014-05-26 18:18:01 +02:00
case P_ID::GHOST:
2012-08-10 17:01:35 +02:00
setGhost(v.toBool());
break;
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_TYPE:
setHeadType(NoteHead::Type(v.toInt()));
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::VELO_TYPE:
2014-05-07 18:09:01 +02:00
setVeloType(ValueType(v.toInt()));
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
2012-08-10 17:01:35 +02:00
break;
2014-05-26 18:18:01 +02:00
case P_ID::VISIBLE: { // P_ID::VISIBLE requires reflecting property on dots
setVisible(v.toBool());
int dots = chord()->dots();
for (int i = 0; i < dots; ++i) {
if (_dots[i])
_dots[i]->setVisible(visible());
}
if (m)
m->checkMultiVoices(chord()->staffIdx());
break;
}
2014-05-26 18:18:01 +02:00
case P_ID::PLAY:
setPlay(v.toBool());
2015-01-30 17:03:51 +01:00
score()->setPlaylistDirty();
break;
case P_ID::FIXED:
setFixed(v.toBool());
break;
case P_ID::FIXED_LINE:
setFixedLine(v.toInt());
break;
2012-08-10 17:01:35 +02:00
default:
if (!Element::setProperty(propertyId, v))
return false;
break;
}
2016-06-03 10:17:06 +02:00
score()->setLayout(tick());
2012-08-10 17:01:35 +02:00
return true;
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetFret
//---------------------------------------------------------
void Note::undoSetFret(int val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::FRET, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetString
//---------------------------------------------------------
void Note::undoSetString(int val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::STRING, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetGhost
//---------------------------------------------------------
void Note::undoSetGhost(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::GHOST, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetSmall
//---------------------------------------------------------
void Note::undoSetSmall(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::SMALL, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetPlay
//---------------------------------------------------------
void Note::undoSetPlay(bool val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::PLAY, val);
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetTuning
//---------------------------------------------------------
void Note::undoSetTuning(qreal val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::TUNING, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetVeloType
//---------------------------------------------------------
2014-05-07 18:09:01 +02:00
void Note::undoSetVeloType(ValueType val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::VELO_TYPE, int(val));
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetVeloOffset
//---------------------------------------------------------
void Note::undoSetVeloOffset(int val)
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::VELO_OFFSET, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetUserMirror
//---------------------------------------------------------
void Note::undoSetUserMirror(MScore::DirectionH val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::MIRROR_HEAD, int(val));
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
2014-04-02 20:31:37 +02:00
// undoSetUserDotPosition
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
2016-03-02 13:20:19 +01:00
void Note::undoSetUserDotPosition(Direction val)
2012-08-12 11:44:36 +02:00
{
2016-05-12 16:51:42 +02:00
undoChangeProperty(P_ID::DOT_POSITION, val);
2012-08-12 11:44:36 +02:00
}
//---------------------------------------------------------
// undoSetHeadGroup
//---------------------------------------------------------
void Note::undoSetHeadGroup(NoteHead::Group val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::HEAD_GROUP, int(val));
2012-08-12 11:44:36 +02:00
}
2013-01-28 21:45:36 +01:00
//---------------------------------------------------------
// setHeadType
//---------------------------------------------------------
void Note::setHeadType(NoteHead::Type t)
2013-01-28 21:45:36 +01:00
{
_headType = t;
}
2012-08-12 11:44:36 +02:00
//---------------------------------------------------------
// undoSetHeadType
//---------------------------------------------------------
void Note::undoSetHeadType(NoteHead::Type val)
2012-08-12 11:44:36 +02:00
{
2014-05-26 18:18:01 +02:00
undoChangeProperty(P_ID::HEAD_TYPE, int(val));
2012-08-12 11:44:36 +02:00
}
2012-08-10 17:01:35 +02:00
//---------------------------------------------------------
// propertyDefault
//---------------------------------------------------------
QVariant Note::propertyDefault(P_ID propertyId) const
{
switch(propertyId) {
2014-05-26 18:18:01 +02:00
case P_ID::GHOST:
case P_ID::SMALL:
2012-08-10 17:01:35 +02:00
return false;
2014-05-26 18:18:01 +02:00
case P_ID::MIRROR_HEAD:
return int(MScore::DirectionH::AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::DOT_POSITION:
return Direction(Direction::AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_GROUP:
return int(NoteHead::Group::HEAD_NORMAL);
2014-05-26 18:18:01 +02:00
case P_ID::VELO_OFFSET:
2012-08-10 17:01:35 +02:00
return 0;
2014-05-26 18:18:01 +02:00
case P_ID::TUNING:
2012-08-10 17:01:35 +02:00
return 0.0;
2014-05-26 18:18:01 +02:00
case P_ID::FRET:
case P_ID::STRING:
2012-08-10 17:01:35 +02:00
return -1;
2014-05-26 18:18:01 +02:00
case P_ID::HEAD_TYPE:
return int(NoteHead::Type::HEAD_AUTO);
2014-05-26 18:18:01 +02:00
case P_ID::VELO_TYPE:
2014-05-07 18:09:01 +02:00
return int (ValueType::OFFSET_VAL);
2014-05-26 18:18:01 +02:00
case P_ID::PLAY:
return true;
case P_ID::FIXED:
return false;
case P_ID::FIXED_LINE:
return 0;
2016-11-25 11:46:28 +01:00
case P_ID::TPC2:
return getProperty(P_ID::TPC1);
2012-08-10 17:01:35 +02:00
default:
break;
}
return Element::propertyDefault(propertyId);
2012-08-10 17:01:35 +02:00
}
2012-11-19 09:29:46 +01:00
//---------------------------------------------------------
// setOnTimeOffset
//---------------------------------------------------------
void Note::setOnTimeOffset(int val)
2012-11-19 09:29:46 +01:00
{
_playEvents[0].setOntime(val);
chord()->setPlayEventType(PlayEventType::User);
2012-11-19 09:29:46 +01:00
}
//---------------------------------------------------------
// setOffTimeOffset
//---------------------------------------------------------
void Note::setOffTimeOffset(int val)
2012-11-19 09:29:46 +01:00
{
_playEvents[0].setLen(val - _playEvents[0].ontime());
chord()->setPlayEventType(PlayEventType::User);
2012-11-19 09:29:46 +01:00
}
2013-01-02 20:13:58 +01:00
//---------------------------------------------------------
2013-06-10 11:03:34 +02:00
// setScore
//---------------------------------------------------------
2013-06-10 11:03:34 +02:00
void Note::setScore(Score* s)
{
2013-06-10 11:03:34 +02:00
Element::setScore(s);
if (_tieFor)
_tieFor->setScore(s);
if (_accidental)
_accidental->setScore(s);
for (Element* el : _el)
el->setScore(s);
}
2014-06-26 11:41:18 +02:00
//---------------------------------------------------------
// accessibleInfo
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Note::accessibleInfo() const
{
QString duration = chord()->durationUserName();
QString voice = QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
QString pitchName;
2015-04-12 10:17:00 +02:00
const Drumset* drumset = part()->instrument()->drumset();
if (fixed() && headGroup() == NoteHead::Group::HEAD_SLASH)
pitchName = chord()->noStem() ? QObject::tr("Beat Slash") : QObject::tr("Rhythm Slash");
2016-12-13 13:16:17 +01:00
else if (staff()->isDrumStaff(tick()) && drumset)
2015-04-12 10:17:00 +02:00
pitchName = qApp->translate("drumset", drumset->name(pitch()).toUtf8().constData());
else
pitchName = tpcUserName(false);
return QObject::tr("%1; Pitch: %2; Duration: %3%4").arg(noteTypeUserName()).arg(pitchName).arg(duration).arg((chord()->isGrace() ? "" : QString("; %1").arg(voice)));
}
//---------------------------------------------------------
// screenReaderInfo
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Note::screenReaderInfo() const
{
QString duration = chord()->durationUserName();
QString voice = QObject::tr("Voice: %1").arg(QString::number(track() % VOICES + 1));
QString pitchName;
2015-04-12 10:17:00 +02:00
const Drumset* drumset = part()->instrument()->drumset();
if (fixed() && headGroup() == NoteHead::Group::HEAD_SLASH)
pitchName = chord()->noStem() ? QObject::tr("Beat Slash") : QObject::tr("Rhythm Slash");
2016-12-13 13:16:17 +01:00
else if (staff()->isDrumStaff(tick()) && drumset)
2015-04-12 10:17:00 +02:00
pitchName = qApp->translate("drumset", drumset->name(pitch()).toUtf8().constData());
else
pitchName = tpcUserName(true);
return QString("%1 %2 %3%4").arg(noteTypeUserName()).arg(pitchName).arg(duration).arg((chord()->isGrace() ? "" : QString("; %1").arg(voice)));
}
//---------------------------------------------------------
// accessibleExtraInfo
//---------------------------------------------------------
2016-02-04 17:06:32 +01:00
QString Note::accessibleExtraInfo() const
{
QString rez = "";
if (accidental()) {
rez = QString("%1 %2").arg(rez).arg(accidental()->screenReaderInfo());
}
2016-02-06 22:03:43 +01:00
if (!el().empty()) {
2016-03-19 11:41:38 +01:00
for (Element* e : el()) {
if (!score()->selectionFilter().canSelect(e)) continue;
rez = QString("%1 %2").arg(rez).arg(e->screenReaderInfo());
}
}
if (tieFor())
rez = QObject::tr("%1 Start of %2").arg(rez).arg(tieFor()->screenReaderInfo());
if (tieBack())
rez = QObject::tr("%1 End of %2").arg(rez).arg(tieBack()->screenReaderInfo());
2016-02-06 22:03:43 +01:00
if (!spannerFor().empty()) {
2016-03-19 11:41:38 +01:00
for (Spanner* s : spannerFor()) {
if (!score()->selectionFilter().canSelect(s))
continue;
rez = QObject::tr("%1 Start of %2").arg(rez).arg(s->screenReaderInfo());
}
}
2016-02-06 22:03:43 +01:00
if (!spannerBack().empty()) {
2016-03-19 11:41:38 +01:00
for (Spanner* s : spannerBack()) {
if (!score()->selectionFilter().canSelect(s))
continue;
rez = QObject::tr("%1 End of %2").arg(rez).arg(s->screenReaderInfo());
}
}
rez = QString("%1 %2").arg(rez).arg(chord()->accessibleExtraInfo());
return rez;
}
2014-06-26 11:41:18 +02:00
//---------------------------------------------------------
// noteVal
//---------------------------------------------------------
NoteVal Note::noteVal() const
{
NoteVal nval;
nval.pitch = pitch();
2014-09-22 14:31:28 +02:00
nval.tpc1 = tpc1();
nval.tpc2 = tpc2();
2014-06-26 11:41:18 +02:00
nval.fret = fret();
nval.string = string();
nval.headGroup = headGroup();
return nval;
}
//---------------------------------------------------------
2015-03-19 10:39:18 +01:00
// qmlDotsCount
// returns number of dots for plugins
//---------------------------------------------------------
2016-02-06 11:41:16 +01:00
2015-03-19 10:39:18 +01:00
int Note::qmlDotsCount()
{
2016-02-06 11:41:16 +01:00
return _dots.size();
}
2015-02-19 10:28:25 +01:00
//---------------------------------------------------------
// subtypeName
//---------------------------------------------------------
QString Note::subtypeName() const
{
return NoteHead::group2userName(_headGroup);
}
//---------------------------------------------------------
// nextElement
//---------------------------------------------------------
Element* Note::nextElement()
{
if (chord()->isGrace())
return Element::nextElement();
2016-02-06 22:03:43 +01:00
const std::vector<Note*>& notes = chord()->notes();
if (this == notes.front())
return chord()->nextElement();
2016-02-06 22:03:43 +01:00
auto i = std::find(notes.begin(), notes.end(), this);
return *(i-1);
}
//---------------------------------------------------------
// prevElement
//---------------------------------------------------------
Element* Note::prevElement()
{
if (chord()->isGrace())
return Element::prevElement();
2016-02-06 22:03:43 +01:00
const std::vector<Note*>& notes = chord()->notes();
if (this == notes.back())
return chord()->prevElement();
2016-02-06 22:03:43 +01:00
auto i = std::find(notes.begin(), notes.end(), this);
return *++i;
}
//---------------------------------------------------------
// lastTiedNote
//---------------------------------------------------------
Note* Note::lastTiedNote() const
{
2016-03-19 11:41:38 +01:00
std::vector<Note*> notes;
Note* note = const_cast<Note*>(this);
2016-03-19 11:41:38 +01:00
notes.push_back(note);
while (note->tieFor()) {
2016-03-19 11:41:38 +01:00
if (std::find(notes.begin(), notes.end(), note->tieFor()->endNote()) != notes.end())
break;
if (!note->tieFor()->endNote())
break;
note = note->tieFor()->endNote();
2016-03-19 11:41:38 +01:00
notes.push_back(note);
}
return note;
}
//---------------------------------------------------------
// firstTiedNote
// if note has ties, return last note in chain
// - handle recursion in connected notes
//---------------------------------------------------------
Note* Note::firstTiedNote() const
{
std::vector<const Note*> notes;
const Note* note = this;
2016-03-19 11:41:38 +01:00
notes.push_back(note);
while (note->tieBack()) {
2016-03-19 11:41:38 +01:00
if (std::find(notes.begin(), notes.end(), note->tieBack()->startNote()) != notes.end())
break;
note = note->tieBack()->startNote();
2016-03-19 11:41:38 +01:00
notes.push_back(note);
}
return const_cast<Note*>(note);
}
//---------------------------------------------------------
// tiedNotes
//---------------------------------------------------------
2016-03-19 11:41:38 +01:00
std::vector<Note*> Note::tiedNotes() const
{
2016-03-19 11:41:38 +01:00
std::vector<Note*> notes;
Note* note = firstTiedNote();
2016-03-19 11:41:38 +01:00
notes.push_back(note);
while (note->tieFor()) {
2016-03-19 11:41:38 +01:00
if (std::find(notes.begin(), notes.end(), note->tieFor()->endNote()) != notes.end())
break;
note = note->tieFor()->endNote();
2016-03-19 11:41:38 +01:00
notes.push_back(note);
}
return notes;
}
2015-04-03 18:23:51 +02:00
//---------------------------------------------------------
// accidentalType
//---------------------------------------------------------
AccidentalType Note::accidentalType() const
{
return _accidental ? _accidental->accidentalType() : AccidentalType::NONE;
}
//---------------------------------------------------------
// setAccidentalType
//---------------------------------------------------------
void Note::setAccidentalType(AccidentalType type)
{
2016-03-10 10:41:31 +01:00
if (score())
score()->changeAccidental(this, type);
2015-04-03 18:23:51 +02:00
}
2016-01-04 14:48:58 +01:00
//---------------------------------------------------------
// shape
//---------------------------------------------------------
Shape Note::shape() const
{
Shape shape;
2016-02-06 11:41:16 +01:00
shape.add(symBbox(noteHead()));
2016-01-04 14:48:58 +01:00
for (NoteDot* dot : _dots)
2016-02-06 11:41:16 +01:00
shape.add(symBbox(SymId::augmentationDot).translated(dot->pos()));
2016-01-04 14:48:58 +01:00
if (_accidental)
2016-02-06 11:41:16 +01:00
shape.add(_accidental->bbox().translated(_accidental->pos()));
return shape;
2016-01-04 14:48:58 +01:00
}
2015-04-03 18:23:51 +02:00
}