Merge pull request #17824 from miiizen/harp-pedal-gliss
Added option for harp pedal diagram glissando
This commit is contained in:
commit
9db8ed18f6
|
@ -37,8 +37,10 @@ NICE-TO-HAVE TODO:
|
|||
#include "types/typesconv.h"
|
||||
|
||||
#include "chord.h"
|
||||
#include "harppedaldiagram.h"
|
||||
#include "measure.h"
|
||||
#include "note.h"
|
||||
#include "part.h"
|
||||
#include "score.h"
|
||||
#include "segment.h"
|
||||
#include "staff.h"
|
||||
|
@ -57,6 +59,8 @@ static const ElementStyle glissandoElementStyle {
|
|||
{ Sid::glissandoFontStyle, Pid::FONT_STYLE },
|
||||
{ Sid::glissandoLineWidth, Pid::LINE_WIDTH },
|
||||
{ Sid::glissandoText, Pid::GLISS_TEXT },
|
||||
{ Sid::glissandoStyle, Pid::GLISS_STYLE },
|
||||
{ Sid::glissandoStyleHarp, Pid::GLISS_STYLE }
|
||||
};
|
||||
|
||||
//=========================================================
|
||||
|
@ -129,6 +133,7 @@ Glissando::Glissando(const Glissando& g)
|
|||
_showText = g._showText;
|
||||
_playGlissando = g._playGlissando;
|
||||
_fontStyle = g._fontStyle;
|
||||
m_isHarpGliss = g.m_isHarpGliss;
|
||||
}
|
||||
|
||||
const TranslatableString& Glissando::glissandoTypeName() const
|
||||
|
@ -148,6 +153,15 @@ LineSegment* Glissando::createLineSegment(System* parent)
|
|||
return seg;
|
||||
}
|
||||
|
||||
Sid Glissando::getPropertyStyle(Pid id) const
|
||||
{
|
||||
if (id == Pid::GLISS_STYLE) {
|
||||
return isHarpGliss().value_or(false) ? Sid::glissandoStyleHarp : Sid::glissandoStyle;
|
||||
}
|
||||
|
||||
return SLine::getPropertyStyle(id);
|
||||
}
|
||||
|
||||
void Glissando::addLineAttachPoints()
|
||||
{
|
||||
GlissandoSegment* frontSeg = toGlissandoSegment(frontSegment());
|
||||
|
@ -195,6 +209,41 @@ bool Glissando::pitchSteps(const Spanner* spanner, std::vector<int>& pitchOffset
|
|||
int direction = pitchEnd > pitchStart ? 1 : -1;
|
||||
pitchOffsets.clear();
|
||||
if (glissandoStyle == GlissandoStyle::DIATONIC) {
|
||||
// Obey harp pedal diagrams if on a harp staff
|
||||
if (glissando->isHarpGliss().value_or(false)) {
|
||||
HarpPedalDiagram* hd = spanner->part()->currentHarpDiagram(spanner->tick());
|
||||
std::set<int> playableTpcs = hd ? hd->playableTpcs() : std::set<int>({ 14, 16, 18, 13, 15, 17, 19 });
|
||||
std::vector<int> playablePitches;
|
||||
for (int t : playableTpcs) {
|
||||
playablePitches.push_back(tpc2pitch(t) % PITCH_DELTA_OCTAVE);
|
||||
}
|
||||
|
||||
// Push starting note, then check for enharmonic on the next string. If there is an enharmonic, 0 will be pushed back twice
|
||||
pitchOffsets.push_back(0);
|
||||
int en = noteStart->tpc() + TPC_DELTA_ENHARMONIC * -direction;
|
||||
// Harp pedalling will only have 1 flat or sharp
|
||||
if (en >= TPC_F_B && en <= TPC_B_S && playableTpcs.find(en) != playableTpcs.end()) {
|
||||
pitchOffsets.push_back(0);
|
||||
}
|
||||
|
||||
for (int p = pitchStart + direction; p != pitchEnd; p += direction) {
|
||||
// Count times pitch occurs in harp pedalling - this accounts for enharmonics
|
||||
int pitchOccurrences = std::count(playablePitches.begin(), playablePitches.end(), p % PITCH_DELTA_OCTAVE);
|
||||
if (pitchOccurrences > 0) {
|
||||
pitchOffsets.insert(pitchOffsets.end(), pitchOccurrences, p - pitchStart);
|
||||
}
|
||||
}
|
||||
|
||||
// Check for enharmonic at end, in correct direction
|
||||
en = noteEnd->tpc() + TPC_DELTA_ENHARMONIC * direction;
|
||||
if (en >= TPC_F_B && en <= TPC_B_S && playableTpcs.find(en) != playableTpcs.end()) {
|
||||
pitchOffsets.push_back(pitchEnd - pitchStart);
|
||||
}
|
||||
|
||||
return pitchOffsets.size() > 0;
|
||||
}
|
||||
|
||||
// Regular diatonic mode
|
||||
int lineStart = noteStart->line();
|
||||
// scale obeying accidentals
|
||||
for (int line = lineStart, pitch = pitchStart; (direction == 1) ? (pitch < pitchEnd) : (pitch > pitchEnd); line -= direction) {
|
||||
|
@ -448,8 +497,15 @@ bool Glissando::setProperty(Pid propertyId, const PropertyValue& v)
|
|||
setShowText(v.toBool());
|
||||
break;
|
||||
case Pid::GLISS_STYLE:
|
||||
setGlissandoStyle(v.value<GlissandoStyle>());
|
||||
{
|
||||
// Make sure harp glisses can only be diatonic and chromatic
|
||||
GlissandoStyle glissStyle = v.value<GlissandoStyle>();
|
||||
if (isHarpGliss().value_or(false) && (glissStyle != GlissandoStyle::DIATONIC && glissStyle != GlissandoStyle::CHROMATIC)) {
|
||||
glissStyle = GlissandoStyle::DIATONIC;
|
||||
}
|
||||
setGlissandoStyle(glissStyle);
|
||||
break;
|
||||
}
|
||||
case Pid::GLISS_SHIFT:
|
||||
setGlissandoShift(v.toBool());
|
||||
break;
|
||||
|
@ -493,7 +549,7 @@ PropertyValue Glissando::propertyDefault(Pid propertyId) const
|
|||
case Pid::GLISS_SHOW_TEXT:
|
||||
return true;
|
||||
case Pid::GLISS_STYLE:
|
||||
return GlissandoStyle::CHROMATIC;
|
||||
return style().styleV(getPropertyStyle(propertyId));
|
||||
case Pid::GLISS_SHIFT:
|
||||
return false;
|
||||
case Pid::GLISS_EASEIN:
|
||||
|
|
|
@ -76,6 +76,8 @@ class Glissando final : public SLine
|
|||
M_PROPERTY(int, easeIn, setEaseIn)
|
||||
M_PROPERTY(int, easeOut, setEaseOut)
|
||||
|
||||
std::optional<bool> m_isHarpGliss = std::nullopt;
|
||||
|
||||
public:
|
||||
static constexpr double GLISS_PALETTE_WIDTH = 4.0;
|
||||
static constexpr double GLISS_PALETTE_HEIGHT = 4.0;
|
||||
|
@ -88,12 +90,16 @@ public:
|
|||
|
||||
const TranslatableString& glissandoTypeName() const;
|
||||
|
||||
std::optional<bool> isHarpGliss() const { return m_isHarpGliss; }
|
||||
void setIsHarpGliss(std::optional<bool> v) { m_isHarpGliss = v; }
|
||||
|
||||
// overridden inherited methods
|
||||
Glissando* clone() const override { return new Glissando(*this); }
|
||||
|
||||
LineSegment* createLineSegment(System* parent) override;
|
||||
|
||||
// property/style methods
|
||||
Sid getPropertyStyle(Pid id) const override;
|
||||
PropertyValue getProperty(Pid propertyId) const override;
|
||||
bool setProperty(Pid propertyId, const PropertyValue&) override;
|
||||
PropertyValue propertyDefault(Pid) const override;
|
||||
|
|
|
@ -82,6 +82,7 @@ public:
|
|||
void updateDiagramText();
|
||||
|
||||
bool isTpcPlayable(int tpc);
|
||||
const std::set<int>& playableTpcs() const { return m_playableTpcs; }
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -724,7 +724,7 @@ HarpPedalDiagram* Part::currentHarpDiagram(const Fraction& tick) const
|
|||
|
||||
HarpPedalDiagram* Part::nextHarpDiagram(const Fraction& tick) const
|
||||
{
|
||||
auto i = harpDiagrams.lower_bound(tick.ticks());
|
||||
auto i = harpDiagrams.upper_bound(tick.ticks());
|
||||
return (i == harpDiagrams.end()) ? nullptr : i->second;
|
||||
}
|
||||
|
||||
|
|
|
@ -3057,6 +3057,23 @@ void ChangeHarpPedalState::flip(EditData*)
|
|||
diagram->triggerLayout();
|
||||
}
|
||||
|
||||
std::vector<const EngravingObject*> ChangeHarpPedalState::objectItems() const
|
||||
{
|
||||
Part* part = diagram->part();
|
||||
std::vector<const EngravingObject*> objs{ diagram };
|
||||
if (!part) {
|
||||
return objs;
|
||||
}
|
||||
|
||||
HarpPedalDiagram* nextDiagram = part->nextHarpDiagram(diagram->tick());
|
||||
if (nextDiagram) {
|
||||
objs.push_back(nextDiagram);
|
||||
} else {
|
||||
objs.push_back(diagram->score()->lastElement());
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
void ChangeSingleHarpPedal::flip(EditData*)
|
||||
{
|
||||
HarpStringType f_type = type;
|
||||
|
@ -3068,8 +3085,26 @@ void ChangeSingleHarpPedal::flip(EditData*)
|
|||
diagram->setPedal(type, pos);
|
||||
type = f_type;
|
||||
pos = f_pos;
|
||||
|
||||
diagram->triggerLayout();
|
||||
}
|
||||
|
||||
std::vector<const EngravingObject*> ChangeSingleHarpPedal::objectItems() const
|
||||
{
|
||||
Part* part = diagram->part();
|
||||
std::vector<const EngravingObject*> objs{ diagram };
|
||||
if (!part) {
|
||||
return objs;
|
||||
}
|
||||
|
||||
HarpPedalDiagram* nextDiagram = part->nextHarpDiagram(diagram->tick());
|
||||
if (nextDiagram) {
|
||||
objs.push_back(nextDiagram);
|
||||
} else {
|
||||
objs.push_back(diagram->score()->lastElement());
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
}
|
||||
|
||||
void ChangeStringData::flip(EditData*)
|
||||
|
|
|
@ -1592,15 +1592,19 @@ public:
|
|||
|
||||
class ChangeHarpPedalState : public UndoCommand
|
||||
{
|
||||
OBJECT_ALLOCATOR(engraving, ChangeHarpPedalState)
|
||||
HarpPedalDiagram* diagram;
|
||||
std::array<PedalPosition, HARP_STRING_NO> pedalState;
|
||||
|
||||
void flip(EditData*) override;
|
||||
|
||||
public:
|
||||
ChangeHarpPedalState(HarpPedalDiagram* _diagram, std::array<PedalPosition, HARP_STRING_NO> _pedalState)
|
||||
: diagram(_diagram), pedalState(_pedalState) {}
|
||||
void flip(EditData*) override;
|
||||
|
||||
UNDO_NAME("ChangeHarpPedalState")
|
||||
UNDO_CHANGED_OBJECTS({ diagram });
|
||||
// UNDO_CHANGED_OBJECTS({ diagram })
|
||||
std::vector<const EngravingObject*> objectItems() const override;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------
|
||||
|
@ -1609,10 +1613,14 @@ public:
|
|||
|
||||
class ChangeSingleHarpPedal : public UndoCommand
|
||||
{
|
||||
OBJECT_ALLOCATOR(engraving, ChangeSingleHarpPedal)
|
||||
|
||||
HarpPedalDiagram* diagram;
|
||||
HarpStringType type;
|
||||
PedalPosition pos;
|
||||
|
||||
void flip(EditData*) override;
|
||||
|
||||
public:
|
||||
ChangeSingleHarpPedal(HarpPedalDiagram* _diagram, HarpStringType _type, PedalPosition _pos)
|
||||
: diagram(_diagram),
|
||||
|
@ -1621,9 +1629,9 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
void flip(EditData*) override;
|
||||
UNDO_NAME("ChangeSingleHarpPedal")
|
||||
UNDO_CHANGED_OBJECTS({ diagram });
|
||||
// UNDO_CHANGED_OBJECTS({ diagram });
|
||||
std::vector<const EngravingObject*> objectItems() const override;
|
||||
};
|
||||
|
||||
class ChangeStringData : public UndoCommand
|
||||
|
|
|
@ -2530,7 +2530,7 @@ void TLayout::layoutFretDiagram(const FretDiagram* item, FretDiagram::LayoutData
|
|||
}
|
||||
}
|
||||
|
||||
static void _layoutGlissando(const Glissando* item, LayoutContext& ctx, Glissando::LayoutData* ldata)
|
||||
static void _layoutGlissando(Glissando* item, LayoutContext& ctx, Glissando::LayoutData* ldata)
|
||||
{
|
||||
double _spatium = item->spatium();
|
||||
|
||||
|
@ -2542,6 +2542,32 @@ static void _layoutGlissando(const Glissando* item, LayoutContext& ctx, Glissand
|
|||
}
|
||||
ldata->setPos(0.0, 0.0);
|
||||
|
||||
String instrId = item->staff()->part()->instrumentId(item->tick());
|
||||
bool harpStaff = instrId == "harp";
|
||||
if (!item->isHarpGliss().has_value()) {
|
||||
item->setIsHarpGliss(harpStaff);
|
||||
} else {
|
||||
if (harpStaff != item->isHarpGliss().value()) {
|
||||
// Preserve whether this gliss has its default playback style
|
||||
bool defaultStyle = false;
|
||||
if (item->isStyled(Pid::GLISS_STYLE)) {
|
||||
defaultStyle = true;
|
||||
}
|
||||
item->setIsHarpGliss(harpStaff);
|
||||
if (defaultStyle) {
|
||||
item->resetProperty(Pid::GLISS_STYLE);
|
||||
}
|
||||
|
||||
// Make sure harp glisses can only be diatonic and chromatic
|
||||
GlissandoStyle glissStyle = item->glissandoStyle();
|
||||
if (item->isHarpGliss().value()
|
||||
&& (glissStyle != GlissandoStyle::DIATONIC
|
||||
&& glissStyle != GlissandoStyle::CHROMATIC)) {
|
||||
item->setGlissandoStyle(GlissandoStyle::DIATONIC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Note* anchor1 = toNote(item->startElement());
|
||||
Note* anchor2 = toNote(item->endElement());
|
||||
Chord* cr1 = anchor1->chord();
|
||||
|
|
|
@ -2648,6 +2648,15 @@ void TRead::read(Glissando* g, XmlReader& e, ReadContext& ctx)
|
|||
ctx.addSpanner(e.intAttribute("id", -1), g);
|
||||
}
|
||||
|
||||
g->setShowText(false);
|
||||
staff_idx_t staffIdx = track2staff(ctx.track());
|
||||
Staff* staff = ctx.score()->staff(staffIdx);
|
||||
if (staff) {
|
||||
String instrId = staff->part()->instrumentId(g->tick());
|
||||
g->setIsHarpGliss(instrId == "harp");
|
||||
}
|
||||
g->resetProperty(Pid::GLISS_STYLE);
|
||||
|
||||
g->setShowText(false);
|
||||
while (e.readNextStartElement()) {
|
||||
const AsciiStringView tag = e.name();
|
||||
|
|
|
@ -2692,11 +2692,22 @@ void TRead::read(Glissando* g, XmlReader& e, ReadContext& ctx)
|
|||
}
|
||||
|
||||
g->setShowText(false);
|
||||
staff_idx_t staffIdx = track2staff(ctx.track());
|
||||
Staff* staff = ctx.score()->staff(staffIdx);
|
||||
if (staff) {
|
||||
String instrId = staff->part()->instrumentId(g->tick());
|
||||
g->setIsHarpGliss(instrId == "harp");
|
||||
}
|
||||
g->resetProperty(Pid::GLISS_STYLE);
|
||||
|
||||
while (e.readNextStartElement()) {
|
||||
const AsciiStringView tag = e.name();
|
||||
if (tag == "text") {
|
||||
g->setShowText(true);
|
||||
TRead::readProperty(g, e, ctx, Pid::GLISS_TEXT);
|
||||
} else if (tag == "isHarpGliss" && ctx.pasteMode()) {
|
||||
g->setIsHarpGliss(e.readBool());
|
||||
g->resetProperty(Pid::GLISS_STYLE);
|
||||
} else if (tag == "subtype") {
|
||||
g->setGlissandoType(TConv::fromXml(e.readAsciiText(), GlissandoType::STRAIGHT));
|
||||
} else if (tag == "glissandoStyle") {
|
||||
|
|
|
@ -1250,6 +1250,10 @@ void TWrite::write(const Glissando* item, XmlWriter& xml, WriteContext& ctx)
|
|||
xml.tag("text", item->text());
|
||||
}
|
||||
|
||||
if (ctx.clipboardmode() && item->isHarpGliss().has_value()) {
|
||||
xml.tagProperty("isHarpGliss", PropertyValue(item->isHarpGliss().value()));
|
||||
}
|
||||
|
||||
for (auto id : { Pid::GLISS_TYPE, Pid::PLAY, Pid::GLISS_STYLE, Pid::GLISS_SHIFT, Pid::GLISS_EASEIN, Pid::GLISS_EASEOUT }) {
|
||||
writeProperty(item, xml, id);
|
||||
}
|
||||
|
|
|
@ -183,6 +183,9 @@ bool MStyle::readProperties(XmlReader& e)
|
|||
case P_TYPE::TIE_PLACEMENT:
|
||||
set(idx, TConv::fromXml(e.readAsciiText(), TiePlacement::AUTO));
|
||||
break;
|
||||
case P_TYPE::GLISS_STYLE:
|
||||
set(idx, GlissandoStyle(e.readText().toInt()));
|
||||
break;
|
||||
default:
|
||||
ASSERT_X(u"unhandled type " + String::number(int(type)));
|
||||
}
|
||||
|
|
|
@ -1187,6 +1187,8 @@ const std::array<StyleDef::StyleValue, size_t(Sid::STYLES)> StyleDef::styleValue
|
|||
{ Sid::glissandoFrameBgColor, "glissandoFrameBgColor", PropertyValue::fromValue(Color::transparent) },
|
||||
{ Sid::glissandoLineWidth, "glissandoLineWidth", Spatium(0.15) },
|
||||
{ Sid::glissandoText, "glissandoText", String(u"gliss.") },
|
||||
{ Sid::glissandoStyle, "glissandoStyle", GlissandoStyle::CHROMATIC },
|
||||
{ Sid::glissandoStyleHarp, "glissandoStyleHarp", GlissandoStyle::DIATONIC },
|
||||
|
||||
{ Sid::bendFontFace, "bendFontFace", "Edwin" },
|
||||
{ Sid::bendFontSize, "bendFontSize", 8.0 },
|
||||
|
|
|
@ -1195,6 +1195,8 @@ enum class Sid {
|
|||
glissandoFrameBgColor,
|
||||
glissandoLineWidth,
|
||||
glissandoText,
|
||||
glissandoStyle,
|
||||
glissandoStyleHarp,
|
||||
|
||||
bendFontFace,
|
||||
bendFontSize,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<container>
|
||||
<rootfiles>
|
||||
<rootfile full-path="score_style.mss"/>
|
||||
<rootfile full-path="TwoNotes_Discrete_Harp_Glissando.mscx"/>
|
||||
<rootfile full-path="Thumbnails/thumbnail.png"/>
|
||||
<rootfile full-path="audiosettings.json"/>
|
||||
<rootfile full-path="viewsettings.json"/>
|
||||
</rootfiles>
|
||||
</container>
|
Binary file not shown.
After Width: | Height: | Size: 2.8 KiB |
|
@ -0,0 +1,261 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<museScore version="4.20">
|
||||
<programVersion>4.2.0</programVersion>
|
||||
<programRevision></programRevision>
|
||||
<Score>
|
||||
<Division>480</Division>
|
||||
<showInvisible>1</showInvisible>
|
||||
<showUnprintable>1</showUnprintable>
|
||||
<showFrames>1</showFrames>
|
||||
<showMargins>0</showMargins>
|
||||
<open>1</open>
|
||||
<metaTag name="arranger"></metaTag>
|
||||
<metaTag name="composer">Composer / arranger</metaTag>
|
||||
<metaTag name="copyright"></metaTag>
|
||||
<metaTag name="creationDate">2023-09-11</metaTag>
|
||||
<metaTag name="lyricist"></metaTag>
|
||||
<metaTag name="movementNumber"></metaTag>
|
||||
<metaTag name="movementTitle"></metaTag>
|
||||
<metaTag name="platform">Apple Macintosh</metaTag>
|
||||
<metaTag name="poet"></metaTag>
|
||||
<metaTag name="source"></metaTag>
|
||||
<metaTag name="sourceRevisionId"></metaTag>
|
||||
<metaTag name="subtitle">Subtitle</metaTag>
|
||||
<metaTag name="translator"></metaTag>
|
||||
<metaTag name="workNumber"></metaTag>
|
||||
<metaTag name="workTitle">Untitled score</metaTag>
|
||||
<Order id="orchestral">
|
||||
<name>Orchestral</name>
|
||||
<instrument id="harp">
|
||||
<family id="harps">Harps</family>
|
||||
</instrument>
|
||||
<section id="woodwind" brackets="true" barLineSpan="true" thinBrackets="true">
|
||||
<family>flutes</family>
|
||||
<family>oboes</family>
|
||||
<family>clarinets</family>
|
||||
<family>saxophones</family>
|
||||
<family>bassoons</family>
|
||||
<unsorted group="woodwinds"/>
|
||||
</section>
|
||||
<section id="brass" brackets="true" barLineSpan="true" thinBrackets="true">
|
||||
<family>horns</family>
|
||||
<family>trumpets</family>
|
||||
<family>cornets</family>
|
||||
<family>flugelhorns</family>
|
||||
<family>trombones</family>
|
||||
<family>tubas</family>
|
||||
</section>
|
||||
<section id="timpani" brackets="true" barLineSpan="true" thinBrackets="true">
|
||||
<family>timpani</family>
|
||||
</section>
|
||||
<section id="percussion" brackets="true" barLineSpan="true" thinBrackets="true">
|
||||
<family>keyboard-percussion</family>
|
||||
<family>drums</family>
|
||||
<family>unpitched-metal-percussion</family>
|
||||
<family>unpitched-wooden-percussion</family>
|
||||
<family>other-percussion</family>
|
||||
</section>
|
||||
<family>keyboards</family>
|
||||
<family>harps</family>
|
||||
<family>organs</family>
|
||||
<family>synths</family>
|
||||
<soloists/>
|
||||
<section id="voices" brackets="true" barLineSpan="false" thinBrackets="true">
|
||||
<family>voices</family>
|
||||
<family>voice-groups</family>
|
||||
</section>
|
||||
<section id="strings" brackets="true" barLineSpan="true" thinBrackets="true">
|
||||
<family>orchestral-strings</family>
|
||||
</section>
|
||||
<unsorted/>
|
||||
</Order>
|
||||
<Part id="1">
|
||||
<Staff id="1">
|
||||
<StaffType group="pitched">
|
||||
<name>stdNormal</name>
|
||||
</StaffType>
|
||||
<bracket type="1" span="2" col="0" visible="1"/>
|
||||
<barLineSpan>1</barLineSpan>
|
||||
</Staff>
|
||||
<Staff id="2">
|
||||
<StaffType group="pitched">
|
||||
<name>stdNormal</name>
|
||||
</StaffType>
|
||||
<defaultClef>F</defaultClef>
|
||||
</Staff>
|
||||
<trackName>Harp</trackName>
|
||||
<Instrument id="harp">
|
||||
<longName>Harp</longName>
|
||||
<shortName>Hrp.</shortName>
|
||||
<trackName>Harp</trackName>
|
||||
<minPitchP>23</minPitchP>
|
||||
<maxPitchP>104</maxPitchP>
|
||||
<minPitchA>23</minPitchA>
|
||||
<maxPitchA>104</maxPitchA>
|
||||
<instrumentId>pluck.harp</instrumentId>
|
||||
<clef staff="2">F</clef>
|
||||
<Channel>
|
||||
<program value="46"/>
|
||||
<synti>Fluid</synti>
|
||||
</Channel>
|
||||
<Channel name="staccato">
|
||||
<program value="45"/>
|
||||
<synti>Fluid</synti>
|
||||
</Channel>
|
||||
<Channel name="flageoletti">
|
||||
<program value="11"/>
|
||||
<synti>Fluid</synti>
|
||||
</Channel>
|
||||
</Instrument>
|
||||
</Part>
|
||||
<Staff id="1">
|
||||
<Measure>
|
||||
<voice>
|
||||
<KeySig>
|
||||
<concertKey>0</concertKey>
|
||||
</KeySig>
|
||||
<TimeSig>
|
||||
<sigN>4</sigN>
|
||||
<sigD>4</sigD>
|
||||
</TimeSig>
|
||||
<HarpPedalDiagram>
|
||||
<isDiagram>0</isDiagram>
|
||||
<pedalState>
|
||||
<string name="0">1</string>
|
||||
<string name="1">1</string>
|
||||
<string name="2">2</string>
|
||||
<string name="3">1</string>
|
||||
<string name="4">2</string>
|
||||
<string name="5">2</string>
|
||||
<string name="6">2</string>
|
||||
</pedalState>
|
||||
<style>harp_pedal_text_diagram</style>
|
||||
<text>E<sym>csymAccidentalNatural</sym> F<sym>csymAccidentalSharp</sym> G<sym>csymAccidentalSharp</sym> A<sym>csymAccidentalSharp</sym>
|
||||
D<sym>csymAccidentalNatural</sym> C<sym>csymAccidentalNatural</sym> B<sym>csymAccidentalSharp</sym> </text>
|
||||
</HarpPedalDiagram>
|
||||
<Chord>
|
||||
<durationType>whole</durationType>
|
||||
<Note>
|
||||
<Accidental>
|
||||
<subtype>accidentalSharp</subtype>
|
||||
</Accidental>
|
||||
<pitch>60</pitch>
|
||||
<tpc>26</tpc>
|
||||
<Spanner type="Glissando">
|
||||
<Glissando>
|
||||
<text>gliss.</text>
|
||||
<diagonal>1</diagonal>
|
||||
<anchor>3</anchor>
|
||||
</Glissando>
|
||||
<next>
|
||||
<location>
|
||||
<measures>1</measures>
|
||||
</location>
|
||||
</next>
|
||||
</Spanner>
|
||||
</Note>
|
||||
</Chord>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<HarpPedalDiagram>
|
||||
<isDiagram>0</isDiagram>
|
||||
<pedalState>
|
||||
<string name="0">1</string>
|
||||
<string name="1">1</string>
|
||||
<string name="2">1</string>
|
||||
<string name="3">1</string>
|
||||
<string name="4">2</string>
|
||||
<string name="5">0</string>
|
||||
<string name="6">1</string>
|
||||
</pedalState>
|
||||
<style>harp_pedal_text_diagram</style>
|
||||
<text>G<sym>csymAccidentalFlat</sym> A<sym>csymAccidentalNatural</sym>
|
||||
B<sym>csymAccidentalNatural</sym> </text>
|
||||
</HarpPedalDiagram>
|
||||
<Chord>
|
||||
<durationType>whole</durationType>
|
||||
<Note>
|
||||
<pitch>72</pitch>
|
||||
<tpc>14</tpc>
|
||||
<Spanner type="Glissando">
|
||||
<Glissando>
|
||||
<text>gliss.</text>
|
||||
<diagonal>1</diagonal>
|
||||
<anchor>3</anchor>
|
||||
</Glissando>
|
||||
<next>
|
||||
<location>
|
||||
<measures>1</measures>
|
||||
</location>
|
||||
</next>
|
||||
</Spanner>
|
||||
<Spanner type="Glissando">
|
||||
<prev>
|
||||
<location>
|
||||
<measures>-1</measures>
|
||||
</location>
|
||||
</prev>
|
||||
</Spanner>
|
||||
</Note>
|
||||
</Chord>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Chord>
|
||||
<durationType>whole</durationType>
|
||||
<Note>
|
||||
<Accidental>
|
||||
<subtype>accidentalSharp</subtype>
|
||||
</Accidental>
|
||||
<pitch>66</pitch>
|
||||
<tpc>20</tpc>
|
||||
<Spanner type="Glissando">
|
||||
<prev>
|
||||
<location>
|
||||
<measures>-1</measures>
|
||||
</location>
|
||||
</prev>
|
||||
</Spanner>
|
||||
</Note>
|
||||
</Chord>
|
||||
</voice>
|
||||
</Measure>
|
||||
</Staff>
|
||||
<Staff id="2">
|
||||
<Measure>
|
||||
<voice>
|
||||
<KeySig>
|
||||
<concertKey>0</concertKey>
|
||||
</KeySig>
|
||||
<TimeSig>
|
||||
<sigN>4</sigN>
|
||||
<sigD>4</sigD>
|
||||
</TimeSig>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
<Measure>
|
||||
<voice>
|
||||
<Rest>
|
||||
<durationType>measure</durationType>
|
||||
<duration>4/4</duration>
|
||||
</Rest>
|
||||
</voice>
|
||||
</Measure>
|
||||
</Staff>
|
||||
</Score>
|
||||
</museScore>
|
|
@ -0,0 +1,113 @@
|
|||
{
|
||||
"activeSoundProfile": "MuseScore Basic",
|
||||
"aux": [
|
||||
{
|
||||
"out": {
|
||||
"balance": 0,
|
||||
"fxChain": {
|
||||
"0": {
|
||||
"active": true,
|
||||
"chainOrder": 0,
|
||||
"resourceMeta": {
|
||||
"attributes": {
|
||||
},
|
||||
"hasNativeEditorSupport": true,
|
||||
"id": "Muse Reverb",
|
||||
"type": "muse_plugin",
|
||||
"vendor": "Muse"
|
||||
},
|
||||
"unitConfiguration": {
|
||||
}
|
||||
}
|
||||
},
|
||||
"volumeDb": 0
|
||||
},
|
||||
"soloMuteState": {
|
||||
"mute": false,
|
||||
"solo": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"out": {
|
||||
"balance": 0,
|
||||
"fxChain": {
|
||||
},
|
||||
"volumeDb": 0
|
||||
},
|
||||
"soloMuteState": {
|
||||
"mute": false,
|
||||
"solo": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"master": {
|
||||
"balance": 0,
|
||||
"fxChain": {
|
||||
},
|
||||
"volumeDb": 0
|
||||
},
|
||||
"tracks": [
|
||||
{
|
||||
"in": {
|
||||
"resourceMeta": {
|
||||
"attributes": {
|
||||
},
|
||||
"hasNativeEditorSupport": false,
|
||||
"id": "MS Basic",
|
||||
"type": "fluid_soundfont",
|
||||
"vendor": "Fluid"
|
||||
},
|
||||
"unitConfiguration": {
|
||||
}
|
||||
},
|
||||
"instrumentId": "harp",
|
||||
"out": {
|
||||
"auxSends": [
|
||||
{
|
||||
"active": true,
|
||||
"signalAmount": 0.30000001192092896
|
||||
},
|
||||
{
|
||||
"active": true,
|
||||
"signalAmount": 0.30000001192092896
|
||||
}
|
||||
],
|
||||
"balance": 0,
|
||||
"fxChain": {
|
||||
},
|
||||
"volumeDb": 0
|
||||
},
|
||||
"partId": "1",
|
||||
"soloMuteState": {
|
||||
"mute": false,
|
||||
"solo": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"in": {
|
||||
"resourceMeta": {
|
||||
"attributes": {
|
||||
},
|
||||
"hasNativeEditorSupport": false,
|
||||
"id": "MS Basic",
|
||||
"type": "fluid_soundfont",
|
||||
"vendor": "Fluid"
|
||||
},
|
||||
"unitConfiguration": {
|
||||
}
|
||||
},
|
||||
"instrumentId": "metronome",
|
||||
"out": {
|
||||
"balance": 0,
|
||||
"fxChain": {
|
||||
},
|
||||
"volumeDb": 0
|
||||
},
|
||||
"partId": "999",
|
||||
"soloMuteState": {
|
||||
"mute": false,
|
||||
"solo": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"notation": {
|
||||
"viewMode": "page"
|
||||
}
|
||||
}
|
|
@ -25,8 +25,11 @@
|
|||
|
||||
#include "mpe/tests/utils/articulationutils.h"
|
||||
|
||||
#include "dom/factory.h"
|
||||
#include "dom/measure.h"
|
||||
#include "dom/part.h"
|
||||
#include "dom/segment.h"
|
||||
#include "dom/harppedaldiagram.h"
|
||||
|
||||
#include "utils/scorerw.h"
|
||||
#include "playback/playbackeventsrenderer.h"
|
||||
|
@ -40,6 +43,7 @@ static constexpr duration_t QUARTER_NOTE_DURATION = 500000; // duration in micro
|
|||
static constexpr duration_t QUAVER_NOTE_DURATION = QUARTER_NOTE_DURATION / 2; // duration in microseconds for 4/4 120BPM
|
||||
static constexpr duration_t SEMI_QUAVER_NOTE_DURATION = QUAVER_NOTE_DURATION / 2; // duration in microseconds for 4/4 120BPM
|
||||
static constexpr duration_t DEMI_SEMI_QUAVER_NOTE_DURATION = QUARTER_NOTE_DURATION / 8; // duration in microseconds for 4/4 120BPM
|
||||
static constexpr duration_t WHOLE_NOTE_DURATION = QUARTER_NOTE_DURATION * 4; // duration in microseconds for 4/4 120BPM
|
||||
|
||||
class Engraving_PlaybackEventsRendererTests : public ::testing::Test
|
||||
{
|
||||
|
@ -897,6 +901,118 @@ TEST_F(Engraving_PlaybackEventsRendererTests, TwoNotes_Glissando_NoPlay)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PlaybackEventsRendererTests_TwoNotes_Discrete_Harp_Glissando
|
||||
* @details Render a score with 3 measures containing whole notes (C4, C5, C4) and glissandos
|
||||
* between them. Add harp pedal diagrams to the first and second measures set to a
|
||||
* whole tone scale, then back to Cmaj. Check the glisses render as expected
|
||||
*/
|
||||
TEST_F(Engraving_PlaybackEventsRendererTests, TwoNotes_Discrete_Harp_Glissando) {
|
||||
// [GIVEN]
|
||||
Score* score = ScoreRW::readScore(
|
||||
PLAYBACK_EVENTS_RENDERING_DIR + "TwoNotes_Discrete_Harp_Glissando/TwoNotes_Discrete_Harp_Glissando.mscx");
|
||||
|
||||
Measure* firstMeasure = score->firstMeasure();
|
||||
ASSERT_TRUE(firstMeasure);
|
||||
|
||||
Segment* firstSegment = firstMeasure->segments().firstCRSegment();
|
||||
ASSERT_TRUE(firstSegment);
|
||||
|
||||
ChordRest* chord = firstSegment->nextChordRest(0);
|
||||
ASSERT_TRUE(chord);
|
||||
|
||||
Measure* secondMeasure = firstMeasure->nextMeasure();
|
||||
ASSERT_TRUE(secondMeasure);
|
||||
|
||||
Segment* secondSegment = secondMeasure->segments().firstCRSegment();
|
||||
ASSERT_TRUE(secondSegment);
|
||||
|
||||
ChordRest* secondChord = secondSegment->nextChordRest(0);
|
||||
ASSERT_TRUE(secondChord);
|
||||
|
||||
// TEST FIRST GLISS
|
||||
|
||||
// [GIVEN] Expected glissando disclosure
|
||||
size_t expectedSubNotesCount = 8;
|
||||
|
||||
float expectedDuration = static_cast<float>(WHOLE_NOTE_DURATION) / expectedSubNotesCount;
|
||||
|
||||
pitch_level_t nominalPitchLevel = pitchLevel(PitchClass::C, 4);
|
||||
std::vector<int> pitchesWt = { 0, 0, 2, 4, 6, 8, 10, 12 };
|
||||
|
||||
std::vector<pitch_level_t> expectedPitches;
|
||||
for (size_t i = 0; i < expectedSubNotesCount; ++i) {
|
||||
expectedPitches.push_back(nominalPitchLevel + pitchesWt.at(i) * PITCH_LEVEL_STEP);
|
||||
}
|
||||
|
||||
m_defaultProfile->setPattern(ArticulationType::DiscreteGlissando, m_dummyPattern);
|
||||
|
||||
PlaybackEventsMap result;
|
||||
m_renderer.render(chord, dynamicLevelFromType(mu::mpe::DynamicType::Natural),
|
||||
ArticulationType::Standard, m_defaultProfile, result);
|
||||
|
||||
for (const auto& pair : result) {
|
||||
// [THEN] We expect that rendered note events number will match expectations
|
||||
EXPECT_EQ(pair.second.size(), expectedSubNotesCount);
|
||||
|
||||
for (size_t i = 0; i < pair.second.size(); ++i) {
|
||||
const mu::mpe::NoteEvent& noteEvent = std::get<mu::mpe::NoteEvent>(pair.second.at(i));
|
||||
|
||||
// [THEN] We expect that each note event has only one articulation applied - Discrete Glissando
|
||||
EXPECT_EQ(noteEvent.expressionCtx().articulations.size(), 1);
|
||||
EXPECT_TRUE(noteEvent.expressionCtx().articulations.contains(ArticulationType::DiscreteGlissando));
|
||||
|
||||
// [THEN] We expect that each sub-note in Discrete Glissando articulation has expected duration
|
||||
EXPECT_EQ(noteEvent.arrangementCtx().nominalDuration, static_cast<int>(expectedDuration));
|
||||
EXPECT_EQ(noteEvent.arrangementCtx().nominalTimestamp, static_cast<int>(i * expectedDuration));
|
||||
|
||||
// [THEN] We expect that each note event will match expected pitch disclosure
|
||||
EXPECT_EQ(noteEvent.pitchCtx().nominalPitchLevel, expectedPitches.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
// TEST SECOND GLISS
|
||||
|
||||
// [GIVEN] Expected glissando disclosure
|
||||
expectedSubNotesCount = 4;
|
||||
expectedDuration = static_cast<float>(WHOLE_NOTE_DURATION) / expectedSubNotesCount;
|
||||
|
||||
nominalPitchLevel = pitchLevel(PitchClass::C, 5);
|
||||
std::vector<int> pitches2 = { 0, 1, 3, 6 };
|
||||
|
||||
expectedPitches.clear();
|
||||
for (size_t i = 0; i < expectedSubNotesCount; ++i) {
|
||||
expectedPitches.push_back(nominalPitchLevel - pitches2.at(i) * PITCH_LEVEL_STEP);
|
||||
}
|
||||
|
||||
m_defaultProfile->setPattern(ArticulationType::DiscreteGlissando, m_dummyPattern);
|
||||
|
||||
PlaybackEventsMap result2;
|
||||
m_renderer.render(secondChord, dynamicLevelFromType(mu::mpe::DynamicType::Natural),
|
||||
ArticulationType::Standard, m_defaultProfile, result2);
|
||||
|
||||
for (const auto& pair : result2) {
|
||||
// [THEN] We expect that rendered note events number will match expectations
|
||||
EXPECT_EQ(pair.second.size(), expectedSubNotesCount);
|
||||
|
||||
for (size_t i = 0; i < pair.second.size(); ++i) {
|
||||
const mu::mpe::NoteEvent& noteEvent = std::get<mu::mpe::NoteEvent>(pair.second.at(i));
|
||||
|
||||
// [THEN] We expect that each note event has only one articulation applied - Discrete Glissando
|
||||
EXPECT_EQ(noteEvent.expressionCtx().articulations.size(), 1);
|
||||
EXPECT_TRUE(noteEvent.expressionCtx().articulations.contains(ArticulationType::DiscreteGlissando));
|
||||
|
||||
// [THEN] We expect that each sub-note in Discrete Glissando articulation has expected duration
|
||||
EXPECT_EQ(noteEvent.arrangementCtx().nominalDuration, static_cast<int>(expectedDuration));
|
||||
|
||||
EXPECT_EQ(noteEvent.arrangementCtx().nominalTimestamp, WHOLE_NOTE_DURATION + static_cast<int>(i * expectedDuration));
|
||||
|
||||
// [THEN] We expect that each note event will match expected pitch disclosure
|
||||
EXPECT_EQ(noteEvent.pitchCtx().nominalPitchLevel, expectedPitches.at(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PlaybackEventsRendererTests_SingleNote_Acciaccatura
|
||||
* @details In this case we're gonna render a simple piece of score with a single measure,
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<Spanner type="Glissando">
|
||||
<Glissando>
|
||||
<text>gliss.</text>
|
||||
<isHarpGliss>0</isHarpGliss>
|
||||
<ticks_f>1/4</ticks_f>
|
||||
<diagonal>1</diagonal>
|
||||
<anchor>3</anchor>
|
||||
|
@ -46,6 +47,7 @@
|
|||
<Spanner type="Glissando">
|
||||
<Glissando>
|
||||
<text>gliss.</text>
|
||||
<isHarpGliss>0</isHarpGliss>
|
||||
<subtype>1</subtype>
|
||||
<ticks_f>1/4</ticks_f>
|
||||
<diagonal>1</diagonal>
|
||||
|
|
|
@ -22,8 +22,10 @@
|
|||
#include "glissandoplaybackmodel.h"
|
||||
|
||||
#include "translation.h"
|
||||
#include "dom/glissando.h"
|
||||
|
||||
using namespace mu::inspector;
|
||||
using namespace mu::engraving;
|
||||
|
||||
GlissandoPlaybackModel::GlissandoPlaybackModel(QObject* parent, IElementRepositoryService* repository)
|
||||
: AbstractInspectorModel(parent, repository)
|
||||
|
@ -42,6 +44,7 @@ void GlissandoPlaybackModel::createProperties()
|
|||
void GlissandoPlaybackModel::requestElements()
|
||||
{
|
||||
m_elementList = m_repository->findElementsByType(mu::engraving::ElementType::GLISSANDO);
|
||||
updateIsHarpGliss();
|
||||
}
|
||||
|
||||
void GlissandoPlaybackModel::loadProperties()
|
||||
|
@ -54,6 +57,35 @@ void GlissandoPlaybackModel::resetProperties()
|
|||
m_styleType->resetToDefault();
|
||||
}
|
||||
|
||||
void GlissandoPlaybackModel::updateIsHarpGliss()
|
||||
{
|
||||
bool isHarpGliss = false;
|
||||
for (const EngravingItem* element : m_elementList) {
|
||||
if (element->isGlissando()) {
|
||||
if (toGlissando(element)->isHarpGliss().value_or(false)) {
|
||||
isHarpGliss = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
setIsHarpGliss(isHarpGliss);
|
||||
}
|
||||
|
||||
bool GlissandoPlaybackModel::isHarpGliss() const
|
||||
{
|
||||
return m_isHarpGliss;
|
||||
}
|
||||
|
||||
void GlissandoPlaybackModel::setIsHarpGliss(bool isHarpGliss)
|
||||
{
|
||||
if (isHarpGliss == m_isHarpGliss) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_isHarpGliss = isHarpGliss;
|
||||
emit isHarpGlissChanged(m_isHarpGliss);
|
||||
}
|
||||
|
||||
PropertyItem* GlissandoPlaybackModel::styleType() const
|
||||
{
|
||||
return m_styleType;
|
||||
|
|
|
@ -30,11 +30,16 @@ class GlissandoPlaybackModel : public AbstractInspectorModel
|
|||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(PropertyItem * styleType READ styleType CONSTANT)
|
||||
Q_PROPERTY(bool isHarpGliss READ isHarpGliss NOTIFY isHarpGlissChanged)
|
||||
|
||||
public:
|
||||
explicit GlissandoPlaybackModel(QObject* parent, IElementRepositoryService* repository);
|
||||
|
||||
PropertyItem* styleType() const;
|
||||
bool isHarpGliss() const;
|
||||
|
||||
signals:
|
||||
void isHarpGlissChanged(bool isHarpGliss);
|
||||
|
||||
protected:
|
||||
void createProperties() override;
|
||||
|
@ -43,7 +48,12 @@ protected:
|
|||
void resetProperties() override;
|
||||
|
||||
private:
|
||||
void updateIsHarpGliss();
|
||||
void setIsHarpGliss(bool isHarpGliss);
|
||||
|
||||
PropertyItem* m_styleType = nullptr;
|
||||
|
||||
bool m_isHarpGliss = false;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ ExpandableBlank {
|
|||
|
||||
property int navigationRowEnd: contentItem.navigationRowEnd
|
||||
|
||||
readonly property bool isHarpGliss: model ? model.isHarpGliss : false
|
||||
|
||||
enabled: model ? !model.isEmpty : false
|
||||
|
||||
title: model ? model.title : ""
|
||||
|
@ -49,12 +51,17 @@ ExpandableBlank {
|
|||
titleText: qsTrc("inspector", "Style")
|
||||
propertyItem: root.model ? root.model.styleType : null
|
||||
|
||||
model: [
|
||||
{ text: qsTrc("inspector", "Chromatic"), value: Glissando.STYLE_CHROMATIC },
|
||||
{ text: qsTrc("inspector", "White keys"), value: Glissando.STYLE_WHITE_KEYS },
|
||||
{ text: qsTrc("inspector", "Black keys"), value: Glissando.STYLE_BLACK_KEYS },
|
||||
{ text: qsTrc("inspector", "Diatonic"), value: Glissando.STYLE_DIATONIC },
|
||||
{ text: qsTrc("inspector", "Portamento"), value: Glissando.STYLE_PORTAMENTO }
|
||||
]
|
||||
model: root.isHarpGliss
|
||||
? [
|
||||
{ text: qsTrc("inspector", "Default (Diatonic)"), value: Glissando.STYLE_DIATONIC },
|
||||
{ text: qsTrc("inspector", "Chromatic"), value: Glissando.STYLE_CHROMATIC }
|
||||
]
|
||||
: [
|
||||
{ text: qsTrc("inspector", "Chromatic"), value: Glissando.STYLE_CHROMATIC },
|
||||
{ text: qsTrc("inspector", "White keys"), value: Glissando.STYLE_WHITE_KEYS },
|
||||
{ text: qsTrc("inspector", "Black keys"), value: Glissando.STYLE_BLACK_KEYS },
|
||||
{ text: qsTrc("inspector", "Diatonic"), value: Glissando.STYLE_DIATONIC },
|
||||
{ text: qsTrc("inspector", "Portamento"), value: Glissando.STYLE_PORTAMENTO }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -425,7 +425,7 @@ enum class GlissandoStyle {
|
|||
WHITE_KEYS = int(mu::engraving::GlissandoStyle::WHITE_KEYS),
|
||||
BLACK_KEYS = int(mu::engraving::GlissandoStyle::BLACK_KEYS),
|
||||
DIATONIC = int(mu::engraving::GlissandoStyle::DIATONIC),
|
||||
PORTAMENTO = int(mu::engraving::GlissandoStyle::PORTAMENTO),
|
||||
PORTAMENTO = int(mu::engraving::GlissandoStyle::PORTAMENTO)
|
||||
};
|
||||
Q_ENUM_NS(GlissandoStyle);
|
||||
|
||||
|
|
Loading…
Reference in New Issue