Skip to content

Commit c83d31b

Browse files
committed
Introduce configurable pedal release gap for VST and MIDI engines
1 parent fd30c52 commit c83d31b

12 files changed

Lines changed: 88 additions & 6 deletions

File tree

src/engraving/compat/midi/compatmidirenderinternal.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include "dom/segment.h"
5959
#include "dom/staff.h"
6060
#include "dom/stafftextbase.h"
61+
#include "dom/tempo.h"
6162
#include "dom/swing.h"
6263
#include "dom/tie.h"
6364
#include "dom/trill.h"
@@ -1468,6 +1469,8 @@ void CompatMidiRendererInternal::doRenderSpanners(EventsHolder& events, Spanner*
14681469
if (s->isPedal()) {
14691470
PedalEvent lastEvent;
14701471

1472+
// TODO: pedalEventList is always empty here, so consecutive-pedal gap checks below never fire.
1473+
// The list should likely be accumulated across spanners.
14711474
if (!pedalEventList.empty()) {
14721475
lastEvent = pedalEventList.back();
14731476
} else {
@@ -1476,14 +1479,20 @@ void CompatMidiRendererInternal::doRenderSpanners(EventsHolder& events, Spanner*
14761479

14771480
int st = s->tick().ticks();
14781481

1482+
int gapTicks = 1;
1483+
if (MScore::pedalReleaseGapMs > 0) {
1484+
double bps = score->tempomap()->multipliedTempo(st).val;
1485+
gapTicks = std::max(1, static_cast<int>((1000LL * MScore::pedalReleaseGapMs * bps * Constants::DIVISION) / 1000000));
1486+
}
1487+
14791488
if (!lastEvent.on && lastEvent.tick >= (st + 2)) {
14801489
pedalEventList.emplace(pedalEventList.cend() - 1,
1481-
st + (2 - MScore::pedalEventsMinTicks), false, staffIdx);
1490+
st + (2 - gapTicks), false, staffIdx);
14821491
}
14831492
int a = st + 2;
14841493
pedalEventList.emplace_back(a, true, staffIdx);
14851494

1486-
int t = s->tick2().ticks() + (2 - MScore::pedalEventsMinTicks);
1495+
int t = s->tick2().ticks() + (2 - gapTicks);
14871496
if (!score->repeatList().empty()) {
14881497
const RepeatSegment& lastRepeat = *score->repeatList().back();
14891498
if (t > lastRepeat.utick + lastRepeat.len()) {

src/engraving/dom/mscore.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ double MScore::horizontalPageGapOdd = 3.5 * DPMM;
5050

5151
bool MScore::warnPitchRange;
5252
bool MScore::warnGuitarBends;
53-
int MScore::pedalEventsMinTicks;
53+
int MScore::pedalReleaseGapMs;
5454

5555
double MScore::nudgeStep;
5656
double MScore::nudgeStep10;

src/engraving/dom/mscore.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ class MScore
196196

197197
static bool warnPitchRange;
198198
static bool warnGuitarBends;
199-
static int pedalEventsMinTicks;
199+
static int pedalReleaseGapMs;
200200

201201
static double nudgeStep;
202202
static double nudgeStep10;

src/engraving/engravingmodule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ void EngravingModule::onInit(const IApplication::RunMode&)
252252

253253
MScore::warnPitchRange = true;
254254
MScore::warnGuitarBends = true;
255-
MScore::pedalEventsMinTicks = 1;
255+
MScore::pedalReleaseGapMs = 0;
256256

257257
Drumset::initDrumset();
258258
FiguredBass::readConfigFile(String());

src/engraving/playback/metaparsers/internal/spannersmetaparser.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
#include "dom/tempo.h"
3131

3232
#include "playback/utils/pitchutils.h"
33+
#include "playback/utils/arrangementutils.h"
3334
#include "playback/filters/spannerfilter.h"
35+
#include "dom/mscore.h"
3436

3537
using namespace mu::engraving;
3638
using namespace muse;
@@ -142,6 +144,16 @@ void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingConte
142144
return;
143145
}
144146

147+
int durationTicks = overallDurationTicks;
148+
149+
if (spanner->type() == ElementType::PEDAL) {
150+
int gapTicks = 1;
151+
if (MScore::pedalReleaseGapMs > 0) {
152+
gapTicks = std::max(1, ticksFromTempoAndDuration(spannerCtx.beatsPerSecond.val, MScore::pedalReleaseGapMs * 1000));
153+
}
154+
durationTicks = std::max(1, overallDurationTicks - gapTicks);
155+
}
156+
145157
mpe::ArticulationMeta articulationMeta;
146158
articulationMeta.type = type;
147159
articulationMeta.pattern = pattern;
@@ -150,7 +162,7 @@ void SpannersMetaParser::doParse(const EngravingItem* item, const RenderingConte
150162
articulationMeta.overallDynamicChangesRange = overallDynamicRange;
151163
articulationMeta.overallDuration = durationFromStartAndTicks(spannerCtx.score,
152164
spannerCtx.nominalPositionStartTick,
153-
overallDurationTicks,
165+
durationTicks,
154166
spannerCtx.positionTickOffset);
155167

156168
appendArticulationData(std::move(articulationMeta), result);

src/notation/inotationconfiguration.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ class INotationConfiguration : MODULE_GLOBAL_INTERFACE
207207
virtual void setNotePlayDurationMilliseconds(int durationMs) = 0;
208208
virtual muse::async::Channel<int> notePlayDurationMillisecondsChanged() const = 0;
209209

210+
virtual int pedalReleaseGapMs() const = 0;
211+
virtual void setPedalReleaseGapMs(int ms) = 0;
212+
virtual muse::async::Channel<int> pedalReleaseGapMsChanged() const = 0;
213+
210214
virtual void setTemplateModeEnabled(std::optional<bool> enabled) = 0;
211215
virtual void setTestModeEnabled(std::optional<bool> enabled) = 0;
212216

src/notation/internal/notationconfiguration.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static const Settings::Key IS_CANVAS_ORIENTATION_VERTICAL_KEY(module_name, "ui/c
9292
static const Settings::Key COLOR_NOTES_OUTSIDE_OF_USABLE_PITCH_RANGE(module_name, "score/note/warnPitchRange");
9393
static const Settings::Key WARN_GUITAR_BENDS(module_name, "score/note/warnGuitarBends");
9494
static const Settings::Key REALTIME_DELAY(module_name, "io/midi/realtimeDelay");
95+
static const Settings::Key PEDAL_RELEASE_GAP_MS(module_name, "application/playback/pedalReleaseGapMs");
9596
static const Settings::Key USE_MIDI_VELOCITY_AND_DURATION_DURING_NOTE_INPUT(module_name,
9697
"io/midi/useMidiVelocityAndDurationDuringNoteInput");
9798
static const Settings::Key NOTE_DEFAULT_PLAY_DURATION(module_name, "score/note/defaultPlayDuration");
@@ -309,6 +310,14 @@ void NotationConfiguration::init()
309310
m_delayBetweenNotesInRealTimeModeMillisecondsChanged.send(val.toInt());
310311
});
311312

313+
settings()->setDefaultValue(PEDAL_RELEASE_GAP_MS, Val(0));
314+
settings()->setDescription(PEDAL_RELEASE_GAP_MS, muse::trc("notation", "Pedal release gap for playback (in milliseconds)"));
315+
settings()->setCanBeManuallyEdited(PEDAL_RELEASE_GAP_MS, true, Val(0));
316+
settings()->valueChanged(PEDAL_RELEASE_GAP_MS).onReceive(this, [this](const Val& val) {
317+
mu::engraving::MScore::pedalReleaseGapMs = val.toInt();
318+
m_pedalReleaseGapMsChanged.send(val.toInt());
319+
});
320+
312321
settings()->setDefaultValue(USE_MIDI_VELOCITY_AND_DURATION_DURING_NOTE_INPUT, Val(true));
313322
settings()->valueChanged(USE_MIDI_VELOCITY_AND_DURATION_DURING_NOTE_INPUT).onReceive(this, [this](const Val& val) {
314323
m_useMidiVelocityAndDurationDuringNoteInputChanged.send(val.toBool());
@@ -340,6 +349,7 @@ void NotationConfiguration::init()
340349

341350
mu::engraving::MScore::warnPitchRange = colorNotesOutsideOfUsablePitchRange();
342351
mu::engraving::MScore::warnGuitarBends = warnGuitarBends();
352+
mu::engraving::MScore::pedalReleaseGapMs = settings()->value(PEDAL_RELEASE_GAP_MS).toInt();
343353

344354
// Restore stored grid size values
345355
mu::engraving::MScore::setHRaster(settings()->value(HORIZONTAL_GRID_SIZE_KEY).toInt());
@@ -987,6 +997,21 @@ async::Channel<int> NotationConfiguration::delayBetweenNotesInRealTimeModeMillis
987997
return m_delayBetweenNotesInRealTimeModeMillisecondsChanged;
988998
}
989999

1000+
int NotationConfiguration::pedalReleaseGapMs() const
1001+
{
1002+
return settings()->value(PEDAL_RELEASE_GAP_MS).toInt();
1003+
}
1004+
1005+
void NotationConfiguration::setPedalReleaseGapMs(int ms)
1006+
{
1007+
settings()->setSharedValue(PEDAL_RELEASE_GAP_MS, Val(ms));
1008+
}
1009+
1010+
async::Channel<int> NotationConfiguration::pedalReleaseGapMsChanged() const
1011+
{
1012+
return m_pedalReleaseGapMsChanged;
1013+
}
1014+
9901015
bool NotationConfiguration::useMidiVelocityAndDurationDuringNoteInput() const
9911016
{
9921017
return settings()->value(USE_MIDI_VELOCITY_AND_DURATION_DURING_NOTE_INPUT).toBool();

src/notation/internal/notationconfiguration.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,10 @@ class NotationConfiguration : public INotationConfiguration, public muse::async:
209209
void setNotePlayDurationMilliseconds(int durationMs) override;
210210
muse::async::Channel<int> notePlayDurationMillisecondsChanged() const override;
211211

212+
int pedalReleaseGapMs() const override;
213+
void setPedalReleaseGapMs(int ms) override;
214+
muse::async::Channel<int> pedalReleaseGapMsChanged() const override;
215+
212216
void setTemplateModeEnabled(std::optional<bool> enabled) override;
213217
void setTestModeEnabled(std::optional<bool> enabled) override;
214218

@@ -261,6 +265,7 @@ class NotationConfiguration : public INotationConfiguration, public muse::async:
261265
muse::async::Channel<bool> m_warnGuitarBendsChanged;
262266
muse::async::Channel<int> m_delayBetweenNotesInRealTimeModeMillisecondsChanged;
263267
muse::async::Channel<int> m_notePlayDurationMillisecondsChanged;
268+
muse::async::Channel<int> m_pedalReleaseGapMsChanged;
264269
muse::async::Channel<bool> m_useMidiVelocityAndDurationDuringNoteInputChanged;
265270
muse::async::Channel<std::string> m_styleFileImportPathChanged;
266271
muse::async::Notification m_isPlayRepeatsChanged;

src/notation/internal/notationplayback.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ void NotationPlayback::init()
115115
}
116116
});
117117

118+
configuration()->pedalReleaseGapMsChanged().onReceive(this, [this](int) {
119+
m_playbackModel.reload();
120+
});
121+
118122
score()->loopBoundaryTickChanged().onReceive(this, [this](LoopBoundaryType, unsigned) {
119123
updateLoopBoundaries();
120124
});

src/notation/tests/mocks/notationconfigurationmock.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ class NotationConfigurationMock : public INotationConfiguration
195195
MOCK_METHOD(void, setNotePlayDurationMilliseconds, (int), (override));
196196
MOCK_METHOD((muse::async::Channel<int>), notePlayDurationMillisecondsChanged, (), (const, override));
197197

198+
MOCK_METHOD(int, pedalReleaseGapMs, (), (const, override));
199+
MOCK_METHOD(void, setPedalReleaseGapMs, (int), (override));
200+
MOCK_METHOD((muse::async::Channel<int>), pedalReleaseGapMsChanged, (), (const, override));
201+
198202
MOCK_METHOD(void, setTemplateModeEnabled, (std::optional<bool>), (override));
199203
MOCK_METHOD(void, setTestModeEnabled, (std::optional<bool>), (override));
200204

0 commit comments

Comments
 (0)