Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix modulation matrix when CC modulations are per voice #1179

Merged
merged 1 commit into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}