Fix modulation matrix when CC modulations are per voice
This commit is contained in:
parent
d470f7004e
commit
507c61d458
9 changed files with 142 additions and 55 deletions
|
|
@ -18,21 +18,6 @@
|
|||
|
||||
namespace sfz {
|
||||
|
||||
enum ExtendedCCs {
|
||||
pitchBend = 128,
|
||||
channelAftertouch = 129,
|
||||
polyphonicAftertouch = 130,
|
||||
noteOnVelocity = 131,
|
||||
noteOffVelocity = 132,
|
||||
keyboardNoteNumber = 133,
|
||||
keyboardNoteGate = 134,
|
||||
unipolarRandom = 135,
|
||||
bipolarRandom = 136,
|
||||
alternate = 137,
|
||||
keydelta = 140,
|
||||
absoluteKeydelta = 141,
|
||||
};
|
||||
|
||||
namespace config {
|
||||
constexpr float defaultSampleRate { 48000 };
|
||||
constexpr float maxSampleRate { 192000 };
|
||||
|
|
|
|||
|
|
@ -18,21 +18,6 @@
|
|||
|
||||
namespace sfz {
|
||||
|
||||
enum ExtendedCCs {
|
||||
pitchBend = 128,
|
||||
channelAftertouch = 129,
|
||||
polyphonicAftertouch = 130,
|
||||
noteOnVelocity = 131,
|
||||
noteOffVelocity = 132,
|
||||
keyboardNoteNumber = 133,
|
||||
keyboardNoteGate = 134,
|
||||
unipolarRandom = 135,
|
||||
bipolarRandom = 136,
|
||||
alternate = 137,
|
||||
keydelta = 140,
|
||||
absoluteKeydelta = 141,
|
||||
};
|
||||
|
||||
namespace config {
|
||||
constexpr float defaultSampleRate { 48000 };
|
||||
constexpr float maxSampleRate { 192000 };
|
||||
|
|
|
|||
|
|
@ -36,8 +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));
|
||||
ccEvent(delay, AriaExtendedCCs::keydelta, keydelta);
|
||||
ccEvent(delay, AriaExtendedCCs::absoluteKeydelta, std::abs(keydelta));
|
||||
activeNotes++;
|
||||
|
||||
ccEvent(delay, ExtendedCCs::alternate, alternate);
|
||||
|
|
|
|||
|
|
@ -1690,8 +1690,13 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec,
|
|||
// search an existing connection of same CC number and target
|
||||
// if it exists, modify, otherwise create
|
||||
auto it = std::find_if(connections.begin(), connections.end(),
|
||||
[ccNumber, &target](const Connection& x) -> bool
|
||||
[ccNumber, &target, this](const Connection& x) -> bool
|
||||
{
|
||||
if (ccModulationIsPerVoice(ccNumber))
|
||||
return x.source.id() == ModId::PerVoiceController &&
|
||||
x.source.region() == id &&
|
||||
x.source.parameters().cc == ccNumber &&
|
||||
x.target == target;
|
||||
return x.source.id() == ModId::Controller &&
|
||||
x.source.parameters().cc == ccNumber &&
|
||||
x.target == target;
|
||||
|
|
@ -1730,21 +1735,10 @@ bool sfz::Region::processGenericCc(const Opcode& opcode, OpcodeSpec<float> spec,
|
|||
break;
|
||||
}
|
||||
|
||||
switch (p.cc) {
|
||||
case ExtendedCCs::noteOnVelocity: // fallthrough
|
||||
case ExtendedCCs::noteOffVelocity: // fallthrough
|
||||
case ExtendedCCs::keyboardNoteNumber: // fallthrough
|
||||
case ExtendedCCs::keyboardNoteGate: // fallthrough
|
||||
case ExtendedCCs::unipolarRandom: // fallthrough
|
||||
case ExtendedCCs::bipolarRandom: // fallthrough
|
||||
case ExtendedCCs::alternate:
|
||||
case ExtendedCCs::keydelta:
|
||||
case ExtendedCCs::absoluteKeydelta:
|
||||
if (ccModulationIsPerVoice(p.cc)) {
|
||||
conn->source = ModKey(ModId::PerVoiceController, id, p);
|
||||
break;
|
||||
default:
|
||||
} else {
|
||||
conn->source = ModKey(ModId::Controller, {}, p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1850,13 +1844,23 @@ sfz::Region::Connection& sfz::Region::getOrCreateConnection(const ModKey& source
|
|||
|
||||
sfz::Region::Connection* sfz::Region::getConnectionFromCC(int sourceCC, const ModKey& target)
|
||||
{
|
||||
if (ccModulationIsPerVoice(sourceCC)) {
|
||||
for (sfz::Region::Connection& conn : connections) {
|
||||
if (conn.source.id() == sfz::ModId::Controller && conn.target == target) {
|
||||
auto p = conn.source.parameters();
|
||||
if (conn.source.id() == sfz::ModId::PerVoiceController && conn.target == target && conn.source.region() == id) {
|
||||
const auto& p = conn.source.parameters();
|
||||
if (p.cc == sourceCC)
|
||||
return &conn;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (sfz::Region::Connection& conn : connections) {
|
||||
if (conn.source.id() == sfz::ModId::Controller && conn.target == target) {
|
||||
const auto& p = conn.source.parameters();
|
||||
if (p.cc == sourceCC)
|
||||
return &conn;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,6 +210,78 @@ inline CXX14_CONSTEXPR uint8_t offsetAndClampKey(uint8_t key, int offset)
|
|||
return clamp<uint8_t>(static_cast<uint8_t>(offsetKey), 0, 127);
|
||||
}
|
||||
|
||||
enum ExtendedCCs {
|
||||
pitchBend = 128,
|
||||
channelAftertouch,
|
||||
polyphonicAftertouch,
|
||||
noteOnVelocity,
|
||||
noteOffVelocity,
|
||||
keyboardNoteNumber,
|
||||
keyboardNoteGate,
|
||||
unipolarRandom,
|
||||
bipolarRandom,
|
||||
alternate,
|
||||
extendedCCupperBound
|
||||
};
|
||||
|
||||
enum AriaExtendedCCs {
|
||||
keydelta = 140,
|
||||
absoluteKeydelta,
|
||||
ariaCCupperBound
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Check if a CC is an ARIA-defined extended CC
|
||||
*
|
||||
* @param cc
|
||||
* @return bool
|
||||
*/
|
||||
inline CXX14_CONSTEXPR bool isAriaExtendedCC(int cc) {
|
||||
if (cc >= AriaExtendedCCs::keydelta && cc < AriaExtendedCCs::ariaCCupperBound)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a CC is an extended CC (ARIA or not)
|
||||
*
|
||||
* @param cc
|
||||
* @return bool
|
||||
*/
|
||||
inline CXX14_CONSTEXPR bool isExtendedCC(int cc) {
|
||||
if (isAriaExtendedCC(cc))
|
||||
return true;
|
||||
|
||||
if (cc >= ExtendedCCs::pitchBend && cc < ExtendedCCs::extendedCCupperBound)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if a CC applies per voice modulation
|
||||
*
|
||||
* @param cc
|
||||
* @return bool
|
||||
*/
|
||||
inline CXX14_CONSTEXPR bool ccModulationIsPerVoice(int cc) {
|
||||
switch (cc) {
|
||||
case ExtendedCCs::noteOnVelocity: // fallthrough
|
||||
case ExtendedCCs::noteOffVelocity: // fallthrough
|
||||
case ExtendedCCs::keyboardNoteNumber: // fallthrough
|
||||
case ExtendedCCs::keyboardNoteGate: // fallthrough
|
||||
case ExtendedCCs::unipolarRandom: // fallthrough
|
||||
case ExtendedCCs::bipolarRandom: // fallthrough
|
||||
case ExtendedCCs::alternate:
|
||||
case AriaExtendedCCs::keydelta:
|
||||
case AriaExtendedCCs::absoluteKeydelta:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace literals {
|
||||
inline float operator""_norm(unsigned long long int value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -401,7 +401,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);
|
||||
extendedCCValues_.keydelta = midiState.getCCValue(AriaExtendedCCs::keydelta);
|
||||
}
|
||||
|
||||
bool Voice::startVoice(Layer* layer, int delay, const TriggerEvent& event) noexcept
|
||||
|
|
|
|||
|
|
@ -170,14 +170,14 @@ void ControllerSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceI
|
|||
canShortcut = true;
|
||||
break;
|
||||
}
|
||||
case ExtendedCCs::keydelta: {
|
||||
case AriaExtendedCCs::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: {
|
||||
case AriaExtendedCCs::absoluteKeydelta: {
|
||||
const auto voice = impl_->voiceManager_->getVoiceById(voiceId);
|
||||
const float fillValue = voice ? std::abs(voice->getExtendedCCValues().keydelta) : 0.0f;
|
||||
sfz::fill(buffer, quantize(fillValue));
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ set(SFIZZ_TEST_SOURCES
|
|||
PolyphonyT.cpp
|
||||
RegionActivationT.cpp
|
||||
RegionValueComputationsT.cpp
|
||||
SfzHelpersT.cpp
|
||||
# If we're tweaking the curves this kind of tests does not make sense
|
||||
# Use integration tests with comparison curves
|
||||
# ADSREnvelopeT.cpp
|
||||
|
|
|
|||
40
tests/SfzHelpersT.cpp
Normal file
40
tests/SfzHelpersT.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
// This code is part of the sfizz library and is licensed under a BSD 2-clause
|
||||
// license. You should have receive a LICENSE.md file along with the code.
|
||||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "catch2/catch.hpp"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include <set>
|
||||
|
||||
template<class F>
|
||||
void checkAllCCs(const std::set<int>& validCCs, F&& check)
|
||||
{
|
||||
for (int cc = 0; cc < sfz::config::numCCs; ++cc) {
|
||||
INFO("The number is " << cc);
|
||||
if (validCCs.find(cc) != validCCs.end())
|
||||
REQUIRE(check(cc));
|
||||
else
|
||||
REQUIRE_FALSE(check(cc));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("[CC] Extended CCs")
|
||||
{
|
||||
std::set<int> supportedExtendedCCs { 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 140, 141};
|
||||
checkAllCCs(supportedExtendedCCs, sfz::isExtendedCC);
|
||||
}
|
||||
|
||||
TEST_CASE("[CC] ARIA Extended CCs")
|
||||
{
|
||||
std::set<int> supportedAriaExtendedCCs { 140, 141 };
|
||||
checkAllCCs(supportedAriaExtendedCCs, sfz::isAriaExtendedCC);
|
||||
}
|
||||
|
||||
TEST_CASE("[CC] Per-Voice Extended CCs")
|
||||
{
|
||||
std::set<int> perVoiceSupportedCCs { 131, 132, 133, 134, 135, 136, 137, 140, 141 };
|
||||
checkAllCCs(perVoiceSupportedCCs, sfz::ccModulationIsPerVoice);
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue