MuseScore/src/engraving/libmscore/note.h

525 lines
19 KiB
C++

/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __NOTE_H__
#define __NOTE_H__
/**
\file
Definition of classes Note and NoteHead.
*/
#include "containers.h"
#include "engravingitem.h"
#include "symbol.h"
#include "noteevent.h"
#include "pitchspelling.h"
#include "shape.h"
#include "key.h"
#include "iengravingconfiguration.h"
#include "modularity/ioc.h"
namespace mu::engraving {
class Factory;
}
namespace Ms {
class Tie;
class Chord;
class NoteEvent;
class Text;
class Score;
class MuseScoreView;
class Bend;
class AccidentalState;
class Accidental;
class NoteDot;
class Spanner;
class StaffType;
class NoteEditData;
enum class AccidentalType;
static constexpr int MAX_DOTS = 4;
//---------------------------------------------------------
// @@ NoteHead
//---------------------------------------------------------
class NoteHead final : public Symbol
{
public:
NoteHead(Note* parent = 0);
NoteHead(const NoteHead&) = default;
NoteHead& operator=(const NoteHead&) = delete;
NoteHead* clone() const override { return new NoteHead(*this); }
NoteHeadGroup headGroup() const;
};
//---------------------------------------------------------
// NoteVal
/// helper structure
/// \cond PLUGIN_API \private \endcond
//---------------------------------------------------------
struct NoteVal {
int pitch { -1 };
int tpc1 { Tpc::TPC_INVALID };
int tpc2 { Tpc::TPC_INVALID };
int fret { INVALID_FRET_INDEX };
int string { INVALID_STRING_INDEX };
NoteHeadGroup headGroup { NoteHeadGroup::HEAD_NORMAL };
NoteVal() {}
NoteVal(int p)
: pitch(p) {}
};
static const int INVALID_LINE = -10000;
//---------------------------------------------------------------------------------------
// @@ Note
/// Graphic representation of a note.
//
// @P accidental Accidental note accidental (null if none)
// @P accidentalType int note accidental type
// @P dots array[NoteDot] list of note dots (some can be null, read only)
// @P dotsCount int number of note dots (read only)
// @P elements array[EngravingItem] list of elements attached to notehead
// @P fret int fret number in tablature
// @P ghost bool ghost note (guitar: death note)
// @P headScheme enum (NoteHeadScheme.HEAD_AUTO, .HEAD_NORMAL, .HEAD_PITCHNAME, .HEAD_PITCHNAME_GERMAN, .HEAD_SHAPE_NOTE_4, .HEAD_SHAPE_NOTE_7_AIKIN, .HEAD_SHAPE_NOTE_7_FUNK, .HEAD_SHAPE_NOTE_7_WALKER, .HEAD_SOLFEGE, .HEAD_SOLFEGE_FIXED)
// @P headGroup enum (NoteHeadGroup.HEAD_NORMAL, .HEAD_BREVIS_ALT, .HEAD_CROSS, .HEAD_DIAMOND, .HEAD_DO, .HEAD_FA, .HEAD_LA, .HEAD_MI, .HEAD_RE, .HEAD_SLASH, .HEAD_SOL, .HEAD_TI, .HEAD_XCIRCLE, .HEAD_TRIANGLE)
// @P headType enum (NoteHeadType.HEAD_AUTO, .HEAD_BREVIS, .HEAD_HALF, .HEAD_QUARTER, .HEAD_WHOLE)
// @P hidden bool hidden, not played note (read only)
// @P line int notehead position (read only)
// @P mirror bool mirror notehead on x axis (read only)
// @P pitch int midi pitch
// @P play bool play note
// @P ppitch int actual played midi pitch (honoring ottavas) (read only)
// @P isSmall bool small notehead
// @P string int string number in tablature
// @P subchannel int midi subchannel (for midi articulation) (read only)
// @P tieBack Tie note backward tie (null if none, read only)
// @P tieFor Tie note forward tie (null if none, read only)
// @P tpc int tonal pitch class, as per concert pitch setting
// @P tpc1 int tonal pitch class, non transposed
// @P tpc2 int tonal pitch class, transposed
// @P tuning float tuning offset in cent
// @P userDotPosition enum (Direction.AUTO, Direction.DOWN, Direction.UP)
// @P userMirror enum (DirectionH.AUTO, DirectionH.LEFT, DirectionH.RIGHT)
// @P veloOffset int
// @P veloType enum (Note.OFFSET_VAL, Note.USER_VAL)
//---------------------------------------------------------------------------------------
class Note final : public EngravingItem
{
public:
enum class SlideType {
Undefined = 0,
Shift, // connects 2 notes
Legato, // connects 2 notes and adds a slur
Plop, // from up to note
Lift, // from down to note
Doit, // from note to up
Fall, // from note to down
};
struct Slide {
SlideType type { SlideType::Undefined };
Note* startNote = nullptr; // note to start slide (for 2 notes slides)
Note* endNote = nullptr; // note to end slide (for 2 notes slides)
bool isValid() const { return type != SlideType::Undefined; }
bool is(SlideType t) const { return t == type; }
uint32_t slideToNoteLength = 40;
};
enum DisplayFretOption {
Hide = -1,
NoHarmonic,
NaturalHarmonic,
ArtificialHarmonic
};
private:
bool _ghost = false; ///< ghost note
bool _deadNote = false; ///< dead note
bool _hidden = false; ///< marks this note as the hidden one if there are
///< overlapping notes; hidden notes are not played
///< and heads + accidentals are not shown
bool _dotsHidden = false; ///< dots of hidden notes are hidden too
///< except if only one note is dotted
bool _fretConflict = false; ///< used by TAB staves to mark a fretting conflict:
///< two or more notes on the same string
bool dragMode = false;
bool _mirror = false; ///< True if note is mirrored at stem.
bool m_isSmall = false;
bool _play = true; ///< note is not played if false
mutable bool _mark = false; ///< for use in sequencer
bool _fixed = false; ///< for slash notation
DirectionH _userMirror = DirectionH::AUTO; ///< user override of mirror
DirectionV _userDotPosition = DirectionV::AUTO; ///< user override of dot position
NoteHeadScheme _headScheme = NoteHeadScheme::HEAD_AUTO;
NoteHeadGroup _headGroup = NoteHeadGroup::HEAD_NORMAL;
NoteHeadType _headType = NoteHeadType::HEAD_AUTO;
VeloType _veloType = VeloType::OFFSET_VAL;
int _offTimeType = 0; ///< compatibility only 1 - user(absolute), 2 - offset (%)
int _onTimeType = 0; ///< compatibility only 1 - user, 2 - offset
int _subchannel = 0; ///< articulation
int _line = INVALID_LINE; ///< y-Position; 0 - top line.
int _fret = -1; ///< for tablature view
float m_harmonicFret = -1.0;
DisplayFretOption m_displayFret = DisplayFretOption::NoHarmonic;
int _string = -1;
mutable int _tpc[2] = { Tpc::TPC_INVALID, Tpc::TPC_INVALID }; ///< tonal pitch class (concert/transposing)
mutable int _pitch = 0; ///< Note pitch as midi value (0 - 127).
int _veloOffset = 0; ///< velocity user offset in percent, or absolute velocity for this note
int _fixedLine = 0; ///< fixed line number if _fixed == true
qreal _tuning = 0.0; ///< pitch offset in cent, playable only by internal synthesizer
Accidental* _accidental = nullptr;
Tie* _tieFor = nullptr;
Tie* _tieBack = nullptr;
Slide _attachedSlide; ///< slide which starts from note
Slide* _relatedSlide = nullptr; ///< slide which goes to note
Symbol* _leftParenthesis = nullptr;
Symbol* _rightParenthesis = nullptr;
bool _hasHeadParentheses = false;
bool _isHammerOn = false;
bool _harmonic = false;
ElementList _el; ///< fingering, other text, symbols or images
std::vector<NoteDot*> _dots;
NoteEventList _playEvents;
std::vector<Spanner*> _spannerFor;
std::vector<Spanner*> _spannerBack;
SymId _cachedNoteheadSym; // use in draw to avoid recomputing at every update
SymId _cachedSymNull; // additional symbol for some transparent notehead
QString _fretString;
friend class mu::engraving::Factory;
Note(Chord* ch = 0);
Note(const Note&, bool link = false);
void startDrag(EditData&) override;
mu::RectF drag(EditData& ed) override;
void endDrag(EditData&) override;
void editDrag(EditData& editData) override;
void verticalDrag(EditData& ed);
void horizontalDrag(EditData& ed);
void addSpanner(Spanner*);
void removeSpanner(Spanner*);
int concertPitchIdx() const;
void updateRelLine(int relLine, bool undoable);
bool isNoteName() const;
SymId noteHead() const;
void normalizeLeftDragDelta(Segment* seg, EditData& ed, NoteEditData* ned);
static QString tpcUserName(int tpc, int pitch, bool explicitAccidental);
public:
~Note();
Note& operator=(const Note&) = delete;
virtual Note* clone() const override { return new Note(*this, false); }
Chord* chord() const { return (Chord*)explicitParent(); }
void setParent(Chord* ch);
// Score Tree functions
EngravingObject* scanParent() const override;
EngravingObjectList scanChildren() const override;
void undoUnlink() override;
qreal mag() const override;
EngravingItem* elementBase() const override;
void layout() override;
void layout2();
//setter is used only in drumset tools to setup the notehead preview in the drumset editor and the palette
void setCachedNoteheadSym(SymId i) { _cachedNoteheadSym = i; }
void scanElements(void* data, void (* func)(void*, EngravingItem*), bool all = true) override;
void setTrack(track_idx_t val) override;
int playTicks() const;
Fraction playTicksFraction() const;
qreal headWidth() const;
qreal headHeight() const;
qreal tabHeadWidth(const StaffType* tab = 0) const;
qreal tabHeadHeight(const StaffType* tab = 0) const;
mu::PointF stemDownNW() const;
mu::PointF stemUpSE() const;
qreal bboxXShift() const;
qreal noteheadCenterX() const;
qreal bboxRightPos() const;
qreal headBodyWidth() const;
qreal outsideTieAttachX(bool up) const;
NoteHeadScheme headScheme() const { return _headScheme; }
void updateHeadGroup(const NoteHeadGroup headGroup);
NoteHeadGroup headGroup() const { return _headGroup; }
NoteHeadType headType() const { return _headType; }
void setHeadScheme(NoteHeadScheme val);
void setHeadGroup(NoteHeadGroup val);
void setHeadType(NoteHeadType t);
int subtype() const override { return int(_headGroup); }
QString subtypeName() const override;
void setPitch(int val);
void setPitch(int pitch, int tpc1, int tpc2);
int pitch() const { return _pitch; }
int ottaveCapoFret() const;
int ppitch() const; ///< playback pitch
int epitch() const; ///< effective pitch
int octave() const;
int playingOctave() const;
qreal tuning() const { return _tuning; }
void setTuning(qreal v) { _tuning = v; }
void undoSetTpc(int v);
int transposition() const;
bool fixed() const { return _fixed; }
void setFixed(bool v) { _fixed = v; }
int fixedLine() const { return _fixedLine; }
void setFixedLine(int v) { _fixedLine = v; }
int tpc() const;
int tpc1() const { return _tpc[0]; } // non transposed tpc
int tpc2() const { return _tpc[1]; } // transposed tpc
QString tpcUserName(bool explicitAccidental = false) const;
void setTpc(int v);
void setTpc1(int v) { _tpc[0] = v; }
void setTpc2(int v) { _tpc[1] = v; }
void setTpcFromPitch();
int tpc1default(int pitch) const;
int tpc2default(int pitch) const;
int transposeTpc(int tpc) const;
int playingTpc() const;
Accidental* accidental() const { return _accidental; }
void setAccidental(Accidental* a) { _accidental = a; }
AccidentalType accidentalType() const;
void setAccidentalType(AccidentalType type);
int line() const;
void setLine(int n) { _line = n; }
int fret() const { return _fret; }
void setFret(int val) { _fret = val; }
float harmonicFret() const { return m_harmonicFret; }
void setHarmonicFret(float val) { m_harmonicFret = val; }
DisplayFretOption displayFret() const { return m_displayFret; }
void setDisplayFret(DisplayFretOption val) { m_displayFret = val; }
int string() const { return _string; }
void setString(int val);
bool ghost() const { return _ghost; }
void setGhost(bool val) { _ghost = val; }
bool deadNote() const { return _deadNote; }
void setDeadNote(bool deadNote) { _deadNote = deadNote; }
bool fretConflict() const { return _fretConflict; }
void setFretConflict(bool val) { _fretConflict = val; }
void add(EngravingItem*) override;
void remove(EngravingItem*) override;
bool mirror() const { return _mirror; }
void setMirror(bool val) { _mirror = val; }
bool isSmall() const { return m_isSmall; }
void setSmall(bool val);
bool play() const { return _play; }
void setPlay(bool val) { _play = val; }
Ms::Tie* tieFor() const { return _tieFor; }
Ms::Tie* tieBack() const { return _tieBack; }
void setTieFor(Tie* t) { _tieFor = t; }
void setTieBack(Tie* t) { _tieBack = t; }
Note* firstTiedNote() const;
const Note* lastTiedNote() const;
Note* lastTiedNote() { return const_cast<Note*>(static_cast<const Note*>(this)->lastTiedNote()); }
int unisonIndex() const;
void disconnectTiedNotes();
void connectTiedNotes();
void draw(mu::draw::Painter*) const override;
void read(XmlReader&) override;
bool readProperties(XmlReader&) override;
void readAddConnector(ConnectorInfoReader* info, bool pasteMode) override;
void write(XmlWriter&) const override;
bool acceptDrop(EditData&) const override;
EngravingItem* drop(EditData&) override;
bool hidden() const { return _hidden; }
void setHidden(bool val) { _hidden = val; }
bool dotsHidden() const { return _dotsHidden; }
void setDotsHidden(bool val) { _dotsHidden = val; }
NoteType noteType() const;
QString noteTypeUserName() const;
ElementList& el() { return _el; }
const ElementList& el() const { return _el; }
int subchannel() const { return _subchannel; }
void setSubchannel(int val) { _subchannel = val; }
DirectionH userMirror() const { return _userMirror; }
void setUserMirror(DirectionH d) { _userMirror = d; }
DirectionV userDotPosition() const { return _userDotPosition; }
void setUserDotPosition(DirectionV d) { _userDotPosition = d; }
bool dotIsUp() const; // actual dot position
void reset() override;
VeloType veloType() const { return _veloType; }
void setVeloType(VeloType v) { _veloType = v; }
int veloOffset() const { return _veloOffset; }
void setVeloOffset(int v) { _veloOffset = v; }
void setOnTimeOffset(int v);
void setOffTimeOffset(int v);
int customizeVelocity(int velo) const;
NoteDot* dot(int n) { return _dots[n]; }
const std::vector<NoteDot*>& dots() const { return _dots; }
std::vector<NoteDot*>& dots() { return _dots; }
int qmlDotsCount();
void updateAccidental(AccidentalState*);
void updateLine();
void setNval(const NoteVal&, Fraction = { -1, 1 });
NoteEventList& playEvents() { return _playEvents; }
const NoteEventList& playEvents() const { return _playEvents; }
NoteEvent* noteEvent(int idx) { return &_playEvents[idx]; }
void setPlayEvents(const NoteEventList& l) { _playEvents = l; }
const std::vector<Spanner*>& spannerFor() const { return _spannerFor; }
const std::vector<Spanner*>& spannerBack() const { return _spannerBack; }
void addSpannerBack(Spanner* e)
{
if (!mu::contains(_spannerBack, e)) {
_spannerBack.push_back(e);
}
}
bool removeSpannerBack(Spanner* e) { return mu::remove(_spannerBack, e); }
void addSpannerFor(Spanner* e)
{
if (!mu::contains(_spannerFor, e)) {
_spannerFor.push_back(e);
}
}
bool removeSpannerFor(Spanner* e) { return mu::remove(_spannerFor, e); }
void transposeDiatonic(int interval, bool keepAlterations, bool useDoubleAccidentals);
void localSpatiumChanged(qreal oldValue, qreal newValue) override;
mu::engraving::PropertyValue getProperty(Pid propertyId) const override;
bool setProperty(Pid propertyId, const mu::engraving::PropertyValue&) override;
mu::engraving::PropertyValue propertyDefault(Pid) const override;
bool mark() const { return _mark; }
void setMark(bool v) const { _mark = v; }
void setScore(Score* s) override;
void setDotY(DirectionV);
void setHeadHasParentheses(bool hasParentheses);
static SymId noteHead(int direction, NoteHeadGroup, NoteHeadType, int tpc, Key key, NoteHeadScheme scheme);
static SymId noteHead(int direction, NoteHeadGroup, NoteHeadType);
NoteVal noteVal() const;
EngravingItem* nextInEl(EngravingItem* e);
EngravingItem* prevInEl(EngravingItem* e);
EngravingItem* nextElement() override;
EngravingItem* prevElement() override;
virtual EngravingItem* lastElementBeforeSegment();
EngravingItem* nextSegmentElement() override;
EngravingItem* prevSegmentElement() override;
QString accessibleInfo() const override;
QString screenReaderInfo() const override;
QString accessibleExtraInfo() const override;
Shape shape() const override;
std::vector<Note*> tiedNotes() const;
void setOffTimeType(int v) { _offTimeType = v; }
void setOnTimeType(int v) { _onTimeType = v; }
int offTimeType() const { return _offTimeType; }
int onTimeType() const { return _onTimeType; }
const Slide& slide() const { return _attachedSlide; }
void attachSlide(const Slide& s) { _attachedSlide = s; }
void setRelatedSlide(Slide* pSlide) { _relatedSlide = pSlide; }
bool hasRelatedSlide() const { return !!_relatedSlide; }
const Slide& relatedSlide() const { return *_relatedSlide; }
bool isSlideToNote() const;
bool isSlideOutNote() const;
bool isSlideStart() const;
bool isSlideEnd() const;
void relateSlide(Note& start) { _relatedSlide = &start._attachedSlide; }
bool isHammerOn() const { return _isHammerOn; }
void setIsHammerOn(bool hammerOn) { _isHammerOn = hammerOn; }
void setHarmonic(bool val) { _harmonic = val; }
bool harmonic() const { return _harmonic; }
};
} // namespace Ms
#endif