Skip to content

Commit 2cd2ccd

Browse files
committed
Add transpose per note column
1 parent f2422c7 commit 2cd2ccd

File tree

12 files changed

+104
-6
lines changed

12 files changed

+104
-6
lines changed

CHANGELOG

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

66
New features:
77

8+
* Add transpose per note column
9+
- Located in column settings
10+
- The value is added to the track-level transpose
11+
812
* Add song transposition feature
913
- Fixes also drum track not taken into account properly
1014

src/application/models/column_settings_model.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ void ColumnSettingsModel::reset()
3434
m_settings = {};
3535

3636
emit delayChanged();
37+
emit transposeChanged();
3738
emit chordNote1OffsetChanged();
3839
emit chordNote1VelocityChanged();
3940
emit chordNote1DelayChanged();
@@ -57,6 +58,7 @@ void ColumnSettingsModel::requestData()
5758
void ColumnSettingsModel::setColumnSettings(const ColumnSettings & settings)
5859
{
5960
const bool delayChanged = m_settings.delay != settings.delay;
61+
const bool transposeChanged = m_settings.transpose != settings.transpose;
6062
const bool note1OffsetChanged = m_settings.chordAutomationSettings.note1.offset != settings.chordAutomationSettings.note1.offset;
6163
const bool note1VelocityChanged = m_settings.chordAutomationSettings.note1.velocity != settings.chordAutomationSettings.note1.velocity;
6264
const bool note1DelayChanged = m_settings.chordAutomationSettings.note1.delay != settings.chordAutomationSettings.note1.delay;
@@ -76,6 +78,9 @@ void ColumnSettingsModel::setColumnSettings(const ColumnSettings & settings)
7678
if (delayChanged) {
7779
emit this->delayChanged();
7880
}
81+
if (transposeChanged) {
82+
emit this->transposeChanged();
83+
}
7984
if (note1OffsetChanged) {
8085
emit chordNote1OffsetChanged();
8186
}
@@ -156,6 +161,19 @@ void ColumnSettingsModel::setDelay(int delay)
156161
}
157162
}
158163

164+
int ColumnSettingsModel::transpose() const
165+
{
166+
return m_settings.transpose;
167+
}
168+
169+
void ColumnSettingsModel::setTranspose(int transpose)
170+
{
171+
if (m_settings.transpose != transpose) {
172+
m_settings.transpose = transpose;
173+
emit transposeChanged();
174+
}
175+
}
176+
159177
qint8 ColumnSettingsModel::chordNote1Offset() const
160178
{
161179
return m_settings.chordAutomationSettings.note1.offset;

src/application/models/column_settings_model.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ColumnSettingsModel : public QObject
3030
Q_PROPERTY(quint64 columnIndex READ columnIndex WRITE setColumnIndex NOTIFY columnIndexChanged)
3131

3232
Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged)
33+
Q_PROPERTY(int transpose READ transpose WRITE setTranspose NOTIFY transposeChanged)
3334

3435
Q_PROPERTY(qint8 chordNote1Offset READ chordNote1Offset WRITE setChordNote1Offset NOTIFY chordNote1OffsetChanged)
3536
Q_PROPERTY(quint8 chordNote1Velocity READ chordNote1Velocity WRITE setChordNote1Velocity NOTIFY chordNote1VelocityChanged)
@@ -64,6 +65,9 @@ class ColumnSettingsModel : public QObject
6465
int delay() const;
6566
void setDelay(int delay);
6667

68+
int transpose() const;
69+
void setTranspose(int transpose);
70+
6771
qint8 chordNote1Offset() const;
6872
void setChordNote1Offset(qint8 offset);
6973

@@ -108,6 +112,7 @@ class ColumnSettingsModel : public QObject
108112
void columnIndexChanged();
109113

110114
void delayChanged();
115+
void transposeChanged();
111116

112117
void chordNote1OffsetChanged();
113118
void chordNote1VelocityChanged();

src/domain/column.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Column::ColumnSettingsS Column::settings() const
6262

6363
bool Column::hasData() const
6464
{
65-
return !name().empty() || m_settings->chordAutomationSettings.isEnabled() || std::ranges::any_of(m_lines, [](auto && line) {
65+
return !name().empty() || m_settings->isEnabled() || std::ranges::any_of(m_lines, [](auto && line) {
6666
return line->hasData();
6767
});
6868
}
@@ -243,7 +243,7 @@ void Column::serializeToXml(QXmlStreamWriter & writer) const
243243
writer.writeAttribute(Constants::NahdXml::xmlKeyName(), QString::fromStdString(name()));
244244
writer.writeAttribute(Constants::NahdXml::xmlKeyLineCount(), QString::number(lineCount()));
245245

246-
if (m_settings->chordAutomationSettings.isEnabled()) {
246+
if (m_settings->isEnabled()) {
247247
m_settings->serializeToXml(writer);
248248
}
249249

src/domain/column_settings.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ void ColumnSettings::serializeToXml(QXmlStreamWriter & writer) const
3535
writer.writeStartElement(Constants::NahdXml::xmlKeyColumnSettings());
3636

3737
writer.writeAttribute(Constants::NahdXml::xmlKeyDelay(), QString::number(delay.count()));
38+
writer.writeAttribute(Constants::NahdXml::xmlKeyTranspose(), QString::number(transpose));
3839

3940
writer.writeAttribute(Constants::NahdXml::xmlKeyChordNote1Offset(), QString::number(chordAutomationSettings.note1.offset));
4041
writer.writeAttribute(Constants::NahdXml::xmlKeyChordNote1Velocity(), QString::number(chordAutomationSettings.note1.velocity));
@@ -60,6 +61,7 @@ ColumnSettings::ColumnSettingsU ColumnSettings::deserializeFromXml(QXmlStreamRea
6061
auto settings = std::make_unique<ColumnSettings>();
6162

6263
settings->delay = std::chrono::milliseconds { Utils::Xml::readIntAttribute(reader, Constants::NahdXml::xmlKeyDelay(), false).value_or(0) };
64+
settings->transpose = Utils::Xml::readIntAttribute(reader, Constants::NahdXml::xmlKeyTranspose(), false).value_or(0);
6365

6466
settings->chordAutomationSettings.note1.offset = Utils::Xml::readIntAttribute(reader, Constants::NahdXml::xmlKeyChordNote1Offset(), false).value_or(0);
6567
settings->chordAutomationSettings.note1.velocity = Utils::Xml::readUIntAttribute(reader, Constants::NahdXml::xmlKeyChordNote1Velocity(), false).value_or(100);
@@ -80,8 +82,9 @@ ColumnSettings::ColumnSettingsU ColumnSettings::deserializeFromXml(QXmlStreamRea
8082

8183
QString ColumnSettings::toString() const
8284
{
83-
return QStringLiteral("ColumnSettings(delay=%1, chordAutomation: note1(offset=%2, velocity=%3, delay=%4), note2(offset=%5, velocity=%6, delay=%7), note3(offset=%8, velocity=%9, delay=%10))")
85+
return QStringLiteral("ColumnSettings(delay=%1, transpose=%2, chordAutomation: note1(offset=%3, velocity=%4, delay=%5), note2(offset=%6, velocity=%7, delay=%8), note3(offset=%9, velocity=%10, delay=%11))")
8486
.arg(delay.count())
87+
.arg(transpose)
8588
.arg(chordAutomationSettings.note1.offset)
8689
.arg(chordAutomationSettings.note1.velocity)
8790
.arg(chordAutomationSettings.note1.delay)

src/domain/column_settings.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class ColumnSettings
3636

3737
std::chrono::milliseconds delay { 0 };
3838

39+
int transpose { 0 };
40+
3941
struct ChordAutomationSettings
4042
{
4143
struct ChordNote
@@ -62,6 +64,11 @@ class ColumnSettings
6264

6365
ChordAutomationSettings chordAutomationSettings;
6466

67+
bool isEnabled() const
68+
{
69+
return delay.count() || transpose || chordAutomationSettings.isEnabled();
70+
}
71+
6572
void serializeToXml(QXmlStreamWriter & writer) const;
6673
using ColumnSettingsU = std::unique_ptr<ColumnSettings>;
6774
static ColumnSettingsU deserializeFromXml(QXmlStreamReader & reader);

src/domain/song.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,10 @@ Song::EventList Song::applyInstrumentsOnEvents(EventListCR events) const
801801
event->applyDelay(totalDelay - delayOffset, msPerTick);
802802
event->applyVelocityJitter(event->instrument()->settings().midiEffects.velocityJitter);
803803
event->applyVelocityKeyTrack(event->instrument()->settings().midiEffects.velocityKeyTrack, event->instrument()->settings().midiEffects.velocityKeyTrackOffset);
804-
event->transpose(event->instrument()->settings().transpose);
804+
const int instrumentTranspose = event->instrument()->settings().transpose;
805+
const auto colSettings = columnSettings(noteData->track(), noteData->column());
806+
const int columnTranspose = colSettings ? colSettings->transpose : 0;
807+
event->transpose(instrumentTranspose + columnTranspose);
805808
}
806809
}
807810
} else if (event->type() == Event::Type::MidiCcData) {

src/unit_tests/song_test/song_test.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,30 @@ void SongTest::test_renderToEvents_transposeSet_shouldApplyTranspose()
525525
QCOMPARE(noteOn->noteData()->note(), 49);
526526
}
527527

528+
void SongTest::test_renderToEvents_columnTransposeSet_shouldApplyTranspose()
529+
{
530+
Song song;
531+
532+
const auto instrument1 = std::make_shared<Instrument>("Instrument1");
533+
song.setInstrument(0, instrument1);
534+
auto instSettings = instrument1->settings();
535+
instSettings.transpose = 2;
536+
instrument1->setSettings(instSettings);
537+
538+
const auto colSettings = std::make_shared<ColumnSettings>();
539+
colSettings->transpose = 10;
540+
song.setColumnSettings(0, 0, colSettings);
541+
542+
const Position noteOnPosition = { 0, 0, 0, 0, 0 };
543+
song.noteDataAtPosition(noteOnPosition)->setAsNoteOn(60, 100);
544+
545+
const auto events = song.renderToEvents(std::make_shared<AutomationService>(std::make_shared<PropertyService>()), std::make_shared<SideChainService>(), 0);
546+
QCOMPARE(events.size(), 4);
547+
const auto noteOn = events.at(1);
548+
// 60 + 2 (instrument) + 10 (column) = 72
549+
QCOMPARE(noteOn->noteData()->note(), 72);
550+
}
551+
528552
void SongTest::test_renderToEvents_velocityJitterSet_shouldApplyVelocityJitter()
529553
{
530554
Song song;

src/unit_tests/song_test/song_test.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ private slots:
5858
void test_renderToEvents_chordAutomation_shouldRenderChordNotes();
5959

6060
void test_renderToEvents_transposeSet_shouldApplyTranspose();
61+
void test_renderToEvents_columnTransposeSet_shouldApplyTranspose();
6162
void test_renderToEvents_velocityJitterSet_shouldApplyVelocityJitter();
6263
void test_renderToEvents_velocityKeyTrackSet_shouldScaleVelocity();
6364
void test_renderToEvents_velocityKeyTrackOffsetSet_shouldScaleVelocity();

src/unit_tests/xml_serialization_test/xml_serialization_test.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ void XmlSerializationTest::test_toXmlFromXml_trackName_shouldLoadTrackName()
111111
void XmlSerializationTest::test_toXmlFromXml_columnSettings_shouldSaveAndLoad()
112112
{
113113
EditorService editorServiceOut { std::make_shared<SelectionService>(), std::make_shared<SettingsService>(), std::make_shared<AutomationService>(std::make_shared<PropertyService>()) };
114-
auto settingsOut = std::make_shared<ColumnSettings>();
114+
const auto settingsOut = std::make_shared<ColumnSettings>();
115+
settingsOut->delay = std::chrono::milliseconds { 123 };
116+
settingsOut->transpose = -12;
115117
settingsOut->chordAutomationSettings.note1.offset = 4;
116118
settingsOut->chordAutomationSettings.note1.velocity = 80;
117119
settingsOut->chordAutomationSettings.note2.offset = 7;
@@ -130,6 +132,8 @@ void XmlSerializationTest::test_toXmlFromXml_columnSettings_shouldSaveAndLoad()
130132
const auto settingsIn = editorServiceIn.columnSettings(1, 0);
131133

132134
QVERIFY(settingsIn);
135+
QCOMPARE(settingsIn->delay, settingsOut->delay);
136+
QCOMPARE(settingsIn->transpose, settingsOut->transpose);
133137
QCOMPARE(settingsIn->chordAutomationSettings.note1.offset, settingsOut->chordAutomationSettings.note1.offset);
134138
QCOMPARE(settingsIn->chordAutomationSettings.note1.velocity, settingsOut->chordAutomationSettings.note1.velocity);
135139
QCOMPARE(settingsIn->chordAutomationSettings.note2.offset, settingsOut->chordAutomationSettings.note2.offset);

0 commit comments

Comments
 (0)