Skip to content

Commit

Permalink
Plugin API: add tuplets support
Browse files Browse the repository at this point in the history
  • Loading branch information
dmitrio95 committed May 16, 2020
1 parent 87c911d commit 41abc27
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 15 deletions.
6 changes: 0 additions & 6 deletions libmscore/duration.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,6 @@ class DurationElement : public Element {
Fraction _duration;
Tuplet* _tuplet;

// #ifdef SCRIPT_INTERFACE
// void setDurationW(FractionWrapper* f) { _duration = f->fraction(); }
// FractionWrapper* durationW() const { return new FractionWrapper(_duration); }
// FractionWrapper* globalDurW() const { return new FractionWrapper(globalDuration()); }
// #endif

public:
DurationElement(Score* = 0, ElementFlags = ElementFlag::MOVABLE | ElementFlag::ON_STAFF);
DurationElement(const DurationElement& e);
Expand Down
81 changes: 81 additions & 0 deletions mscore/plugin/api/cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "libmscore/system.h"
#include "libmscore/segment.h"
#include "libmscore/timesig.h"
#include "libmscore/tuplet.h"

namespace Ms {
namespace PluginAPI {
Expand Down Expand Up @@ -407,6 +408,86 @@ void Cursor::addRest()
_score->enterRest(inputState().duration(), is.get());
}

//---------------------------------------------------------
// addTuplet
/// \brief Adds a tuplet to the current cursor position.
/// \details This function provides a possibility to setup
/// the tuplet's ratio to any value (similarly to
/// Add > Tuplets > Other... dialog in MuseScore).
///
/// Examples of most typical usage:
/// \code
/// // add a triplet of three eighth notes
/// cursor.addTuplet(fraction(3, 2), fraction(1, 4));
///
/// // add a quintuplet in place of the current chord/rest
/// var cr = cursor.element;
/// if (cr)
/// cursor.addTuplet(fraction(5, 4), cr.duration);
/// \endcode
///
/// \param ratio tuplet ratio. Numerator represents
/// actual number of notes in this tuplet, denominator is
/// a number of "normal" notes which correspond to the
/// same total duration. For example, a triplet has a
/// ratio of 3/2 as it has 3 notes fitting to the
/// duration which would normally be occupied by 2 notes
/// of the same nominal length.
/// \param duration total duration of the tuplet. To
/// create a tuplet with duration matching to duration of
/// existing chord or rest, use its
/// \ref DurationElement.duration "duration" value as
/// a parameter.
/// \since MuseScore 3.5
/// \see \ref DurationElement.tuplet
//---------------------------------------------------------

void Cursor::addTuplet(FractionWrapper* ratio, FractionWrapper* duration)
{
if (!segment()) {
qWarning("Cursor::addTuplet: cursor location is undefined, use rewind() to define its location");
return;
}

const Ms::Fraction fRatio = ratio->fraction();
const Ms::Fraction fDuration = duration->fraction();

if (!fRatio.isValid() || fRatio.isZero() || fRatio.negative()
|| !fDuration.isValid() || fDuration.isZero() || fDuration.negative()) {
qWarning("Cursor::addTuplet: invalid parameter values: %s, %s", qPrintable(fRatio.toString()), qPrintable(fDuration.toString()));
return;
}

const Ms::Fraction baseLen = fDuration * Fraction(1, fRatio.denominator());
if (!TDuration::isValid(baseLen)) {
qWarning("Cursor::addTuplet: cannot create tuplet for ratio %s and duration %s", qPrintable(fRatio.toString()), qPrintable(fDuration.toString()));
return;
}

_score->expandVoice(inputState().segment(), inputState().track());
Ms::ChordRest* cr = inputState().cr();
if (!cr) // shouldn't happen?
return;

_score->changeCRlen(cr, fDuration);

Ms::Measure* tupletMeasure = segment()->measure();
const Ms::Fraction tupletTick = segment()->tick();

Ms::Tuplet* tuplet = new Ms::Tuplet(_score);
tuplet->setParent(tupletMeasure);
tuplet->setTrack(track());
tuplet->setTick(tupletTick);
tuplet->setRatio(fRatio);
tuplet->setTicks(fDuration);
tuplet->setBaseLen(baseLen);

_score->cmdCreateTuplet(cr, tuplet);

inputState().setSegment(tupletMeasure->tick2segment(tupletTick));
inputState().setDuration(baseLen);
}

//---------------------------------------------------------
// setDuration
/// Set duration of the notes added by the cursor.
Expand Down
3 changes: 3 additions & 0 deletions mscore/plugin/api/cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#ifndef __CURSOR_H__
#define __CURSOR_H__

#include "fraction.h"

namespace Ms {

class Element;
Expand Down Expand Up @@ -183,6 +185,7 @@ class Cursor : public QObject {

Q_INVOKABLE void addNote(int pitch, bool addToChord = false);
Q_INVOKABLE void addRest();
Q_INVOKABLE void addTuplet(Ms::PluginAPI::FractionWrapper* ratio, Ms::PluginAPI::FractionWrapper* duration);

//@ set duration
//@ z: numerator
Expand Down
32 changes: 32 additions & 0 deletions mscore/plugin/api/elements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//=============================================================================

#include "elements.h"
#include "fraction.h"
#include "libmscore/property.h"
#include "libmscore/undo.h"

Expand Down Expand Up @@ -166,6 +167,33 @@ void Note::remove(Ms::PluginAPI::Element* wrapped)
qDebug("Note::remove() not impl. %s", s->name());
}

//---------------------------------------------------------
// DurationElement::globalDuration
//---------------------------------------------------------

FractionWrapper* DurationElement::globalDuration() const
{
return wrap(durationElement()->globalTicks());
}

//---------------------------------------------------------
// DurationElement::actualDuration
//---------------------------------------------------------

FractionWrapper* DurationElement::actualDuration() const
{
return wrap(durationElement()->actualTicks());
}

//---------------------------------------------------------
// DurationElement::parentTuplet
//---------------------------------------------------------

Tuplet* DurationElement::parentTuplet()
{
return wrap<Tuplet>(durationElement()->tuplet());
}

//---------------------------------------------------------
// Chord::setPlayEventType
//---------------------------------------------------------
Expand Down Expand Up @@ -264,13 +292,17 @@ Element* wrap(Ms::Element* e, Ownership own)
return wrap<Note>(toNote(e), own);
case ElementType::CHORD:
return wrap<Chord>(toChord(e), own);
case ElementType::TUPLET:
return wrap<Tuplet>(toTuplet(e), own);
case ElementType::SEGMENT:
return wrap<Segment>(toSegment(e), own);
case ElementType::MEASURE:
return wrap<Measure>(toMeasure(e), own);
case ElementType::PAGE:
return wrap<Page>(toPage(e), own);
default:
if (e->isDurationElement())
return wrap<DurationElement>(toDurationElement(e), own);
break;
}
return wrap<Element>(e, own);
Expand Down
96 changes: 87 additions & 9 deletions mscore/plugin/api/elements.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "libmscore/notedot.h"
#include "libmscore/page.h"
#include "libmscore/segment.h"
#include "libmscore/tuplet.h"
#include "libmscore/accidental.h"
#include "libmscore/musescoreCore.h"
#include "libmscore/score.h"
Expand All @@ -32,7 +33,9 @@
namespace Ms {
namespace PluginAPI {

class FractionWrapper;
class Element;
class Tuplet;
class Tie;
extern Tie* tieWrap(Ms::Tie* tie);

Expand Down Expand Up @@ -181,12 +184,6 @@ class Element : public Ms::PluginAPI::ScoreElement {
API_PROPERTY( play, PLAY )
API_PROPERTY( timesigNominal, TIMESIG_NOMINAL )
API_PROPERTY( timesigActual, TIMESIG_ACTUAL )
API_PROPERTY( numberType, NUMBER_TYPE )
API_PROPERTY( bracketType, BRACKET_TYPE )
API_PROPERTY( normalNotes, NORMAL_NOTES )
API_PROPERTY( actualNotes, ACTUAL_NOTES )
API_PROPERTY( p1, P1 )
API_PROPERTY( p2, P2 )
API_PROPERTY( growLeft, GROW_LEFT )
API_PROPERTY( growRight, GROW_RIGHT )
API_PROPERTY( boxHeight, BOX_HEIGHT )
Expand Down Expand Up @@ -284,7 +281,6 @@ class Element : public Ms::PluginAPI::ScoreElement {
API_PROPERTY( lineVisible, LINE_VISIBLE )
API_PROPERTY( mag, MAG )
API_PROPERTY( useDrumset, USE_DRUMSET )
API_PROPERTY( duration, DURATION )
API_PROPERTY( durationType, DURATION_TYPE )
API_PROPERTY( role, ROLE )
API_PROPERTY_T( int, track, TRACK )
Expand Down Expand Up @@ -520,12 +516,94 @@ class Note : public Element {
Q_INVOKABLE void remove(Ms::PluginAPI::Element* wrapped);
};

//---------------------------------------------------------
// DurationElement
//---------------------------------------------------------

class DurationElement : public Element {
Q_OBJECT

/**
* Nominal duration of this element.
* The duration is represented as a fraction of whole note length.
*/
API_PROPERTY_READ_ONLY( duration, DURATION )
/**
* Global duration of this element, taking into account ratio of
* parent tuplets if there are any.
* \since MuseScore 3.5
*/
Q_PROPERTY(Ms::PluginAPI::FractionWrapper* globalDuration READ globalDuration)
/**
* Actual duration of this element, taking into account ratio of
* parent tuplets and local time signatures if there are any.
* \since MuseScore 3.5
*/
Q_PROPERTY(Ms::PluginAPI::FractionWrapper* actualDuration READ actualDuration)
/**
* Tuplet which this element belongs to. If there is no parent tuplet, returns null.
* \since MuseScore 3.5
*/
Q_PROPERTY(Ms::PluginAPI::Tuplet* tuplet READ parentTuplet)

public:
/// \cond MS_INTERNAL
DurationElement(Ms::DurationElement* de = nullptr, Ownership own = Ownership::PLUGIN)
: Element(de, own) {}

Ms::DurationElement* durationElement() { return toDurationElement(e); }
const Ms::DurationElement* durationElement() const { return toDurationElement(e); }

FractionWrapper* globalDuration() const;
FractionWrapper* actualDuration() const;

Tuplet* parentTuplet();
/// \endcond
};

//---------------------------------------------------------
// Tuplet
//---------------------------------------------------------

class Tuplet : public DurationElement {
Q_OBJECT

API_PROPERTY( numberType, NUMBER_TYPE )
API_PROPERTY( bracketType, BRACKET_TYPE )
/** Actual number of notes of base nominal length in this tuplet. */
API_PROPERTY_READ_ONLY_T( int, actualNotes, ACTUAL_NOTES )
/**
* Number of "normal" notes of base nominal length which correspond
* to this tuplet's duration.
*/
API_PROPERTY_READ_ONLY_T( int, normalNotes, NORMAL_NOTES )
API_PROPERTY( p1, P1 )
API_PROPERTY( p2, P2 )

/**
* List of elements which belong to this tuplet.
* \since MuseScore 3.5
*/
Q_PROPERTY(QQmlListProperty<Ms::PluginAPI::Element> elements READ elements)

public:
/// \cond MS_INTERNAL
Tuplet(Ms::Tuplet* t = nullptr, Ownership own = Ownership::PLUGIN)
: DurationElement(t, own) {}

Ms::Tuplet* tuplet() { return toTuplet(e); }
const Ms::Tuplet* tuplet() const { return toTuplet(e); }

QQmlListProperty<Element> elements() { return wrapContainerProperty<Element>(this, tuplet()->elements()); }
/// \endcond
};

//---------------------------------------------------------
// Chord
// Chord wrapper
//---------------------------------------------------------

class Chord : public Element {
class Chord : public DurationElement {
Q_OBJECT
Q_PROPERTY(QQmlListProperty<Ms::PluginAPI::Chord> graceNotes READ graceNotes)
Q_PROPERTY(QQmlListProperty<Ms::PluginAPI::Note> notes READ notes )
Expand All @@ -544,7 +622,7 @@ class Chord : public Element {
public:
/// \cond MS_INTERNAL
Chord(Ms::Chord* c = nullptr, Ownership own = Ownership::PLUGIN)
: Element(c, own) {}
: DurationElement(c, own) {}

Ms::Chord* chord() { return toChord(e); }
const Ms::Chord* chord() const { return toChord(e); }
Expand Down

0 comments on commit 41abc27

Please sign in to comment.