Added support for the rt_decay opcode

This commit is contained in:
paulfd 2019-09-16 11:48:51 +02:00
parent 2714c0a0e5
commit 61d1f1963c
7 changed files with 72 additions and 7 deletions

View file

@ -113,6 +113,8 @@ namespace Default
inline constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
inline constexpr SfzCrossfadeCurve crossfadeVelCurve { SfzCrossfadeCurve::power };
inline constexpr SfzCrossfadeCurve crossfadeCCCurve { SfzCrossfadeCurve::power };
inline constexpr float rtDecay { 0.0f };
inline constexpr Range<float> rtDecayRange { 0.0f, 200.0f };
// Performance parameters: pitch
inline constexpr uint8_t pitchKeycenter { 60 };

23
sfizz/MidiState.h Normal file
View file

@ -0,0 +1,23 @@
#include <chrono>
#include <array>
namespace sfz
{
inline std::array<std::chrono::steady_clock::time_point, 128> noteOnTimes { };
inline void setNoteOnTime(int noteNumber)
{
if (noteNumber >= 0 && noteNumber < 128)
noteOnTimes[noteNumber] = std::chrono::steady_clock::now();
}
inline float getNoteDuration(int noteNumber)
{
if (noteNumber >= 0 && noteNumber < 128) {
const auto noteOffTime = std::chrono::steady_clock::now();
const auto duration = std::chrono::duration_cast<std::chrono::duration<float>>(noteOffTime - noteOnTimes[noteNumber]);
return duration.count();
}
return 0.0f;
}
}

View file

@ -27,6 +27,7 @@
#include "Debug.h"
#include "Opcode.h"
#include "StringViewHelpers.h"
#include "MidiState.h"
#include "absl/strings/str_replace.h"
#include <random>
@ -381,6 +382,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
DBG("Unknown crossfade power curve: " << std::string(opcode.value));
}
break;
case hash("rt_decay"):
setValueFromOpcode(opcode, rtDecay, Default::rtDecayRange);
break;
// Performance parameters: pitch
case hash("pitch_keycenter"):
@ -629,9 +633,12 @@ float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexc
return centsFactor(pitchVariationInCents);
}
float sfz::Region::getBaseVolumedB() noexcept
float sfz::Region::getBaseVolumedB(int noteNumber) noexcept
{
return volume + volumeDistribution(Random::randomGenerator);
auto baseVolumedB = volume + volumeDistribution(Random::randomGenerator);
if (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key)
baseVolumedB -= rtDecay * getNoteDuration(noteNumber);
return baseVolumedB;
}
float sfz::Region::getBaseGain() noexcept

View file

@ -57,7 +57,7 @@ struct Region {
float getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept;
float getNoteGain(int noteNumber, uint8_t velocity) noexcept;
float getCrossfadeGain(const CCValueArray& ccState) noexcept;
float getBaseVolumedB() noexcept;
float getBaseVolumedB(int noteNumber) noexcept;
float getBaseGain() noexcept;
float velocityCurve(uint8_t velocity) const noexcept;
uint32_t getOffset() noexcept;
@ -136,6 +136,8 @@ struct Region {
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };
CCMap<Range<uint8_t>> crossfadeCCInRange { Default::crossfadeCCInRange }; // xfin_loccN xfin_hiccN
CCMap<Range<uint8_t>> crossfadeCCOutRange { Default::crossfadeCCOutRange }; // xfout_loccN xfout_hiccN
float rtDecay { Default::rtDecay }; // rt_decay
// Performance parameters: pitch
uint8_t pitchKeycenter { Default::pitchKeycenter }; // pitch_keycenter

View file

@ -22,6 +22,7 @@
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Synth.h"
#include "MidiState.h"
#include "Config.h"
#include "Debug.h"
#include "ScopedFTZ.h"
@ -309,7 +310,10 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
{
ASSERT(noteNumber <= 128);
ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0);
setNoteOnTime(noteNumber);
auto randValue = randNoteDistribution(Random::randomGenerator);
for (auto& region : noteActivationLists[noteNumber]) {
@ -334,7 +338,9 @@ void sfz::Synth::noteOn(int delay, int channel, int noteNumber, uint8_t velocity
void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocity) noexcept
{
ASSERT(noteNumber <= 128);
ASSERT(noteNumber < 128);
ASSERT(noteNumber >= 0);
auto randValue = randNoteDistribution(Random::randomGenerator);
for (auto& voice : voices)
voice->registerNoteOff(delay, channel, noteNumber, velocity);
@ -356,7 +362,9 @@ void sfz::Synth::noteOff(int delay, int channel, int noteNumber, uint8_t velocit
void sfz::Synth::cc(int delay, int channel, int ccNumber, uint8_t ccValue) noexcept
{
ASSERT(ccNumber <= 128);
ASSERT(ccNumber < 128);
ASSERT(ccNumber >= 0);
for (auto& voice : voices)
voice->registerCC(delay, channel, ccNumber, ccValue);

View file

@ -54,7 +54,7 @@ void sfz::Voice::startVoice(Region* region, int delay, int channel, int number,
speedRatio = static_cast<float>(region->sampleRate / this->sampleRate);
pitchRatio = region->getBasePitchVariation(number, value);
baseVolumedB = region->getBaseVolumedB();
baseVolumedB = region->getBaseVolumedB(number);
auto volumedB { baseVolumedB };
if (region->volumeCC)

View file

@ -21,9 +21,13 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "Defaults.h"
#include "Region.h"
#include "catch2/catch.hpp"
#include <SfzHelpers.h>
#include <chrono>
#include <thread>
#include "MidiState.h"
using namespace Catch::literals;
TEST_CASE("[Region] Crossfade in on key")
@ -257,4 +261,23 @@ TEST_CASE("[Region] Velocity bug for extreme values - negative veltrack")
region.parseOpcode({ "amp_veltrack", "-100" });
REQUIRE( region.getNoteGain(64, 127) == Approx(0.0).margin(0.0001) );
REQUIRE( region.getNoteGain(64, 0) == 1.0_a );
}
TEST_CASE("[Region] rt_decay")
{
sfz::Region region {};
region.parseOpcode({ "sample", "*sine" });
region.parseOpcode({ "trigger", "release" });
region.parseOpcode({ "rt_decay", "10" });
sfz::setNoteOnTime(64);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 1.0f).margin(0.1) );
region.parseOpcode({ "rt_decay", "20" });
sfz::setNoteOnTime(64);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume - 2.0f).margin(0.1) );
region.parseOpcode({ "trigger", "attack" });
sfz::setNoteOnTime(64);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
REQUIRE( region.getBaseVolumedB(64) == Approx(sfz::Default::volume).margin(0.1) );
}