Merge pull request #1003 from paulfd/keydelta
Handle extended CCs 140 and 141 (keydelta)
This commit is contained in:
commit
b4b9108735
10 changed files with 93 additions and 15 deletions
|
|
@ -20,15 +20,17 @@ namespace sfz {
|
|||
|
||||
enum ExtendedCCs {
|
||||
pitchBend = 128,
|
||||
channelAftertouch,
|
||||
polyphonicAftertouch,
|
||||
noteOnVelocity,
|
||||
noteOffVelocity,
|
||||
keyboardNoteNumber,
|
||||
keyboardNoteGate,
|
||||
unipolarRandom,
|
||||
bipolarRandom,
|
||||
alternate
|
||||
channelAftertouch = 129,
|
||||
polyphonicAftertouch = 130,
|
||||
noteOnVelocity = 131,
|
||||
noteOffVelocity = 132,
|
||||
keyboardNoteNumber = 133,
|
||||
keyboardNoteGate = 134,
|
||||
unipolarRandom = 135,
|
||||
bipolarRandom = 136,
|
||||
alternate = 137,
|
||||
keydelta = 140,
|
||||
absoluteKeydelta = 141,
|
||||
};
|
||||
|
||||
namespace config {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,13 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
|
|||
ASSERT(velocity >= 0 && velocity <= 1.0);
|
||||
|
||||
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;
|
||||
noteOnTimes[noteNumber] = internalClock + static_cast<unsigned>(delay);
|
||||
lastNotePlayed = noteNumber;
|
||||
|
|
@ -30,6 +36,8 @@ void sfz::MidiState::noteOnEvent(int delay, int noteNumber, float velocity) noex
|
|||
ccEvent(delay, ExtendedCCs::unipolarRandom, unipolarDist(Random::randomGenerator));
|
||||
ccEvent(delay, ExtendedCCs::bipolarRandom, bipolarDist(Random::randomGenerator));
|
||||
ccEvent(delay, ExtendedCCs::keyboardNoteGate, activeNotes > 0 ? 1.0f : 0.0f);
|
||||
ccEvent(delay, ExtendedCCs::keydelta, keydelta);
|
||||
ccEvent(delay, ExtendedCCs::absoluteKeydelta, std::abs(keydelta));
|
||||
activeNotes++;
|
||||
|
||||
ccEvent(delay, ExtendedCCs::alternate, alternate);
|
||||
|
|
@ -222,7 +230,7 @@ void sfz::MidiState::resetNoteStates() noexcept
|
|||
velocityOverride = 0.0f;
|
||||
activeNotes = 0;
|
||||
internalClock = 0;
|
||||
lastNotePlayed = 0;
|
||||
lastNotePlayed = -1;
|
||||
alternate = 0.0f;
|
||||
|
||||
auto setEvents = [] (EventVector& events, float value) {
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ private:
|
|||
/**
|
||||
* @brief Last note played
|
||||
*/
|
||||
int lastNotePlayed { 0 };
|
||||
int lastNotePlayed { -1 };
|
||||
|
||||
/**
|
||||
* @brief Current known values for the CCs.
|
||||
|
|
|
|||
|
|
@ -1724,6 +1724,8 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec,
|
|||
case ExtendedCCs::unipolarRandom: // fallthrough
|
||||
case ExtendedCCs::bipolarRandom: // fallthrough
|
||||
case ExtendedCCs::alternate:
|
||||
case ExtendedCCs::keydelta:
|
||||
case ExtendedCCs::absoluteKeydelta:
|
||||
conn->source = ModKey(ModId::PerVoiceController, id, p);
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ float baseVolumedB(const Region& region, const MidiState& midiState, int noteNum
|
|||
uint64_t sampleOffset(const Region& region, const MidiState& midiState) noexcept
|
||||
{
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ void Synth::Impl::handleGlobalOpcodes(const std::vector<Opcode>& members)
|
|||
|
||||
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;
|
||||
|
||||
const auto parseOpcode = [&](const Opcode& rawMember) {
|
||||
|
|
|
|||
|
|
@ -399,6 +399,7 @@ void Voice::Impl::updateExtendedCCValues() noexcept
|
|||
extendedCCValues_.bipolar = midiState.getCCValue(ExtendedCCs::bipolarRandom);
|
||||
extendedCCValues_.alternate = midiState.getCCValue(ExtendedCCs::alternate);
|
||||
extendedCCValues_.noteGate = midiState.getCCValue(ExtendedCCs::keyboardNoteGate);
|
||||
extendedCCValues_.keydelta = midiState.getCCValue(ExtendedCCs::keydelta);
|
||||
}
|
||||
|
||||
bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ struct ExtendedCCValues {
|
|||
float bipolar {};
|
||||
float noteGate {};
|
||||
float alternate {};
|
||||
float keydelta {};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -170,6 +170,20 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceI
|
|||
canShortcut = true;
|
||||
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::channelAftertouch: {
|
||||
const EventVector& events = ms.getCCEvents(p.cc);
|
||||
|
|
|
|||
|
|
@ -274,4 +274,54 @@ TEST_CASE("[CC] Extended CCs on offset and delay")
|
|||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue