Skip to content

Commit 72e0772

Browse files
committed
Add percentage interpolation to velocity dialog
- Refactors InterpolationDialog.qml to optionally accept percentage values for velocity interpolation.
1 parent d4284b7 commit 72e0772

File tree

9 files changed

+99
-30
lines changed

9 files changed

+99
-30
lines changed

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Release date:
55

66
New features:
77

8+
* Add percentage interpolation to velocity dialog
9+
810
Bug fixes:
911

1012
Other:

src/application/service/editor_service.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,39 +1238,39 @@ void EditorService::requestSelectionTranspose(int semitones)
12381238
}
12391239
}
12401240

1241-
void EditorService::requestLinearVelocityInterpolationOnColumn(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue)
1241+
void EditorService::requestLinearVelocityInterpolationOnColumn(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue, bool usePercentages)
12421242
{
12431243
auto start = position();
12441244
start.line = startLine;
12451245

12461246
auto end = position();
12471247
end.line = endLine;
12481248

1249-
if (const auto changedPositions = NoteDataManipulator::interpolateVelocityOnColumn(m_song, start, end, startValue, endValue); !changedPositions.empty()) {
1249+
if (const auto changedPositions = NoteDataManipulator::interpolateVelocityOnColumn(m_song, start, end, startValue, endValue, usePercentages); !changedPositions.empty()) {
12501250
for (auto && position : changedPositions) {
12511251
emit noteDataAtPositionChanged(position);
12521252
}
12531253
setIsModified(true);
12541254
}
12551255
}
12561256

1257-
void EditorService::requestLinearVelocityInterpolationOnTrack(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue)
1257+
void EditorService::requestLinearVelocityInterpolationOnTrack(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue, bool usePercentages)
12581258
{
12591259
auto start = position();
12601260
start.line = startLine;
12611261

12621262
auto end = position();
12631263
end.line = endLine;
12641264

1265-
if (const auto changedPositions = NoteDataManipulator::interpolateVelocityOnTrack(m_song, start, end, startValue, endValue); !changedPositions.empty()) {
1265+
if (const auto changedPositions = NoteDataManipulator::interpolateVelocityOnTrack(m_song, start, end, startValue, endValue, usePercentages); !changedPositions.empty()) {
12661266
for (auto && position : changedPositions) {
12671267
emit noteDataAtPositionChanged(position);
12681268
}
12691269
setIsModified(true);
12701270
}
12711271
}
12721272

1273-
void EditorService::requestLinearVelocityInterpolationOnSelection(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue)
1273+
void EditorService::requestLinearVelocityInterpolationOnSelection(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue, bool usePercentages)
12741274
{
12751275
if (m_selectionService->isValidSelection()) {
12761276
for (auto column = m_selectionService->minColumn(); column <= m_selectionService->maxColumn(); column++) {
@@ -1283,7 +1283,7 @@ void EditorService::requestLinearVelocityInterpolationOnSelection(quint64 startL
12831283
end.column = column;
12841284
end.line = endLine;
12851285

1286-
if (const auto changedPositions = NoteDataManipulator::interpolateVelocityOnColumn(m_song, start, end, startValue, endValue); !changedPositions.empty()) {
1286+
if (const auto changedPositions = NoteDataManipulator::interpolateVelocityOnColumn(m_song, start, end, startValue, endValue, usePercentages); !changedPositions.empty()) {
12871287
for (auto && position : changedPositions) {
12881288
emit noteDataAtPositionChanged(position);
12891289
}

src/application/service/editor_service.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,11 @@ class EditorService : public QObject
191191
Q_INVOKABLE void requestSelectionTranspose(int semitones);
192192

193193
//! Performs linear interpolation on velocity on current column over given lines.
194-
Q_INVOKABLE void requestLinearVelocityInterpolationOnColumn(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue);
194+
Q_INVOKABLE void requestLinearVelocityInterpolationOnColumn(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue, bool usePercentages);
195195
//! Performs linear interpolation on velocity on current column over given lines.
196-
Q_INVOKABLE void requestLinearVelocityInterpolationOnTrack(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue);
196+
Q_INVOKABLE void requestLinearVelocityInterpolationOnTrack(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue, bool usePercentages);
197197
//! Performs linear interpolation on velocity on currently selected columns.
198-
Q_INVOKABLE void requestLinearVelocityInterpolationOnSelection(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue);
198+
Q_INVOKABLE void requestLinearVelocityInterpolationOnSelection(quint64 startLine, quint64 endLine, quint8 startValue, quint8 endValue, bool usePercentages);
199199

200200
Q_INVOKABLE void setDelayOnCurrentLine(quint8 ticks);
201201
Q_INVOKABLE quint8 delayAtCurrentPosition() const;

src/domain/note_data_manipulator.cpp

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,34 @@ namespace noteahead {
2525

2626
static const auto TAG = "NoteDataManipulator";
2727

28-
NoteDataManipulator::ChangedPositions NoteDataManipulator::interpolateVelocityOnColumn(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue)
28+
NoteDataManipulator::ChangedPositions NoteDataManipulator::interpolateVelocityOnColumn(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue, bool usePercentages)
2929
{
30-
juzzlin::L(TAG).info() << "Requesting linear velocity interpolation on a column: " << start.toString() << " -> " << end.toString() << ", " << static_cast<int>(startValue) << " -> " << static_cast<int>(endValue);
30+
juzzlin::L(TAG).info() << "Requesting linear velocity interpolation on a column: " << start.toString() << " -> " << end.toString() << ", " << static_cast<int>(startValue) << " -> " << static_cast<int>(endValue) << ", usePercentages=" << usePercentages;
3131
NoteDataManipulator::ChangedPositions changedPositions;
3232
if (const auto locked = song.lock(); locked) {
3333
const Interpolator interpolator { start.line, end.line, static_cast<double>(startValue), static_cast<double>(endValue) };
3434
for (auto line = start.line; line <= end.line; line++) {
3535
auto targetPosition = start;
3636
targetPosition.line = line;
3737
if (const auto noteData = locked->noteDataAtPosition(targetPosition); noteData && noteData->type() == NoteData::Type::NoteOn) {
38-
noteData->setVelocity(static_cast<uint8_t>(interpolator.getValue(targetPosition.line)));
38+
if (usePercentages) {
39+
const double currentVelocity = static_cast<double>(noteData->velocity());
40+
const double percentage = interpolator.getValue(targetPosition.line); // This is 0-100
41+
const uint8_t newVelocity = static_cast<uint8_t>(std::clamp(currentVelocity * (percentage / 100.0), 0.0, 127.0));
42+
noteData->setVelocity(newVelocity);
43+
} else {
44+
noteData->setVelocity(static_cast<uint8_t>(interpolator.getValue(targetPosition.line)));
45+
}
3946
changedPositions.push_back(targetPosition);
4047
}
4148
}
4249
}
4350
return changedPositions;
4451
}
4552

46-
NoteDataManipulator::ChangedPositions NoteDataManipulator::interpolateVelocityOnTrack(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue)
53+
NoteDataManipulator::ChangedPositions NoteDataManipulator::interpolateVelocityOnTrack(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue, bool usePercentages)
4754
{
48-
juzzlin::L(TAG).info() << "Requesting linear velocity interpolation on a track: " << start.toString() << " -> " << end.toString() << ", " << static_cast<int>(startValue) << " -> " << static_cast<int>(endValue);
55+
juzzlin::L(TAG).info() << "Requesting linear velocity interpolation on a track: " << start.toString() << " -> " << end.toString() << ", " << static_cast<int>(startValue) << " -> " << static_cast<int>(endValue) << ", usePercentages=" << usePercentages;
4956
NoteDataManipulator::ChangedPositions changedPositions;
5057
if (const auto locked = song.lock(); locked) {
5158
const Interpolator interpolator { start.line, end.line, static_cast<double>(startValue), static_cast<double>(endValue) };
@@ -55,7 +62,14 @@ NoteDataManipulator::ChangedPositions NoteDataManipulator::interpolateVelocityOn
5562
targetPosition.column = column;
5663
targetPosition.line = line;
5764
if (const auto noteData = locked->noteDataAtPosition(targetPosition); noteData && noteData->type() == NoteData::Type::NoteOn) {
58-
noteData->setVelocity(static_cast<uint8_t>(interpolator.getValue(targetPosition.line)));
65+
if (usePercentages) {
66+
const double currentVelocity = static_cast<double>(noteData->velocity());
67+
const double percentage = interpolator.getValue(targetPosition.line); // This is 0-100
68+
const uint8_t newVelocity = static_cast<uint8_t>(std::clamp(currentVelocity * (percentage / 100.0), 0.0, 127.0));
69+
noteData->setVelocity(newVelocity);
70+
} else {
71+
noteData->setVelocity(static_cast<uint8_t>(interpolator.getValue(targetPosition.line)));
72+
}
5973
changedPositions.push_back(targetPosition);
6074
}
6175
}

src/domain/note_data_manipulator.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ namespace NoteDataManipulator {
2828

2929
using SongW = std::weak_ptr<Song>;
3030
using ChangedPositions = std::vector<Position>;
31-
ChangedPositions interpolateVelocityOnColumn(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue);
32-
ChangedPositions interpolateVelocityOnTrack(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue);
31+
ChangedPositions interpolateVelocityOnColumn(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue, bool usePercentages);
32+
ChangedPositions interpolateVelocityOnTrack(SongW song, const Position & start, const Position & end, uint8_t startValue, uint8_t endValue, bool usePercentages);
3333

3434
} // namespace NoteDataManipulator
3535
} // namespace noteahead

src/unit_tests/editor_service_test/editor_service_test.cpp

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -933,7 +933,7 @@ void EditorServiceTest::test_requestLinearVelocityInterpolationOnColumn_shouldIn
933933
QVERIFY(editorService.requestPosition(0, 0, 0, 7, 0));
934934
QVERIFY(editorService.requestNoteOnAtCurrentPosition(1, 3, 64));
935935

936-
editorService.requestLinearVelocityInterpolationOnColumn(0, 7, 0, 100);
936+
editorService.requestLinearVelocityInterpolationOnColumn(0, 7, 0, 100, false);
937937

938938
QCOMPARE(noteDataChangedSpy.count(), 6);
939939
QCOMPARE(editorService.displayNoteAtPosition(0, 0, 0, 0), "C-3");
@@ -965,7 +965,7 @@ void EditorServiceTest::test_requestLinearVelocityInterpolationOnTrack_shouldInt
965965
QVERIFY(editorService.requestPosition(0, 0, 1, 7, 0));
966966
QVERIFY(editorService.requestNoteOnAtCurrentPosition(1, 3, 64));
967967

968-
editorService.requestLinearVelocityInterpolationOnTrack(0, 7, 0, 100);
968+
editorService.requestLinearVelocityInterpolationOnTrack(0, 7, 0, 100, false);
969969

970970
QCOMPARE(noteDataChangedSpy.count(), 12);
971971

@@ -984,6 +984,34 @@ void EditorServiceTest::test_requestLinearVelocityInterpolationOnTrack_shouldInt
984984
QCOMPARE(editorService.displayVelocityAtPosition(0, 0, 1, 7), "100");
985985
}
986986

987+
void EditorServiceTest::test_requestLinearVelocityInterpolationOnColumn_shouldInterpolateVelocitiesAsPercentages()
988+
{
989+
EditorService editorService;
990+
QSignalSpy noteDataChangedSpy { &editorService, &EditorService::noteDataAtPositionChanged };
991+
992+
QVERIFY(editorService.requestPosition(0, 0, 0, 0, 0));
993+
QVERIFY(editorService.requestNoteOnAtCurrentPosition(1, 3, 100)); // Initial velocity 100
994+
QVERIFY(editorService.requestPosition(0, 0, 0, 3, 0));
995+
QVERIFY(editorService.requestNoteOnAtCurrentPosition(1, 3, 50)); // Initial velocity 50
996+
QVERIFY(editorService.requestPosition(0, 0, 0, 7, 0));
997+
QVERIFY(editorService.requestNoteOnAtCurrentPosition(1, 3, 20)); // Initial velocity 20
998+
999+
// Interpolate from 50% to 150%
1000+
editorService.requestLinearVelocityInterpolationOnColumn(0, 7, 50, 150, true);
1001+
1002+
QCOMPARE(noteDataChangedSpy.count(), 6);
1003+
QCOMPARE(editorService.displayNoteAtPosition(0, 0, 0, 0), "C-3");
1004+
// Line 0: 100 * 0.50 = 50
1005+
QCOMPARE(editorService.displayVelocityAtPosition(0, 0, 0, 0), "050");
1006+
QCOMPARE(editorService.displayNoteAtPosition(0, 0, 0, 3), "C-3");
1007+
// Line 3: Interpolated percentage at line 3: 50 + (150 - 50) * (3.0 / 7.0) = 50 + 100 * 0.42857 = 50 + 42.857 = 92.857
1008+
// Velocity at line 3: 50 * 92.857 / 100 = 46.4285 -> 46
1009+
QCOMPARE(editorService.displayVelocityAtPosition(0, 0, 0, 3), "046");
1010+
QCOMPARE(editorService.displayNoteAtPosition(0, 0, 0, 7), "C-3");
1011+
// Line 7: 20 * 1.50 = 30
1012+
QCOMPARE(editorService.displayVelocityAtPosition(0, 0, 0, 7), "030");
1013+
}
1014+
9871015
void EditorServiceTest::test_requestPosition_invalidPosition_shouldNotChangePosition()
9881016
{
9891017
EditorService editorService;

src/unit_tests/editor_service_test/editor_service_test.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ private slots:
7979

8080
void test_requestLinearVelocityInterpolationOnColumn_shouldInterpolateVelocities();
8181
void test_requestLinearVelocityInterpolationOnTrack_shouldInterpolateVelocities();
82+
void test_requestLinearVelocityInterpolationOnColumn_shouldInterpolateVelocitiesAsPercentages();
8283

8384
void test_requestPosition_invalidPosition_shouldNotChangePosition();
8485
void test_requestPosition_validPosition_shouldChangePosition();

src/view/qml/Dialogs/InterpolationDialog.qml

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ Dialog {
88
id: rootItem
99
modal: true
1010
standardButtons: Dialog.Ok | Dialog.Cancel
11+
function usePercentages() {
12+
return percentageCheckBox.checked;
13+
}
1114
function setTitle(text) {
1215
title = "<strong>" + text + "</strong>";
1316
}
@@ -38,10 +41,31 @@ Dialog {
3841
contentItem: GridLayout {
3942
rowSpacing: 10
4043
width: parent.width
44+
CheckBox {
45+
id: percentageCheckBox
46+
text: qsTr("Use percentages")
47+
Layout.columnSpan: 4
48+
Layout.row: 0
49+
onCheckedChanged: {
50+
if (checked) {
51+
startValueSpinBox.from = 0;
52+
startValueSpinBox.to = 200;
53+
endValueSpinBox.from = 0;
54+
endValueSpinBox.to = 200;
55+
if (startValueSpinBox.value > 200) startValueSpinBox.value = 200;
56+
if (endValueSpinBox.value > 200) endValueSpinBox.value = 200;
57+
} else {
58+
startValueSpinBox.from = 0;
59+
startValueSpinBox.to = 127;
60+
endValueSpinBox.from = 0;
61+
endValueSpinBox.to = 127;
62+
}
63+
}
64+
}
4165
Label {
4266
text: qsTr("Start value:")
4367
Layout.column: 0
44-
Layout.row: 0
68+
Layout.row: 1
4569
}
4670
SpinBox {
4771
id: startValueSpinBox
@@ -54,13 +78,13 @@ Dialog {
5478
focus = false;
5579
}
5680
Layout.column: 1
57-
Layout.row: 0
81+
Layout.row: 1
5882
Layout.fillWidth: true
5983
}
6084
Label {
6185
text: " " + qsTr("End value:")
6286
Layout.column: 2
63-
Layout.row: 0
87+
Layout.row: 1
6488
}
6589
SpinBox {
6690
id: endValueSpinBox
@@ -73,14 +97,14 @@ Dialog {
7397
focus = false;
7498
}
7599
Layout.column: 3
76-
Layout.row: 0
100+
Layout.row: 1
77101
Layout.fillWidth: true
78102
}
79103
Label {
80104
text: qsTr("Start line:")
81105
width: parent.width
82106
Layout.column: 0
83-
Layout.row: 1
107+
Layout.row: 2
84108
}
85109
SpinBox {
86110
id: startLineSpinBox
@@ -93,13 +117,13 @@ Dialog {
93117
focus = false;
94118
}
95119
Layout.column: 1
96-
Layout.row: 1
120+
Layout.row: 2
97121
Layout.fillWidth: true
98122
}
99123
Label {
100124
text: " " + qsTr("End line:")
101125
Layout.column: 2
102-
Layout.row: 1
126+
Layout.row: 2
103127
}
104128
SpinBox {
105129
id: endLineSpinBox
@@ -112,7 +136,7 @@ Dialog {
112136
focus = false;
113137
}
114138
Layout.column: 3
115-
Layout.row: 1
139+
Layout.row: 2
116140
Layout.fillWidth: true
117141
}
118142
}

src/view/qml/Main.qml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,19 +213,19 @@ ApplicationWindow {
213213
id: columnVelocityInterpolationDialog
214214
anchors.centerIn: parent
215215
width: parent.width * Constants.defaultDialogScale
216-
onAccepted: editorService.requestLinearVelocityInterpolationOnColumn(startLine(), endLine(), startValue(), endValue())
216+
onAccepted: editorService.requestLinearVelocityInterpolationOnColumn(startLine(), endLine(), startValue(), endValue(), usePercentages())
217217
}
218218
InterpolationDialog {
219219
id: trackVelocityInterpolationDialog
220220
anchors.centerIn: parent
221221
width: parent.width * Constants.defaultDialogScale
222-
onAccepted: editorService.requestLinearVelocityInterpolationOnTrack(startLine(), endLine(), startValue(), endValue())
222+
onAccepted: editorService.requestLinearVelocityInterpolationOnTrack(startLine(), endLine(), startValue(), endValue(), usePercentages())
223223
}
224224
InterpolationDialog {
225225
id: selectionVelocityInterpolationDialog
226226
anchors.centerIn: parent
227227
width: parent.width * Constants.defaultDialogScale
228-
onAccepted: editorService.requestLinearVelocityInterpolationOnSelection(startLine(), endLine(), startValue(), endValue())
228+
onAccepted: editorService.requestLinearVelocityInterpolationOnSelection(startLine(), endLine(), startValue(), endValue(), usePercentages())
229229
}
230230
AddMidiCcAutomationDialog {
231231
id: addMidiCcAutomationDialog

0 commit comments

Comments
 (0)