Skip to content

Commit

Permalink
Fix modulation matrix when CC modulations are per voice
Browse files Browse the repository at this point in the history
  • Loading branch information
PythonBlue authored and paulfd committed Aug 7, 2023
1 parent d470f70 commit 507c61d
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 55 deletions.
15 changes: 0 additions & 15 deletions src/Config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
15 changes: 0 additions & 15 deletions src/sfizz/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
4 changes: 2 additions & 2 deletions src/sfizz/MidiState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
44 changes: 24 additions & 20 deletions src/sfizz/Region.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1730,22 +1735,11 @@ 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;
}
}
}

return true;
Expand Down Expand Up @@ -1850,11 +1844,21 @@ sfz::Region::Connection& sfz::Region::getOrCreateConnection(const ModKey& source

sfz::Region::Connection* sfz::Region::getConnectionFromCC(int sourceCC, const ModKey& target)
{
for (sfz::Region::Connection& conn : connections) {
if (conn.source.id() == sfz::ModId::Controller && conn.target == target) {
auto p = conn.source.parameters();
if (p.cc == sourceCC)
return &conn;
if (ccModulationIsPerVoice(sourceCC)) {
for (sfz::Region::Connection& conn : connections) {
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;
Expand Down
72 changes: 72 additions & 0 deletions src/sfizz/SfzHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 1 addition & 1 deletion src/sfizz/Voice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/sfizz/modulations/sources/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions tests/SfzHelpersT.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 507c61d

Please sign in to comment.