Region and Synth use float velocities internally
This commit is contained in:
parent
2655418406
commit
0e1e65135e
6 changed files with 206 additions and 103 deletions
|
|
@ -68,7 +68,7 @@ namespace Default
|
|||
|
||||
// Region logic: key mapping
|
||||
constexpr Range<uint8_t> keyRange { 0, 127 };
|
||||
constexpr Range<uint8_t> velocityRange { 0, 127 };
|
||||
constexpr Range<float> velocityRange { 0.0f, 1.0f };
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
constexpr Range<uint8_t> channelRange { 1, 16 };
|
||||
|
|
@ -121,8 +121,8 @@ namespace Default
|
|||
constexpr Range<float> ampRandomRange { 0.0, 24.0 };
|
||||
constexpr Range<uint8_t> crossfadeKeyInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeKeyOutRange { 127, 127 };
|
||||
constexpr Range<uint8_t> crossfadeVelInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeVelOutRange { 127, 127 };
|
||||
constexpr Range<float> crossfadeVelInRange { 0.0f, 0.0f };
|
||||
constexpr Range<float> crossfadeVelOutRange { 1.0f, 1.0f };
|
||||
constexpr Range<uint8_t> crossfadeCCInRange { 0, 0 };
|
||||
constexpr Range<uint8_t> crossfadeCCOutRange { 127, 127 };
|
||||
constexpr SfzCrossfadeCurve crossfadeKeyCurve { SfzCrossfadeCurve::power };
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include "MidiState.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/algorithm/container.h"
|
||||
#include <random>
|
||||
|
||||
template<class T>
|
||||
|
|
@ -138,10 +139,12 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange);
|
||||
break;
|
||||
case hash("lovel"):
|
||||
setRangeStartFromOpcode(opcode, velocityRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
velocityRange.setStart(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("hivel"):
|
||||
setRangeEndFromOpcode(opcode, velocityRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
velocityRange.setEnd(normalizeVelocity(*value));
|
||||
break;
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
|
|
@ -303,8 +306,11 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
case hash("amp_velcurve_&"):
|
||||
{
|
||||
auto value = readOpcode(opcode.value, Default::ampVelcurveRange);
|
||||
if (opcode.parameters.back() > 127)
|
||||
return false;
|
||||
|
||||
if (value)
|
||||
velocityPoints.emplace_back(opcode.parameters.back(), *value);
|
||||
velocityPoints.emplace_back(normalizeVelocity(opcode.parameters.back()), *value);
|
||||
}
|
||||
break;
|
||||
case hash("xfin_lokey"):
|
||||
|
|
@ -320,16 +326,20 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|||
setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange);
|
||||
break;
|
||||
case hash("xfin_lovel"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelInRange.setStart(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xfin_hivel"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelInRange.setEnd(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xfout_lovel"):
|
||||
setRangeStartFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelOutRange.setStart(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xfout_hivel"):
|
||||
setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
|
||||
if (auto value = readOpcode(opcode.value, Default::midi7Range))
|
||||
crossfadeVelOutRange.setEnd(normalizeVelocity(*value));
|
||||
break;
|
||||
case hash("xf_keycurve"):
|
||||
switch (hash(opcode.value)) {
|
||||
|
|
@ -773,6 +783,13 @@ bool sfz::Region::isSwitchedOn() const noexcept
|
|||
|
||||
bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randValue) noexcept
|
||||
{
|
||||
return registerNoteOnNormalized(noteNumber, normalizeVelocity(velocity), randValue);
|
||||
}
|
||||
|
||||
bool sfz::Region::registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
||||
if (keyswitch) {
|
||||
if (*keyswitch == noteNumber)
|
||||
|
|
@ -825,6 +842,13 @@ bool sfz::Region::registerNoteOn(int noteNumber, uint8_t velocity, float randVal
|
|||
|
||||
bool sfz::Region::registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept
|
||||
{
|
||||
return registerNoteOffNormalized(noteNumber, normalizeVelocity(velocity), randValue);
|
||||
}
|
||||
|
||||
bool sfz::Region::registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
||||
if (keyswitchDown && *keyswitchDown == noteNumber)
|
||||
keySwitched = false;
|
||||
|
|
@ -893,11 +917,18 @@ void sfz::Region::registerTempo(float secondsPerQuarter) noexcept
|
|||
|
||||
float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept
|
||||
{
|
||||
return getBasePitchVariationNormalized(noteNumber, normalizeVelocity(velocity));
|
||||
}
|
||||
|
||||
float sfz::Region::getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
std::uniform_int_distribution<int> pitchDistribution { -pitchRandom, pitchRandom };
|
||||
auto pitchVariationInCents = pitchKeytrack * (noteNumber - (int)pitchKeycenter); // note difference with pitch center
|
||||
pitchVariationInCents += tune; // sample tuning
|
||||
pitchVariationInCents += config::centPerSemitone * transpose; // sample transpose
|
||||
pitchVariationInCents += velocity / 127 * pitchVeltrack; // track velocity
|
||||
pitchVariationInCents += velocity * pitchVeltrack; // track velocity
|
||||
pitchVariationInCents += pitchDistribution(Random::randomGenerator); // random pitch changes
|
||||
return centsFactor(pitchVariationInCents);
|
||||
}
|
||||
|
|
@ -961,8 +992,13 @@ float crossfadeIn(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCurv
|
|||
{
|
||||
if (value < crossfadeRange.getStart())
|
||||
return 0.0f;
|
||||
|
||||
const auto length = static_cast<float>(crossfadeRange.length());
|
||||
if (length == 0.0f)
|
||||
return 1.0f;
|
||||
|
||||
else if (value < crossfadeRange.getEnd()) {
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / std::max(static_cast<float>(crossfadeRange.length()), 1.0f);
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
|
||||
if (curve == SfzCrossfadeCurve::power)
|
||||
return sqrt(crossfadePosition);
|
||||
if (curve == SfzCrossfadeCurve::gain)
|
||||
|
|
@ -977,8 +1013,13 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
|
|||
{
|
||||
if (value > crossfadeRange.getEnd())
|
||||
return 0.0f;
|
||||
|
||||
const auto length = static_cast<float>(crossfadeRange.length());
|
||||
if (length == 0.0f)
|
||||
return 1.0f;
|
||||
|
||||
else if (value > crossfadeRange.getStart()) {
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / std::max(static_cast<float>(crossfadeRange.length()), 1.0f);
|
||||
const auto crossfadePosition = static_cast<float>(value - crossfadeRange.getStart()) / length;
|
||||
if (curve == SfzCrossfadeCurve::power)
|
||||
return std::sqrt(1 - crossfadePosition);
|
||||
if (curve == SfzCrossfadeCurve::gain)
|
||||
|
|
@ -990,6 +1031,13 @@ float crossfadeOut(const sfz::Range<T>& crossfadeRange, U value, SfzCrossfadeCur
|
|||
|
||||
float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept
|
||||
{
|
||||
return getNoteGainNormalized(noteNumber, normalizeVelocity(velocity));
|
||||
}
|
||||
|
||||
float sfz::Region::getNoteGainNormalized(int noteNumber, float velocity) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
float baseGain { 1.0f };
|
||||
|
||||
// Amplitude key tracking
|
||||
|
|
@ -1000,7 +1048,7 @@ float sfz::Region::getNoteGain(int noteNumber, uint8_t velocity) const noexcept
|
|||
baseGain *= crossfadeOut(crossfadeKeyOutRange, noteNumber, crossfadeKeyCurve);
|
||||
|
||||
// Amplitude velocity tracking
|
||||
baseGain *= velocityCurve(velocity);
|
||||
baseGain *= velocityCurveNormalized(velocity);
|
||||
|
||||
// Crossfades related to velocity
|
||||
baseGain *= crossfadeIn(crossfadeVelInRange, velocity, crossfadeVelCurve);
|
||||
|
|
@ -1031,25 +1079,30 @@ float sfz::Region::getCrossfadeGain() const noexcept
|
|||
|
||||
float sfz::Region::velocityCurve(uint8_t velocity) const noexcept
|
||||
{
|
||||
float gain { 1.0f };
|
||||
return velocityCurveNormalized(normalizeVelocity(velocity));
|
||||
}
|
||||
|
||||
float sfz::Region::velocityCurveNormalized(float velocity) const noexcept
|
||||
{
|
||||
ASSERT(velocity >= 0.0f && velocity <= 1.0f);
|
||||
|
||||
float gain { 1.0f };
|
||||
if (velocityPoints.size() > 0) { // Custom velocity curve
|
||||
auto after = std::find_if(velocityPoints.begin(), velocityPoints.end(), [velocity](const std::pair<int, float>& val) { return val.first >= velocity; });
|
||||
auto after = absl::c_find_if(velocityPoints, [velocity](const std::pair<float, float>& val) { return val.first >= velocity; });
|
||||
auto before = after == velocityPoints.begin() ? velocityPoints.begin() : after - 1;
|
||||
// Linear interpolation
|
||||
float relativePositionInSegment {
|
||||
static_cast<float>(velocity - before->first) / static_cast<float>(after->first - before->first)
|
||||
(velocity - before->first) / (after->first - before->first)
|
||||
};
|
||||
float segmentEndpoints { after->second - before->second };
|
||||
gain *= relativePositionInSegment * segmentEndpoints;
|
||||
} else { // Standard velocity curve
|
||||
const float floatVelocity { static_cast<float>(velocity) / 127.0f };
|
||||
// FIXME: Maybe there's a prettier way to check the boundaries?
|
||||
const float gaindB = [&]() {
|
||||
if (ampVeltrack >= 0)
|
||||
return floatVelocity == 0.0f ? -90.0f : 40 * std::log(floatVelocity) / std::log(10.0f);
|
||||
return velocity == 0.0f ? -90.0f : 40 * std::log(velocity) / std::log(10.0f);
|
||||
else
|
||||
return floatVelocity == 1.0f ? -90.0f : 40 * std::log(1 - floatVelocity) / std::log(10.0f);
|
||||
return velocity == 1.0f ? -90.0f : 40 * std::log(1 - velocity) / std::log(10.0f);
|
||||
}();
|
||||
gain *= db2mag( gaindB * std::abs(ampVeltrack) / sfz::Default::ampVeltrackRange.getEnd());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,30 @@ struct Region {
|
|||
* @return false
|
||||
*/
|
||||
bool registerNoteOff(int noteNumber, uint8_t velocity, float randValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new note on event. The region may be switched on or off using keys so
|
||||
* this function updates the keyswitches state.
|
||||
*
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
|
||||
* and vary the samples
|
||||
* @return true if the region should trigger on this event.
|
||||
* @return false
|
||||
*/
|
||||
bool registerNoteOnNormalized(int noteNumber, float velocity, float randValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new note off event. The region may be switched on or off using keys so
|
||||
* this function updates the keyswitches state.
|
||||
*
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @param randValue a random value between 0 and 1 used to randomize a bit the region activations
|
||||
* and vary the samples
|
||||
* @return true if the region should trigger on this event.
|
||||
* @return false
|
||||
*/
|
||||
bool registerNoteOffNormalized(int noteNumber, float velocity, float randValue) noexcept;
|
||||
/**
|
||||
* @brief Register a new CC event. The region may be switched on or off using CCs so
|
||||
* this function checks if it indeeds need to activate or not.
|
||||
|
|
@ -136,6 +160,15 @@ struct Region {
|
|||
* @return float
|
||||
*/
|
||||
float getBasePitchVariation(int noteNumber, uint8_t velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the base pitch of the region depending on which note has been
|
||||
* pressed and at which velocity.
|
||||
*
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getBasePitchVariationNormalized(int noteNumber, float velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the note-related gain of the region depending on which note has been
|
||||
* pressed and at which velocity.
|
||||
|
|
@ -145,6 +178,15 @@ struct Region {
|
|||
* @return float
|
||||
*/
|
||||
float getNoteGain(int noteNumber, uint8_t velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the note-related gain of the region depending on which note has been
|
||||
* pressed and at which velocity.
|
||||
*
|
||||
* @param noteNumber
|
||||
* @param velocity
|
||||
* @return float
|
||||
*/
|
||||
float getNoteGainNormalized(int noteNumber, float velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the additional crossfade gain of the region depending on the
|
||||
* CC values
|
||||
|
|
@ -179,6 +221,12 @@ struct Region {
|
|||
* @return float
|
||||
*/
|
||||
float velocityCurve(uint8_t velocity) const noexcept;
|
||||
/**
|
||||
* @brief Computes the gain value related to the velocity of the note
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
float velocityCurveNormalized(float velocity) const noexcept;
|
||||
/**
|
||||
* @brief Get the region offset in samples
|
||||
*
|
||||
|
|
@ -243,7 +291,7 @@ struct Region {
|
|||
|
||||
// Region logic: key mapping
|
||||
Range<uint8_t> keyRange { Default::keyRange }; //lokey, hikey and key
|
||||
Range<uint8_t> velocityRange { Default::velocityRange }; // hivel and lovel
|
||||
Range<float> velocityRange { Default::velocityRange }; // hivel and lovel
|
||||
|
||||
// Region logic: MIDI conditions
|
||||
Range<int> bendRange { Default::bendRange }; // hibend and lobend
|
||||
|
|
@ -282,12 +330,12 @@ struct Region {
|
|||
uint8_t ampKeycenter { Default::ampKeycenter }; // amp_keycenter
|
||||
float ampKeytrack { Default::ampKeytrack }; // amp_keytrack
|
||||
float ampVeltrack { Default::ampVeltrack }; // amp_keytrack
|
||||
std::vector<std::pair<int, float>> velocityPoints; // amp_velcurve_N
|
||||
std::vector<std::pair<float, float>> velocityPoints; // amp_velcurve_N
|
||||
float ampRandom { Default::ampRandom }; // amp_random
|
||||
Range<uint8_t> crossfadeKeyInRange { Default::crossfadeKeyInRange };
|
||||
Range<uint8_t> crossfadeKeyOutRange { Default::crossfadeKeyOutRange };
|
||||
Range<uint8_t> crossfadeVelInRange { Default::crossfadeVelInRange };
|
||||
Range<uint8_t> crossfadeVelOutRange { Default::crossfadeVelOutRange };
|
||||
Range<float> crossfadeVelInRange { Default::crossfadeVelInRange };
|
||||
Range<float> crossfadeVelOutRange { Default::crossfadeVelOutRange };
|
||||
SfzCrossfadeCurve crossfadeKeyCurve { Default::crossfadeKeyCurve };
|
||||
SfzCrossfadeCurve crossfadeVelCurve { Default::crossfadeVelCurve };
|
||||
SfzCrossfadeCurve crossfadeCCCurve { Default::crossfadeCCCurve };
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ sfz::Synth::~Synth()
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto& voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
|
||||
resources.filePool.emptyFileLoadingQueues();
|
||||
|
|
@ -129,11 +129,11 @@ void sfz::Synth::clear()
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto &voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
for (auto& list: noteActivationLists)
|
||||
for (auto& list : noteActivationLists)
|
||||
list.clear();
|
||||
for (auto& list: ccActivationLists)
|
||||
for (auto& list : ccActivationLists)
|
||||
list.clear();
|
||||
regions.clear();
|
||||
effectBuses.clear();
|
||||
|
|
@ -275,17 +275,19 @@ void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& members)
|
|||
void addEndpointsToVelocityCurve(sfz::Region& region)
|
||||
{
|
||||
if (region.velocityPoints.size() > 0) {
|
||||
absl::c_sort(region.velocityPoints, [](const std::pair<int, float>& lhs, const std::pair<int, float>& rhs) { return lhs.first < rhs.first; });
|
||||
const auto velocityStart = sfz::Default::velocityRange.getStart();
|
||||
const auto velocityEnd = sfz::Default::velocityRange.getEnd();
|
||||
absl::c_sort(region.velocityPoints, [](const std::pair<float, float>& lhs, const std::pair<float, float>& rhs) { return lhs.first < rhs.first; });
|
||||
if (region.ampVeltrack > 0) {
|
||||
if (region.velocityPoints.back().first != sfz::Default::velocityRange.getEnd())
|
||||
region.velocityPoints.push_back(std::make_pair<int, float>(127, 1.0f));
|
||||
if (region.velocityPoints.front().first != sfz::Default::velocityRange.getStart())
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair<int, float>(0, 0.0f));
|
||||
if (region.velocityPoints.front().first != velocityStart)
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityStart, velocityStart));
|
||||
if (region.velocityPoints.back().first != velocityEnd)
|
||||
region.velocityPoints.push_back(std::make_pair(velocityEnd, velocityEnd));
|
||||
} else {
|
||||
if (region.velocityPoints.front().first != sfz::Default::velocityRange.getEnd())
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair<int, float>(127, 0.0f));
|
||||
if (region.velocityPoints.back().first != sfz::Default::velocityRange.getStart())
|
||||
region.velocityPoints.push_back(std::make_pair<int, float>(0, 1.0f));
|
||||
if (region.velocityPoints.front().first != velocityEnd)
|
||||
region.velocityPoints.insert(region.velocityPoints.begin(), std::make_pair(velocityEnd, velocityStart));
|
||||
if (region.velocityPoints.back().first != velocityStart)
|
||||
region.velocityPoints.push_back(std::make_pair(velocityStart, velocityEnd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -341,8 +343,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
if (region->loopRange.getEnd() == Default::loopRange.getEnd())
|
||||
region->loopRange.setEnd(region->sampleEnd);
|
||||
|
||||
if (fileInformation->loopBegin != Default::loopRange.getStart() &&
|
||||
fileInformation->loopEnd != Default::loopRange.getEnd()) {
|
||||
if (fileInformation->loopBegin != Default::loopRange.getStart() && fileInformation->loopEnd != Default::loopRange.getEnd()) {
|
||||
if (region->loopRange.getStart() == Default::loopRange.getStart())
|
||||
region->loopRange.setStart(fileInformation->loopBegin);
|
||||
|
||||
|
|
@ -374,8 +375,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
}
|
||||
|
||||
for (auto note = 0; note < 128; note++) {
|
||||
if (region->keyRange.containsWithEnd(note) ||
|
||||
(region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
|
||||
if (region->keyRange.containsWithEnd(note) || (region->hasKeyswitches() && region->keyswitchRange.containsWithEnd(note)))
|
||||
noteActivationLists[note].push_back(region);
|
||||
}
|
||||
|
||||
|
|
@ -396,16 +396,13 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
|
||||
// Set the default frequencies on equalizers if needed
|
||||
if (region->equalizers.size() > 0
|
||||
&& region->equalizers[0].frequency == Default::eqFrequencyUnset)
|
||||
{
|
||||
&& region->equalizers[0].frequency == Default::eqFrequencyUnset) {
|
||||
region->equalizers[0].frequency = Default::eqFrequency1;
|
||||
if (region->equalizers.size() > 1
|
||||
&& region->equalizers[1].frequency == Default::eqFrequencyUnset)
|
||||
{
|
||||
&& region->equalizers[1].frequency == Default::eqFrequencyUnset) {
|
||||
region->equalizers[1].frequency = Default::eqFrequency2;
|
||||
if (region->equalizers.size() > 2
|
||||
&& region->equalizers[2].frequency == Default::eqFrequencyUnset)
|
||||
{
|
||||
&& region->equalizers[2].frequency == Default::eqFrequencyUnset) {
|
||||
region->equalizers[2].frequency = Default::eqFrequency3;
|
||||
}
|
||||
}
|
||||
|
|
@ -425,7 +422,7 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
|||
regions.resize(remainingRegions);
|
||||
modificationTime = checkModificationTime();
|
||||
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->setMaxFiltersPerVoice(maxFilters);
|
||||
voice->setMaxEQsPerVoice(maxEQs);
|
||||
}
|
||||
|
|
@ -468,7 +465,6 @@ int sfz::Synth::getNumActiveVoices() const noexcept
|
|||
|
||||
void sfz::Synth::garbageCollect() noexcept
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
||||
|
|
@ -487,7 +483,7 @@ void sfz::Synth::setSamplesPerBlock(int samplesPerBlock) noexcept
|
|||
for (auto& voice : voices)
|
||||
voice->setSamplesPerBlock(samplesPerBlock);
|
||||
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus)
|
||||
bus->setSamplesPerBlock(samplesPerBlock);
|
||||
}
|
||||
|
|
@ -507,7 +503,7 @@ void sfz::Synth::setSampleRate(float sampleRate) noexcept
|
|||
resources.filterPool.setSampleRate(sampleRate);
|
||||
resources.eqPool.setSampleRate(sampleRate);
|
||||
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus)
|
||||
bus->setSampleRate(sampleRate);
|
||||
}
|
||||
|
|
@ -532,7 +528,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
|
||||
{ // Prepare the effect inputs. They are mixes of per-region outputs.
|
||||
ScopedTiming logger { callbackBreakdown.effects };
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus)
|
||||
bus->clearInputs(numFrames);
|
||||
}
|
||||
|
|
@ -576,7 +572,7 @@ void sfz::Synth::renderBlock(AudioSpan<float> buffer) noexcept
|
|||
// without any <effect>, the signal is just going to flow through it.
|
||||
ScopedTiming logger { callbackBreakdown.effects, ScopedTiming::Operation::addToDuration };
|
||||
|
||||
for (auto& bus: effectBuses) {
|
||||
for (auto& bus : effectBuses) {
|
||||
if (bus) {
|
||||
bus->process(numFrames);
|
||||
bus->mixOutputsTo(buffer, tempMixNode, numFrames);
|
||||
|
|
@ -711,11 +707,11 @@ void sfz::Synth::pitchWheel(int delay, int pitch) noexcept
|
|||
ScopedTiming logger { dispatchDuration, ScopedTiming::Operation::addToDuration };
|
||||
resources.midiState.pitchBendEvent(delay, pitch);
|
||||
|
||||
for (auto& region: regions) {
|
||||
for (auto& region : regions) {
|
||||
region->registerPitchWheel(pitch);
|
||||
}
|
||||
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->registerPitchWheel(delay, pitch);
|
||||
}
|
||||
}
|
||||
|
|
@ -753,10 +749,9 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
if (model.empty())
|
||||
model = config::midnamModel;
|
||||
|
||||
doc.append_child(pugi::node_doctype).set_value(
|
||||
"MIDINameDocument PUBLIC"
|
||||
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
|
||||
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
|
||||
doc.append_child(pugi::node_doctype).set_value("MIDINameDocument PUBLIC"
|
||||
" \"-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN\""
|
||||
" \"http://www.midi.org/dtds/MIDINameDocument10.dtd\"");
|
||||
|
||||
pugi::xml_node root = doc.append_child("MIDINameDocument");
|
||||
|
||||
|
|
@ -767,9 +762,11 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
|
||||
pugi::xml_node device = root.append_child("MasterDeviceNames");
|
||||
device.append_child("Manufacturer")
|
||||
.append_child(pugi::node_pcdata).set_value(std::string(manufacturer).c_str());
|
||||
.append_child(pugi::node_pcdata)
|
||||
.set_value(std::string(manufacturer).c_str());
|
||||
device.append_child("Model")
|
||||
.append_child(pugi::node_pcdata).set_value(std::string(model).c_str());
|
||||
.append_child(pugi::node_pcdata)
|
||||
.set_value(std::string(model).c_str());
|
||||
|
||||
{
|
||||
pugi::xml_node devmode = device.append_child("CustomDeviceMode");
|
||||
|
|
@ -795,7 +792,8 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
|||
}
|
||||
|
||||
chns.append_child("UsesControlNameList")
|
||||
.append_attribute("Name").set_value("Controls");
|
||||
.append_attribute("Name")
|
||||
.set_value("Controls");
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -876,7 +874,7 @@ void sfz::Synth::setNumVoices(int numVoices) noexcept
|
|||
|
||||
void sfz::Synth::resetVoices(int numVoices)
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
|
@ -885,7 +883,7 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
for (int i = 0; i < numVoices; ++i)
|
||||
voices.push_back(absl::make_unique<Voice>(resources));
|
||||
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->setSampleRate(this->sampleRate);
|
||||
voice->setSamplesPerBlock(this->samplesPerBlock);
|
||||
}
|
||||
|
|
@ -896,12 +894,12 @@ void sfz::Synth::resetVoices(int numVoices)
|
|||
|
||||
void sfz::Synth::setOversamplingFactor(sfz::Oversampling factor) noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto& voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
|
||||
resources.filePool.emptyFileLoadingQueues();
|
||||
|
|
@ -916,7 +914,7 @@ sfz::Oversampling sfz::Synth::getOversamplingFactor() const noexcept
|
|||
|
||||
void sfz::Synth::setPreloadSize(uint32_t preloadSize) noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
|
@ -951,13 +949,13 @@ void sfz::Synth::resetAllControllers(int delay) noexcept
|
|||
return;
|
||||
|
||||
resources.midiState.resetAllControllers(delay);
|
||||
for (auto& voice: voices) {
|
||||
for (auto& voice : voices) {
|
||||
voice->registerPitchWheel(delay, 0);
|
||||
for (unsigned cc = 0; cc < config::numCCs; ++cc)
|
||||
voice->registerCC(delay, cc, 0);
|
||||
}
|
||||
|
||||
for (auto& region: regions) {
|
||||
for (auto& region : regions) {
|
||||
for (unsigned cc = 0; cc < config::numCCs; ++cc)
|
||||
region->registerCC(cc, 0);
|
||||
}
|
||||
|
|
@ -996,13 +994,13 @@ void sfz::Synth::disableLogging() noexcept
|
|||
|
||||
void sfz::Synth::allSoundOff() noexcept
|
||||
{
|
||||
AtomicDisabler callbackDisabler{ canEnterCallback };
|
||||
AtomicDisabler callbackDisabler { canEnterCallback };
|
||||
while (inCallback) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
for (auto &voice: voices)
|
||||
for (auto& voice : voices)
|
||||
voice->reset();
|
||||
for (auto& effectBus: effectBuses)
|
||||
for (auto& effectBus : effectBuses)
|
||||
effectBus->clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
|
||||
|
||||
#include "sfizz/Synth.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
#include "ghc/fs_std.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("[Files] Single region (regions_one.sfz)")
|
||||
{
|
||||
|
|
@ -137,11 +139,11 @@ TEST_CASE("[Files] Group from AVL")
|
|||
REQUIRE(synth.getRegionView(i)->volume == 6.0f);
|
||||
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(36, 36));
|
||||
}
|
||||
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<uint8_t>(1, 26));
|
||||
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<uint8_t>(27, 52));
|
||||
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<uint8_t>(53, 77));
|
||||
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<uint8_t>(78, 102));
|
||||
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<uint8_t>(103, 127));
|
||||
REQUIRE(synth.getRegionView(0)->velocityRange == sfz::Range<float>(1_norm, 26_norm));
|
||||
REQUIRE(synth.getRegionView(1)->velocityRange == sfz::Range<float>(27_norm, 52_norm));
|
||||
REQUIRE(synth.getRegionView(2)->velocityRange == sfz::Range<float>(53_norm, 77_norm));
|
||||
REQUIRE(synth.getRegionView(3)->velocityRange == sfz::Range<float>(78_norm, 102_norm));
|
||||
REQUIRE(synth.getRegionView(4)->velocityRange == sfz::Range<float>(103_norm, 127_norm));
|
||||
}
|
||||
|
||||
TEST_CASE("[Files] Full hierarchy")
|
||||
|
|
@ -232,7 +234,7 @@ TEST_CASE("[Files] Pizz basic")
|
|||
REQUIRE(synth.getNumRegions() == 4);
|
||||
for (int i = 0; i < synth.getNumRegions(); ++i) {
|
||||
REQUIRE(synth.getRegionView(i)->keyRange == sfz::Range<uint8_t>(12, 22));
|
||||
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<uint8_t>(97, 127));
|
||||
REQUIRE(synth.getRegionView(i)->velocityRange == sfz::Range<float>(97_norm, 127_norm));
|
||||
REQUIRE(synth.getRegionView(i)->pitchKeycenter == 21);
|
||||
REQUIRE(synth.getRegionView(i)->ccConditions.getWithDefault(107) == sfz::Range<uint8_t>(0, 13));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
#include "sfizz/MidiState.h"
|
||||
#include "sfizz/Region.h"
|
||||
#include "sfizz/SfzHelpers.h"
|
||||
#include "catch2/catch.hpp"
|
||||
using namespace Catch::literals;
|
||||
using namespace sfz::literals;
|
||||
|
||||
TEST_CASE("[Region] Parsing opcodes")
|
||||
{
|
||||
|
|
@ -204,19 +206,19 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("lovel, hivel")
|
||||
{
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "lovel", "37" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(37, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(37_norm, 127_norm));
|
||||
region.parseOpcode({ "lovel", "128" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "lovel", "-3" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
|
||||
region.parseOpcode({ "hivel", "65" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 65));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 65_norm));
|
||||
region.parseOpcode({ "hivel", "-1" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "hivel", "128" });
|
||||
REQUIRE(region.velocityRange == sfz::Range<uint8_t>(0, 127));
|
||||
REQUIRE(region.velocityRange == sfz::Range<float>(0_norm, 127_norm));
|
||||
}
|
||||
|
||||
SECTION("lobend, hibend")
|
||||
|
|
@ -563,9 +565,9 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
SECTION("amp_velcurve")
|
||||
{
|
||||
region.parseOpcode({ "amp_velcurve_6", "0.4" });
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<int, float>(6, 0.4f));
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<float, float>(6_norm, 0.4f));
|
||||
region.parseOpcode({ "amp_velcurve_127", "-1.0" });
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<int, float>(127, 0.0f));
|
||||
REQUIRE(region.velocityPoints.back() == std::make_pair<float, float>(127_norm, 0.0f));
|
||||
}
|
||||
|
||||
SECTION("xfin_lokey, xfin_hikey")
|
||||
|
|
@ -589,21 +591,21 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("xfin_lovel, xfin_hivel")
|
||||
{
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfin_lovel", "4" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(4, 4));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(4_norm, 4_norm));
|
||||
region.parseOpcode({ "xfin_lovel", "128" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_lovel", "59" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "59" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "128" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "0" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfin_hivel", "-1" });
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelInRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xfout_lokey, xfout_hikey")
|
||||
|
|
@ -627,21 +629,21 @@ TEST_CASE("[Region] Parsing opcodes")
|
|||
|
||||
SECTION("xfout_lovel, xfout_hivel")
|
||||
{
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_lovel", "4" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(4, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(4_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_lovel", "128" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(127, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(127_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_lovel", "59" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "59" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 59));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 59_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "128" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(59, 127));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(59_norm, 127_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "0" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
region.parseOpcode({ "xfout_hivel", "-1" });
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<uint8_t>(0, 0));
|
||||
REQUIRE(region.crossfadeVelOutRange == sfz::Range<float>(0_norm, 0_norm));
|
||||
}
|
||||
|
||||
SECTION("xfin_locc, xfin_hicc")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue