This commit is contained in:
Paul Ferrand 2020-11-08 23:59:28 +01:00 committed by Jean Pierre Cimalando
parent 31103d11a2
commit 171a5e5083
14 changed files with 4390 additions and 1946 deletions

View file

@ -144,9 +144,9 @@ public:
#ifndef NDEBUG
~BufferPool()
{
DBG("Max buffers used: " << maxBuffersUsed);
DBG("Max index buffers used: " << maxIndexBuffersUsed);
DBG("Max stereo buffers used: " << maxStereoBuffersUsed);
// DBG("Max buffers used: " << maxBuffersUsed);
// DBG("Max index buffers used: " << maxIndexBuffersUsed);
// DBG("Max stereo buffers used: " << maxStereoBuffersUsed);
}
#endif

View file

@ -9,6 +9,7 @@
#include "SfzHelpers.h"
#include <vector>
#include <absl/algorithm/container.h>
#include <absl/types/optional.h>
namespace sfz {
/**
@ -57,6 +58,22 @@ public:
}
}
/**
* @brief Returns the held object at the index, or a default value if not present
*
* @param index
* @return const ValueType&
*/
absl::optional<ValueType> get(int index) const noexcept
{
auto it = absl::c_lower_bound(container, index, CCDataComparator<ValueType> {});
if (it == container.end() || it->cc != index) {
return {};
} else {
return it->data;
}
}
/**
* @brief Get the value at index or emplace a new one if not present
*

View file

@ -207,8 +207,8 @@ namespace Default
constexpr Range<int> pitchVeltrackRange { -12000, 12000 };
constexpr int transpose { 0 };
constexpr Range<int> transposeRange { -127, 127 };
constexpr int tune { 0 };
constexpr Range<int> tuneRange { -12000, 12000 }; // ±100 in SFZv1, more in ARIA
constexpr float tune { 0 };
constexpr Range<float> tuneRange { -12000, 12000 }; // ±100 in SFZv1, more in ARIA
constexpr Range<float> tuneCCRange { -12000, 12000 };
constexpr Range<int> bendBoundRange { -12000, 12000 };
constexpr Range<int> bendStepRange { 1, 1200 };

View file

@ -27,8 +27,6 @@ public:
private:
template <char Tag>
sfizz_arg_t make_arg(OscDecayedType<Tag> value);
private:
void* data_ = nullptr;
sfizz_receive_t* receive_ = nullptr;
};

View file

@ -56,7 +56,7 @@ inline void Client::receive(int delay, const char* path, OscDecayedType<Sig>...
typedef struct Nothing {} type; \
typedef type decayed_type; \
static inline sfizz_arg_t make_arg(decayed_type v) { \
sfizz_arg_t a; (void)v; return a; \
sfizz_arg_t a {}; (void)v; return a; \
} \
}

View file

@ -1936,3 +1936,31 @@ bool sfz::Region::disabled() const noexcept
{
return (sampleEnd == 0);
}
absl::optional<float> sfz::Region::ccModDepth(int cc, ModId id) const noexcept
{
const ModKey target = ModKey::createNXYZ(id, getId());
for (const sfz::Region::Connection& conn : connections) {
if (conn.source.id() == sfz::ModId::Controller && conn.target == target) {
auto p = conn.source.parameters();
if (p.cc == cc)
return conn.sourceDepth;
}
}
return {};
}
absl::optional<sfz::ModKey::Parameters> sfz::Region::ccModParameters(int cc, ModId id) const noexcept
{
const ModKey target = ModKey::createNXYZ(id, getId());
for (const sfz::Region::Connection& conn : connections) {
if (conn.source.id() == sfz::ModId::Controller && conn.target == target) {
auto p = conn.source.parameters();
if (p.cc == cc)
return p;
}
}
return {};
}

View file

@ -303,6 +303,24 @@ struct Region {
*/
bool disabled() const noexcept;
/**
* @brief Extract the source depth modifier for a given cc and id.
*
* @param cc
* @param id
* @return absl::optional<float>
*/
absl::optional<float> ccModDepth(int cc, ModId id) const noexcept;
/**
* @brief Extract the parameters for a given modulation cc and id.
*
* @param cc
* @param id
* @return float
*/
absl::optional<ModKey::Parameters> ccModParameters(int cc, ModId id) const noexcept;
const NumericId<Region> id;
// Sound source: sample playback
@ -412,7 +430,7 @@ struct Region {
float pitchRandom { Default::pitchRandom }; // pitch_random
int pitchVeltrack { Default::pitchVeltrack }; // pitch_veltrack
int transpose { Default::transpose }; // transpose
int tune { Default::tune }; // tune
float tune { Default::tune }; // tune
int bendUp { Default::bendUp };
int bendDown { Default::bendDown };
int bendStep { Default::bendStep };

View file

@ -676,7 +676,10 @@ void Synth::Impl::finalizeSfzLoad()
++currentRegionIndex;
}
DBG("Removing " << (regions_.size() - currentRegionCount) << " out of " << regions_.size() << " regions");
if (currentRegionCount < regions_.size()) {
DBG("Removing " << (regions_.size() - currentRegionCount)
<< " out of " << regions_.size() << " regions");
}
regions_.resize(currentRegionCount);
// collect all CCs used in regions, with matrix not yet connected

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,8 @@
project(sfizz)
set(SFIZZ_TEST_SOURCES
RegionT.cpp
DirectRegionT.cpp
RegionValuesT.cpp
TestHelpers.h
TestHelpers.cpp
ParsingT.cpp

112
tests/DirectRegionT.cpp Normal file
View file

@ -0,0 +1,112 @@
// 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 "TestHelpers.h"
#include "sfizz/MidiState.h"
#include "sfizz/Region.h"
#include "sfizz/SfzHelpers.h"
#include "sfizz/modulations/ModId.h"
#include "sfizz/modulations/ModKey.h"
#include "catch2/catch.hpp"
#include <stdexcept>
using namespace Catch::literals;
using namespace sfz::literals;
using namespace sfz;
TEST_CASE("[Direct Region Tests] amp_velcurve")
{
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "amp_velcurve_6", "0.4" });
REQUIRE(region.velocityPoints.back() == std::pair<uint8_t, float>(6, 0.4f));
region.parseOpcode({ "amp_velcurve_127", "-1.0" });
REQUIRE(region.velocityPoints.back() == std::pair<uint8_t, float>(127, 0.0f));
region.parseOpcode({ "amp_velcurve_008", "0.3" });
REQUIRE(region.velocityPoints.back() == std::pair<uint8_t, float>(8, 0.3f));
region.parseOpcode({ "amp_velcurve_064", "0.9" });
REQUIRE(region.velocityPoints.back() == std::pair<uint8_t, float>(64, 0.9f));
}
TEST_CASE("[Direct Region Tests] Release and release key")
{
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "lokey", "63" });
region.parseOpcode({ "hikey", "65" });
region.parseOpcode({ "sample", "*sine" });
SECTION("Release key without sustain")
{
region.parseOpcode({ "trigger", "release_key" });
midiState.ccEvent(0, 64, 0.0f);
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
REQUIRE( region.registerNoteOff(63, 0.5f, 0.0f) );
}
SECTION("Release key with sustain")
{
region.parseOpcode({ "trigger", "release_key" });
midiState.ccEvent(0, 64, 1.0f);
REQUIRE( !region.registerCC(64, 1.0f) );
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
REQUIRE( region.registerNoteOff(63, 0.5f, 0.0f) );
}
SECTION("Release without sustain")
{
region.parseOpcode({ "trigger", "release" });
midiState.ccEvent(0, 64, 0.0f);
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
REQUIRE( region.registerNoteOff(63, 0.5f, 0.0f) );
}
SECTION("Release with sustain")
{
region.parseOpcode({ "trigger", "release" });
midiState.ccEvent(0, 64, 1.0f);
midiState.noteOnEvent(0, 63, 0.5f);
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
REQUIRE( !region.registerNoteOff(63, 0.5f, 0.0f) );
REQUIRE( region.delayedReleases.size() == 1 );
std::vector<std::pair<int, float>> expected = {
{ 63, 0.5f }
};
REQUIRE( region.delayedReleases == expected );
}
SECTION("Release with sustain and 2 notes")
{
region.parseOpcode({ "trigger", "release" });
midiState.ccEvent(0, 64, 1.0f);
midiState.noteOnEvent(0, 63, 0.5f);
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
midiState.noteOnEvent(0, 64, 0.6f);
REQUIRE( !region.registerNoteOn(64, 0.6f, 0.0f) );
REQUIRE( !region.registerNoteOff(63, 0.0f, 0.0f) );
REQUIRE( !region.registerNoteOff(64, 0.2f, 0.0f) );
REQUIRE( region.delayedReleases.size() == 2 );
std::vector<std::pair<int, float>> expected = {
{ 63, 0.5f },
{ 64, 0.6f }
};
REQUIRE( region.delayedReleases == expected );
}
SECTION("Release with sustain and 2 notes but 1 outside")
{
region.parseOpcode({ "trigger", "release" });
midiState.ccEvent(0, 64, 1.0f);
midiState.noteOnEvent(0, 63, 0.5f);
REQUIRE( !region.registerNoteOn(63, 0.5f, 0.0f) );
midiState.noteOnEvent(0, 66, 0.6f);
REQUIRE( !region.registerNoteOn(66, 0.6f, 0.0f) );
REQUIRE( !region.registerNoteOff(63, 0.0f, 0.0f) );
REQUIRE( !region.registerNoteOff(66, 0.2f, 0.0f) );
REQUIRE( region.delayedReleases.size() == 1 );
std::vector<std::pair<int, float>> expected = {
{ 63, 0.5f }
};
REQUIRE( region.delayedReleases == expected );
}
}

File diff suppressed because it is too large Load diff

View file

@ -6,17 +6,20 @@
#include "sfizz/Defaults.h"
#include "sfizz/Region.h"
#include "sfizz/MidiState.h"
#include "sfizz/SfzHelpers.h"
#include "catch2/catch.hpp"
#include <chrono>
#include <thread>
using namespace Catch::literals;
using namespace sfz::literals;
using namespace sfz;
constexpr int numRandomTests { 64 };
TEST_CASE("[Region] Crossfade in on key")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lokey", "1" });
region.parseOpcode({ "xfin_hikey", "3" });
@ -27,8 +30,8 @@ TEST_CASE("[Region] Crossfade in on key")
TEST_CASE("[Region] Crossfade in on key - 2")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lokey", "1" });
region.parseOpcode({ "xfin_hikey", "5" });
@ -42,8 +45,8 @@ TEST_CASE("[Region] Crossfade in on key - 2")
TEST_CASE("[Region] Crossfade in on key - gain")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lokey", "1" });
region.parseOpcode({ "xfin_hikey", "5" });
@ -57,8 +60,8 @@ TEST_CASE("[Region] Crossfade in on key - gain")
TEST_CASE("[Region] Crossfade out on key")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_lokey", "51" });
region.parseOpcode({ "xfout_hikey", "55" });
@ -73,8 +76,8 @@ TEST_CASE("[Region] Crossfade out on key")
TEST_CASE("[Region] Crossfade out on key - gain")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_lokey", "51" });
region.parseOpcode({ "xfout_hikey", "55" });
@ -90,8 +93,8 @@ TEST_CASE("[Region] Crossfade out on key - gain")
TEST_CASE("[Region] Crossfade in on velocity")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lovel", "20" });
region.parseOpcode({ "xfin_hivel", "24" });
@ -107,8 +110,8 @@ TEST_CASE("[Region] Crossfade in on velocity")
TEST_CASE("[Region] Crossfade in on vel - gain")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_lovel", "20" });
region.parseOpcode({ "xfin_hivel", "24" });
@ -125,8 +128,8 @@ TEST_CASE("[Region] Crossfade in on vel - gain")
TEST_CASE("[Region] Crossfade out on vel")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_lovel", "51" });
region.parseOpcode({ "xfout_hivel", "55" });
@ -142,8 +145,8 @@ TEST_CASE("[Region] Crossfade out on vel")
TEST_CASE("[Region] Crossfade out on vel - gain")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_lovel", "51" });
region.parseOpcode({ "xfout_hivel", "55" });
@ -160,8 +163,8 @@ TEST_CASE("[Region] Crossfade out on vel - gain")
TEST_CASE("[Region] Crossfade in on CC")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_locc24", "20" });
region.parseOpcode({ "xfin_hicc24", "24" });
@ -184,8 +187,8 @@ TEST_CASE("[Region] Crossfade in on CC")
TEST_CASE("[Region] Crossfade in on CC - gain")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfin_locc24", "20" });
region.parseOpcode({ "xfin_hicc24", "24" });
@ -208,8 +211,8 @@ TEST_CASE("[Region] Crossfade in on CC - gain")
}
TEST_CASE("[Region] Crossfade out on CC")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_locc24", "20" });
region.parseOpcode({ "xfout_hicc24", "24" });
@ -232,8 +235,8 @@ TEST_CASE("[Region] Crossfade out on CC")
TEST_CASE("[Region] Crossfade out on CC - gain")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "xfout_locc24", "20" });
region.parseOpcode({ "xfout_hicc24", "24" });
@ -257,8 +260,8 @@ TEST_CASE("[Region] Crossfade out on CC - gain")
TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "amp_veltrack", "0" });
REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a);
@ -268,8 +271,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - veltrack at 0")
TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "amp_veltrack", "100" });
REQUIRE(region.getNoteGain(64, 127_norm) == 1.0_a);
@ -278,8 +281,8 @@ TEST_CASE("[Region] Velocity bug for extreme values - positive veltrack")
TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "amp_veltrack", "-100" });
REQUIRE(region.getNoteGain(64, 127_norm) == Approx(0.0).margin(0.0001));
@ -288,29 +291,29 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
TEST_CASE("[Region] rt_decay")
{
sfz::MidiState midiState;
MidiState midiState;
midiState.setSampleRate(1000);
sfz::Region region { 0, midiState };
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "trigger", "release" });
region.parseOpcode({ "rt_decay", "10" });
midiState.noteOnEvent(0, 64, 64_norm);
midiState.advanceTime(100);
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
REQUIRE( region.getBaseVolumedB(64) == Approx(Default::volume - 1.0f).margin(0.1) );
region.parseOpcode({ "rt_decay", "20" });
midiState.noteOnEvent(0, 64, 64_norm);
midiState.advanceTime(100);
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
REQUIRE( region.getBaseVolumedB(64) == Approx(Default::volume - 2.0f).margin(0.1) );
region.parseOpcode({ "trigger", "attack" });
midiState.noteOnEvent(0, 64, 64_norm);
midiState.advanceTime(100);
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
REQUIRE( region.getBaseVolumedB(64) == Approx(Default::volume).margin(0.1) );
}
TEST_CASE("[Region] Base delay")
{
sfz::MidiState midiState;
sfz::Region region { 0, midiState };
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "delay", "10" });
REQUIRE( region.getDelay() == 10.0f );
@ -322,3 +325,36 @@ TEST_CASE("[Region] Base delay")
REQUIRE( (delay >= 10.0 && delay <= 20.0) );
}
}
TEST_CASE("[Region] Offsets with CCs")
{
MidiState midiState;
Region region { 0, midiState };
region.parseOpcode({ "offset_cc4", "255" });
region.parseOpcode({ "offset", "10" });
REQUIRE( region.getOffset() == 10 );
midiState.ccEvent(0, 4, 127_norm);
REQUIRE( region.getOffset() == 265 );
midiState.ccEvent(0, 4, 100_norm);
REQUIRE( region.getOffset() == 210 );
midiState.ccEvent(0, 4, 10_norm);
REQUIRE( region.getOffset() == 30 );
midiState.ccEvent(0, 4, 0);
REQUIRE( region.getOffset() == 10 );
}
TEST_CASE("[Region] Pitch variation with veltrack")
{
MidiState midiState;
Region region { 0, midiState };
REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0);
REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == 1.0);
REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == 1.0);
region.parseOpcode({ "pitch_veltrack", "1200" });
REQUIRE(region.getBasePitchVariation(60.0, 0_norm) == 1.0);
REQUIRE(region.getBasePitchVariation(60.0, 64_norm) == Approx(centsFactor(600.0)).margin(0.01f));
REQUIRE(region.getBasePitchVariation(60.0, 127_norm) == Approx(centsFactor(1200.0)).margin(0.01f));
}

3033
tests/RegionValuesT.cpp Normal file

File diff suppressed because it is too large Load diff