Handle extended CCs 140 and 141 (keydelta)

This commit is contained in:
Paul Fd 2021-10-30 22:33:54 +02:00
parent 222df62eb0
commit aef980c94d
10 changed files with 93 additions and 15 deletions

View file

@ -20,15 +20,17 @@ namespace sfz {
enum ExtendedCCs { enum ExtendedCCs {
pitchBend = 128, pitchBend = 128,
channelAftertouch, channelAftertouch = 129,
polyphonicAftertouch, polyphonicAftertouch = 130,
noteOnVelocity, noteOnVelocity = 131,
noteOffVelocity, noteOffVelocity = 132,
keyboardNoteNumber, keyboardNoteNumber = 133,
keyboardNoteGate, keyboardNoteGate = 134,
unipolarRandom, unipolarRandom = 135,
bipolarRandom, bipolarRandom = 136,
alternate alternate = 137,
keydelta = 140,
absoluteKeydelta = 141,
}; };
namespace config { namespace config {

View file

@ -19,7 +19,13 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
ASSERT(velocity >= 0 && velocity <= 1.0); ASSERT(velocity >= 0 && velocity <= 1.0);
if (noteNumber >= 0 && noteNumber < 128) { if (noteNumber >= 0 && noteNumber < 128) {
velocityOverride = lastNoteVelocities[lastNotePlayed]; float keydelta { 0 };
if (lastNotePlayed >= 0) {
keydelta = static_cast<float>(noteNumber - lastNotePlayed);
velocityOverride = lastNoteVelocities[lastNotePlayed];
}
lastNoteVelocities[noteNumber] = velocity; lastNoteVelocities[noteNumber] = velocity;
noteOnTimes[noteNumber] = internalClock + static_cast<unsigned>(delay); noteOnTimes[noteNumber] = internalClock + static_cast<unsigned>(delay);
lastNotePlayed = noteNumber; lastNotePlayed = noteNumber;
@ -29,6 +35,8 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
ccEvent(delay, ExtendedCCs::unipolarRandom, unipolarDist(Random::randomGenerator)); ccEvent(delay, ExtendedCCs::unipolarRandom, unipolarDist(Random::randomGenerator));
ccEvent(delay, ExtendedCCs::bipolarRandom, bipolarDist(Random::randomGenerator)); ccEvent(delay, ExtendedCCs::bipolarRandom, bipolarDist(Random::randomGenerator));
ccEvent(delay, ExtendedCCs::keyboardNoteGate, activeNotes > 0 ? 1.0f : 0.0f); ccEvent(delay, ExtendedCCs::keyboardNoteGate, activeNotes > 0 ? 1.0f : 0.0f);
ccEvent(delay, ExtendedCCs::keydelta, keydelta);
ccEvent(delay, ExtendedCCs::absoluteKeydelta, std::abs(keydelta));
activeNotes++; activeNotes++;
ccEvent(delay, ExtendedCCs::alternate, alternate); ccEvent(delay, ExtendedCCs::alternate, alternate);
@ -235,7 +243,7 @@ void sfz::MidiState::reset() noexcept
velocityOverride = 0.0f; velocityOverride = 0.0f;
activeNotes = 0; activeNotes = 0;
internalClock = 0; internalClock = 0;
lastNotePlayed = 0; lastNotePlayed = -1;
noteStates.reset(); noteStates.reset();
absl::c_fill(noteOnTimes, 0); absl::c_fill(noteOnTimes, 0);
absl::c_fill(noteOffTimes, 0); absl::c_fill(noteOffTimes, 0);

View file

@ -241,7 +241,7 @@ private:
/** /**
* @brief Last note played * @brief Last note played
*/ */
int lastNotePlayed { 0 }; int lastNotePlayed { -1 };
/** /**
* @brief Current known values for the CCs. * @brief Current known values for the CCs.

View file

@ -1721,6 +1721,8 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec,
case ExtendedCCs::unipolarRandom: // fallthrough case ExtendedCCs::unipolarRandom: // fallthrough
case ExtendedCCs::bipolarRandom: // fallthrough case ExtendedCCs::bipolarRandom: // fallthrough
case ExtendedCCs::alternate: case ExtendedCCs::alternate:
case ExtendedCCs::keydelta:
case ExtendedCCs::absoluteKeydelta:
conn->source = ModKey(ModId::PerVoiceController, id, p); conn->source = ModKey(ModId::PerVoiceController, id, p);
break; break;
default: default:

View file

@ -25,9 +25,9 @@ float baseVolumedB(const Region& region, const MidiState& midiState, int noteNum
uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept
{ {
std::uniform_int_distribution<int64_t> offsetDistribution { 0, region.offsetRandom }; std::uniform_int_distribution<int64_t> offsetDistribution { 0, region.offsetRandom };
uint64_t finalOffset = region.offset + offsetDistribution(Random::randomGenerator); int64_t finalOffset = region.offset + offsetDistribution(Random::randomGenerator);
for (const auto& mod: region.offsetCC) for (const auto& mod: region.offsetCC)
finalOffset += static_cast<uint64_t>(mod.data * midiState.getCCValue(mod.cc)); finalOffset += static_cast<int64_t>(mod.data * midiState.getCCValue(mod.cc));
return Default::offset.bounds.clamp(finalOffset); return Default::offset.bounds.clamp(finalOffset);
} }

View file

@ -340,7 +340,7 @@ void Synth::Impl::handleGlobalOpcodes(const std::vector<Opcode>& members)
void Synth::Impl::handleGroupOpcodes(const std::vector<Opcode>& members, const std::vector<Opcode>& masterMembers) void Synth::Impl::handleGroupOpcodes(const std::vector<Opcode>& members, const std::vector<Opcode>& masterMembers)
{ {
absl::optional<int> groupIdx; absl::optional<int64_t> groupIdx;
absl::optional<unsigned> maxPolyphony; absl::optional<unsigned> maxPolyphony;
const auto parseOpcode = [&](const Opcode& rawMember) { const auto parseOpcode = [&](const Opcode& rawMember) {

View file

@ -399,6 +399,7 @@ void Voice::Impl::updateExtendedCCValues() noexcept
extendedCCValues_.bipolar = midiState.getCCValue(ExtendedCCs::bipolarRandom); extendedCCValues_.bipolar = midiState.getCCValue(ExtendedCCs::bipolarRandom);
extendedCCValues_.alternate = midiState.getCCValue(ExtendedCCs::alternate); extendedCCValues_.alternate = midiState.getCCValue(ExtendedCCs::alternate);
extendedCCValues_.noteGate = midiState.getCCValue(ExtendedCCs::keyboardNoteGate); extendedCCValues_.noteGate = midiState.getCCValue(ExtendedCCs::keyboardNoteGate);
extendedCCValues_.keydelta = midiState.getCCValue(ExtendedCCs::keydelta);
} }
bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept

View file

@ -26,6 +26,7 @@ struct ExtendedCCValues {
float bipolar {}; float bipolar {};
float noteGate {}; float noteGate {};
float alternate {}; float alternate {};
float keydelta {};
}; };
/** /**

View file

@ -170,6 +170,20 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceI
canShortcut = true; canShortcut = true;
break; break;
} }
case ExtendedCCs::keydelta: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? voice->getExtendedCCValues().keydelta : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::absoluteKeydelta: {
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
const float fillValue = voice ? std::abs(voice->getExtendedCCValues().keydelta) : 0.0f;
sfz::fill(buffer, quantize(fillValue));
canShortcut = true;
break;
}
case ExtendedCCs::pitchBend: // fallthrough case ExtendedCCs::pitchBend: // fallthrough
case ExtendedCCs::channelAftertouch: { case ExtendedCCs::channelAftertouch: {
const EventVector& events = ms.getCCEvents(p.cc); const EventVector& events = ms.getCCEvents(p.cc);

View file

@ -223,4 +223,54 @@ TEST_CASE("[CC] Extended CCs on offset and delay")
}; };
REQUIRE(messageList == expected); REQUIRE(messageList == expected);
} }
SECTION("CC140 - Keydelta")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"(
<region> delay=2 offset=200 delay_cc140=1 offset_cc140=100 sample=kick.wav
)");
synth.hdNoteOn(0, 60, 1.0f);
synth.hdNoteOn(0, 61, 1.0f);
synth.hdNoteOn(0, 59, 1.0f);
synth.dispatchMessage(client, 0, "/voice0/remaining_delay", "", nullptr);
synth.dispatchMessage(client, 0, "/voice1/remaining_delay", "", nullptr);
synth.dispatchMessage(client, 0, "/voice2/remaining_delay", "", nullptr);
synth.dispatchMessage(client, 0, "/voice0/source_position", "", nullptr);
synth.dispatchMessage(client, 0, "/voice1/source_position", "", nullptr);
synth.dispatchMessage(client, 0, "/voice2/source_position", "", nullptr);
std::vector<std::string> expected {
"/voice0/remaining_delay,i : { 96000 }",
"/voice1/remaining_delay,i : { 144000 }",
"/voice2/remaining_delay,i : { 0 }",
"/voice0/source_position,i : { 200 }",
"/voice1/source_position,i : { 300 }",
"/voice2/source_position,i : { 0 }",
};
REQUIRE(messageList == expected);
}
SECTION("CC141 - Absolute Keydelta")
{
synth.loadSfzString(fs::current_path() / "tests/TestFiles/extended_ccs.sfz", R"(
<region> delay=2 offset=200 delay_cc141=1 offset_cc141=100 sample=kick.wav
)");
synth.hdNoteOn(0, 60, 1.0f);
synth.hdNoteOn(0, 61, 1.0f);
synth.hdNoteOn(0, 59, 1.0f);
synth.dispatchMessage(client, 0, "/voice0/remaining_delay", "", nullptr);
synth.dispatchMessage(client, 0, "/voice1/remaining_delay", "", nullptr);
synth.dispatchMessage(client, 0, "/voice2/remaining_delay", "", nullptr);
synth.dispatchMessage(client, 0, "/voice0/source_position", "", nullptr);
synth.dispatchMessage(client, 0, "/voice1/source_position", "", nullptr);
synth.dispatchMessage(client, 0, "/voice2/source_position", "", nullptr);
std::vector<std::string> expected {
"/voice0/remaining_delay,i : { 96000 }",
"/voice1/remaining_delay,i : { 144000 }",
"/voice2/remaining_delay,i : { 192000 }",
"/voice0/source_position,i : { 200 }",
"/voice1/source_position,i : { 300 }",
"/voice2/source_position,i : { 400 }",
};
REQUIRE(messageList == expected);
}
} }