Skip to content

Commit 920ba51

Browse files
committed
Sampler: Reset MIDI CC on song rewind
1 parent 64a6d52 commit 920ba51

4 files changed

Lines changed: 103 additions & 4 deletions

File tree

src/domain/devices/sampler_device.cpp

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,34 @@ void SamplerDevice::processMidiCc(uint8_t controller, uint8_t value, uint8_t cha
126126
{
127127
std::lock_guard<std::mutex> lock { m_mutex };
128128

129+
if (controller == 121) { // Reset All Controllers
130+
m_globalPan = m_manualGlobalPan;
131+
m_globalVolume = m_manualGlobalVolume;
132+
m_globalCutoff = m_manualGlobalCutoff;
133+
m_globalHpfCutoff = m_manualGlobalHpfCutoff;
134+
135+
for (auto && sample : m_samples) {
136+
if (sample) {
137+
sample->pan = sample->manualPan;
138+
sample->volume = sample->manualVolume;
139+
sample->cutoff = sample->manualCutoff;
140+
sample->hpfCutoff = sample->manualHpfCutoff;
141+
}
142+
}
143+
144+
for (auto && voice : m_voices) {
145+
if (voice.active && voice.sample) {
146+
voice.pan = m_globalPan;
147+
voice.volume = m_globalVolume;
148+
voice.cutoff = m_globalCutoff;
149+
voice.hpfCutoff = m_globalHpfCutoff;
150+
updateVoiceEffects(voice);
151+
}
152+
}
153+
emit dataChanged();
154+
return;
155+
}
156+
129157
if (m_channelMode) {
130158
// channel is 0-indexed (0-15)
131159
const size_t note = 36 + channel;
@@ -204,6 +232,33 @@ void SamplerDevice::processMidiAllNotesOff()
204232
voice.releasing = true;
205233
}
206234
}
235+
236+
m_globalPan = m_manualGlobalPan;
237+
m_globalVolume = m_manualGlobalVolume;
238+
m_globalCutoff = m_manualGlobalCutoff;
239+
m_globalHpfCutoff = m_manualGlobalHpfCutoff;
240+
241+
for (auto && sample : m_samples) {
242+
if (sample) {
243+
sample->pan = sample->manualPan;
244+
sample->volume = sample->manualVolume;
245+
sample->cutoff = sample->manualCutoff;
246+
sample->hpfCutoff = sample->manualHpfCutoff;
247+
}
248+
}
249+
250+
// Update active voices to reflect the reset values
251+
for (auto && voice : m_voices) {
252+
if (voice.active && voice.sample) {
253+
voice.pan = m_globalPan;
254+
voice.volume = m_globalVolume;
255+
voice.cutoff = m_globalCutoff;
256+
voice.hpfCutoff = m_globalHpfCutoff;
257+
updateVoiceEffects(voice);
258+
}
259+
}
260+
261+
emit dataChanged();
207262
}
208263

209264
void SamplerDevice::processAudio(float * output, uint32_t nFrames, uint32_t sampleRate)
@@ -304,6 +359,10 @@ void SamplerDevice::loadSample(uint8_t note, const std::string & filePath)
304359
sample->data = std::move(data);
305360
sample->cutoff = 1.0f;
306361
sample->hpfCutoff = 0.0f;
362+
sample->manualPan = sample->pan;
363+
sample->manualVolume = sample->volume;
364+
sample->manualCutoff = sample->cutoff;
365+
sample->manualHpfCutoff = sample->hpfCutoff;
307366

308367
std::lock_guard<std::mutex> lock { m_mutex };
309368
m_samples.at(note) = std::move(sample);
@@ -359,6 +418,7 @@ void SamplerDevice::setSamplePan(uint8_t note, float pan)
359418
std::lock_guard<std::mutex> lock { m_mutex };
360419
if (m_samples.at(note)) {
361420
m_samples.at(note)->pan = std::clamp(pan, 0.0f, 1.0f);
421+
m_samples.at(note)->manualPan = m_samples.at(note)->pan;
362422
for (auto && voice : m_voices) {
363423
if (voice.active && voice.note == note) {
364424
updateVoiceEffects(voice);
@@ -385,6 +445,7 @@ void SamplerDevice::setSampleVolume(uint8_t note, float volume)
385445
std::lock_guard<std::mutex> lock { m_mutex };
386446
if (m_samples.at(note)) {
387447
m_samples.at(note)->volume = std::clamp(volume, 0.0f, 1.0f);
448+
m_samples.at(note)->manualVolume = m_samples.at(note)->volume;
388449
for (auto && voice : m_voices) {
389450
if (voice.active && voice.note == note) {
390451
updateVoiceEffects(voice);
@@ -411,6 +472,7 @@ void SamplerDevice::setSampleCutoff(uint8_t note, float cutoff)
411472
std::lock_guard<std::mutex> lock { m_mutex };
412473
if (m_samples.at(note)) {
413474
m_samples.at(note)->cutoff = std::clamp(cutoff, 0.0f, 1.0f);
475+
m_samples.at(note)->manualCutoff = m_samples.at(note)->cutoff;
414476
for (auto && voice : m_voices) {
415477
if (voice.active && voice.note == note) {
416478
updateVoiceEffects(voice);
@@ -437,6 +499,7 @@ void SamplerDevice::setSampleHpfCutoff(uint8_t note, float cutoff)
437499
std::lock_guard<std::mutex> lock { m_mutex };
438500
if (m_samples.at(note)) {
439501
m_samples.at(note)->hpfCutoff = std::clamp(cutoff, 0.0f, 1.0f);
502+
m_samples.at(note)->manualHpfCutoff = m_samples.at(note)->hpfCutoff;
440503
for (auto && voice : m_voices) {
441504
if (voice.active && voice.note == note) {
442505
updateVoiceEffects(voice);
@@ -506,10 +569,10 @@ void SamplerDevice::serializeToXml(QXmlStreamWriter & writer) const
506569
}();
507570

508571
writer.writeAttribute(Constants::NahdXml::xmlKeySamplePath(), path);
509-
writer.writeAttribute(Constants::NahdXml::xmlKeyPan(), QString::number(std::round((s->pan * 200.0f) - 100.0f)));
510-
writer.writeAttribute(Constants::NahdXml::xmlKeyVolume(), QString::number(std::round(s->volume * 100.0f)));
511-
writer.writeAttribute(Constants::NahdXml::xmlKeyCutoff(), QString::number(std::round(s->cutoff * 100.0f)));
512-
writer.writeAttribute(Constants::NahdXml::xmlKeyHpfCutoff(), QString::number(std::round(s->hpfCutoff * 100.0f)));
572+
writer.writeAttribute(Constants::NahdXml::xmlKeyPan(), QString::number(std::round((s->manualPan * 200.0f) - 100.0f)));
573+
writer.writeAttribute(Constants::NahdXml::xmlKeyVolume(), QString::number(std::round(s->manualVolume * 100.0f)));
574+
writer.writeAttribute(Constants::NahdXml::xmlKeyCutoff(), QString::number(std::round(s->manualCutoff * 100.0f)));
575+
writer.writeAttribute(Constants::NahdXml::xmlKeyHpfCutoff(), QString::number(std::round(s->manualHpfCutoff * 100.0f)));
513576
writer.writeEndElement();
514577
}
515578
}

src/domain/devices/sampler_device.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ class SamplerDevice : public Device
6565
float volume = 1.0f; // 0.0 to 1.0
6666
float cutoff = 1.0f; // LPF cutoff: 0.0 to 1.0
6767
float hpfCutoff = 0.0f; // HPF cutoff: 0.0 to 1.0
68+
float manualPan = 0.5f;
69+
float manualVolume = 1.0f;
70+
float manualCutoff = 1.0f;
71+
float manualHpfCutoff = 0.0f;
6872
};
6973

7074
void loadSample(uint8_t note, const std::string & filePath);
@@ -132,6 +136,10 @@ class SamplerDevice : public Device
132136
float m_globalVolume = 1.0f;
133137
float m_globalCutoff = 1.0f;
134138
float m_globalHpfCutoff = 0.0f;
139+
float m_manualGlobalPan = 0.5f;
140+
float m_manualGlobalVolume = 1.0f;
141+
float m_manualGlobalCutoff = 1.0f;
142+
float m_manualGlobalHpfCutoff = 0.0f;
135143
bool m_channelMode = false;
136144
std::string m_projectPath;
137145
AudioFileReaderU m_audioFileReader;

src/unit_tests/sampler_test/sampler_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,33 @@ void SamplerTest::test_pan()
146146
QCOMPARE(buffer[1], 1.0f); // Right
147147
}
148148

149+
void SamplerTest::test_midiCcReset()
150+
{
151+
SamplerDevice sampler { std::make_unique<MockAudioFileReader>() };
152+
sampler.loadSample(60, "test.wav");
153+
154+
// Set manual pan
155+
sampler.setSamplePan(60, 0.75f);
156+
QVERIFY(qFuzzyCompare(sampler.samplePan(60), 0.75f));
157+
158+
// Enable channel mode and apply automation
159+
sampler.setChannelMode(true);
160+
sampler.processMidiCc(10, 0, 24); // MIDI Pan full left for Note 60 (Channel 24 -> Note 36+24=60)
161+
QVERIFY(qFuzzyCompare(sampler.samplePan(60) + 1.0f, 1.0f)); // Should show automated value (0.0)
162+
163+
// Reset via CC 121
164+
sampler.processMidiCc(121, 127, 24);
165+
QVERIFY(qFuzzyCompare(sampler.samplePan(60), 0.75f)); // Should be back to manual setting
166+
167+
// Apply automation again
168+
sampler.processMidiCc(10, 127, 24); // MIDI Pan full right
169+
QVERIFY(qFuzzyCompare(sampler.samplePan(60), 1.0f));
170+
171+
// Reset via Rewind (All Notes Off)
172+
sampler.processMidiAllNotesOff();
173+
QVERIFY(qFuzzyCompare(sampler.samplePan(60), 0.75f)); // Should be back to manual setting
174+
}
175+
149176
void SamplerTest::test_volume()
150177
{
151178
SamplerDevice sampler { std::make_unique<MockAudioFileReader>() };

src/unit_tests/sampler_test/sampler_test.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ private slots:
3939
void test_cutoff();
4040

4141
void test_channelMode();
42+
void test_midiCcReset();
4243

4344
void test_processAudio();
4445
};

0 commit comments

Comments
 (0)