594 lines
No EOL
20 KiB
C++
594 lines
No EOL
20 KiB
C++
#include "Region.h"
|
|
#include "Helpers.h"
|
|
#include "absl/strings/str_replace.h"
|
|
#include <random>
|
|
|
|
bool sfz::Region::parseOpcode(const Opcode& opcode)
|
|
{
|
|
switch (hash(opcode.opcode)) {
|
|
// Sound source: sample playback
|
|
case hash("sample"):
|
|
sample = absl::StrReplaceAll(trim(opcode.value), { { "\\", "/" } });
|
|
break;
|
|
case hash("delay"):
|
|
setValueFromOpcode(opcode, delay, Default::delayRange);
|
|
break;
|
|
case hash("delay_random"):
|
|
setValueFromOpcode(opcode, delayRandom, Default::delayRange);
|
|
delayDistribution.param(std::uniform_real_distribution<float>::param_type(0, delayRandom));
|
|
break;
|
|
case hash("offset"):
|
|
setValueFromOpcode(opcode, offset, Default::offsetRange);
|
|
break;
|
|
case hash("offset_random"):
|
|
setValueFromOpcode(opcode, offsetRandom, Default::offsetRange);
|
|
offsetDistribution.param(std::uniform_int_distribution<uint32_t>::param_type(0, offsetRandom));
|
|
break;
|
|
case hash("end"):
|
|
setValueFromOpcode(opcode, sampleEnd, Default::sampleEndRange);
|
|
break;
|
|
case hash("count"):
|
|
setValueFromOpcode(opcode, sampleCount, Default::sampleCountRange);
|
|
break;
|
|
case hash("loopmode"):
|
|
case hash("loop_mode"):
|
|
switch (hash(opcode.value)) {
|
|
case hash("no_loop"):
|
|
loopMode = SfzLoopMode::no_loop;
|
|
break;
|
|
case hash("one_shot"):
|
|
loopMode = SfzLoopMode::one_shot;
|
|
break;
|
|
case hash("loop_continuous"):
|
|
loopMode = SfzLoopMode::loop_continuous;
|
|
break;
|
|
case hash("loop_sustain"):
|
|
loopMode = SfzLoopMode::loop_sustain;
|
|
break;
|
|
default:
|
|
DBG("Unkown loop mode:" << std::string(opcode.value));
|
|
}
|
|
break;
|
|
case hash("loopend"):
|
|
case hash("loop_end"):
|
|
setRangeEndFromOpcode(opcode, loopRange, Default::loopRange);
|
|
break;
|
|
case hash("loopstart"):
|
|
case hash("loop_start"):
|
|
setRangeStartFromOpcode(opcode, loopRange, Default::loopRange);
|
|
break;
|
|
|
|
// Instrument settings: voice lifecycle
|
|
case hash("group"):
|
|
setValueFromOpcode(opcode, group, Default::groupRange);
|
|
break;
|
|
case hash("offby"):
|
|
case hash("off_by"):
|
|
setValueFromOpcode(opcode, offBy, Default::groupRange);
|
|
break;
|
|
case hash("off_mode"):
|
|
switch (hash(opcode.value)) {
|
|
case hash("fast"):
|
|
offMode = SfzOffMode::fast;
|
|
break;
|
|
case hash("normal"):
|
|
offMode = SfzOffMode::normal;
|
|
break;
|
|
default:
|
|
DBG("Unkown off mode:" << std::string(opcode.value));
|
|
}
|
|
break;
|
|
// Region logic: key mapping
|
|
case hash("lokey"):
|
|
setRangeStartFromOpcode(opcode, keyRange, Default::keyRange);
|
|
break;
|
|
case hash("hikey"):
|
|
setRangeEndFromOpcode(opcode, keyRange, Default::keyRange);
|
|
break;
|
|
case hash("key"):
|
|
setRangeStartFromOpcode(opcode, keyRange, Default::keyRange);
|
|
setRangeEndFromOpcode(opcode, keyRange, Default::keyRange);
|
|
setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange);
|
|
break;
|
|
case hash("lovel"):
|
|
setRangeStartFromOpcode(opcode, velocityRange, Default::velocityRange);
|
|
break;
|
|
case hash("hivel"):
|
|
setRangeEndFromOpcode(opcode, velocityRange, Default::velocityRange);
|
|
break;
|
|
|
|
// Region logic: MIDI conditions
|
|
case hash("lochan"):
|
|
setRangeStartFromOpcode(opcode, channelRange, Default::channelRange);
|
|
break;
|
|
case hash("hichan"):
|
|
setRangeEndFromOpcode(opcode, channelRange, Default::channelRange);
|
|
break;
|
|
case hash("lobend"):
|
|
setRangeStartFromOpcode(opcode, bendRange, Default::bendRange);
|
|
break;
|
|
case hash("hibend"):
|
|
setRangeEndFromOpcode(opcode, bendRange, Default::bendRange);
|
|
break;
|
|
case hash("locc"):
|
|
if (opcode.parameter) {
|
|
setRangeStartFromOpcode(opcode, ccConditions[*opcode.parameter], Default::ccRange);
|
|
}
|
|
break;
|
|
case hash("hicc"):
|
|
if (opcode.parameter)
|
|
setRangeEndFromOpcode(opcode, ccConditions[*opcode.parameter], Default::ccRange);
|
|
break;
|
|
case hash("sw_lokey"):
|
|
setRangeStartFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
|
break;
|
|
case hash("sw_hikey"):
|
|
setRangeEndFromOpcode(opcode, keyswitchRange, Default::keyRange);
|
|
break;
|
|
case hash("sw_last"):
|
|
setValueFromOpcode(opcode, keyswitch, Default::keyRange);
|
|
keySwitched = false;
|
|
break;
|
|
case hash("sw_down"):
|
|
setValueFromOpcode(opcode, keyswitchDown, Default::keyRange);
|
|
keySwitched = false;
|
|
break;
|
|
case hash("sw_up"):
|
|
setValueFromOpcode(opcode, keyswitchUp, Default::keyRange);
|
|
break;
|
|
case hash("sw_previous"):
|
|
setValueFromOpcode(opcode, previousNote, Default::keyRange);
|
|
previousKeySwitched = false;
|
|
break;
|
|
case hash("sw_vel"):
|
|
switch (hash(opcode.value)) {
|
|
case hash("current"):
|
|
velocityOverride = SfzVelocityOverride::current;
|
|
break;
|
|
case hash("previous"):
|
|
velocityOverride = SfzVelocityOverride::previous;
|
|
break;
|
|
default:
|
|
DBG("Unknown velocity mode: " << std::string(opcode.value));
|
|
}
|
|
break;
|
|
// Region logic: internal conditions
|
|
case hash("lochanaft"):
|
|
setRangeStartFromOpcode(opcode, aftertouchRange, Default::aftertouchRange);
|
|
break;
|
|
case hash("hichanaft"):
|
|
setRangeEndFromOpcode(opcode, aftertouchRange, Default::aftertouchRange);
|
|
break;
|
|
case hash("lobpm"):
|
|
setRangeStartFromOpcode(opcode, bpmRange, Default::bpmRange);
|
|
break;
|
|
case hash("hibpm"):
|
|
setRangeEndFromOpcode(opcode, bpmRange, Default::bpmRange);
|
|
break;
|
|
case hash("lorand"):
|
|
setRangeStartFromOpcode(opcode, randRange, Default::randRange);
|
|
break;
|
|
case hash("hirand"):
|
|
setRangeEndFromOpcode(opcode, randRange, Default::randRange);
|
|
break;
|
|
case hash("seq_length"):
|
|
setValueFromOpcode(opcode, sequenceLength, Default::sequenceRange);
|
|
break;
|
|
case hash("seq_position"):
|
|
setValueFromOpcode(opcode, sequencePosition, Default::sequenceRange);
|
|
sequenceSwitched = (opcode.value == "1");
|
|
break;
|
|
// Region logic: triggers
|
|
case hash("trigger"):
|
|
switch (hash(opcode.value)) {
|
|
case hash("attack"):
|
|
trigger = SfzTrigger::attack;
|
|
break;
|
|
case hash("first"):
|
|
trigger = SfzTrigger::first;
|
|
break;
|
|
case hash("legato"):
|
|
trigger = SfzTrigger::legato;
|
|
break;
|
|
case hash("release"):
|
|
trigger = SfzTrigger::release;
|
|
break;
|
|
case hash("release_key"):
|
|
trigger = SfzTrigger::release_key;
|
|
break;
|
|
default:
|
|
DBG("Unknown trigger mode: " << std::string(opcode.value));
|
|
}
|
|
break;
|
|
case hash("on_locc"):
|
|
if (opcode.parameter)
|
|
setRangeStartFromOpcode(opcode, ccTriggers[*opcode.parameter], Default::ccRange);
|
|
break;
|
|
case hash("on_hicc"):
|
|
if (opcode.parameter)
|
|
setRangeEndFromOpcode(opcode, ccTriggers[*opcode.parameter], Default::ccRange);
|
|
break;
|
|
|
|
// Performance parameters: amplifier
|
|
case hash("volume"):
|
|
setValueFromOpcode(opcode, volume, Default::volumeRange);
|
|
break;
|
|
// case hash("volume_oncc"):
|
|
// if (opcode.parameter)
|
|
// setValueFromOpcode(opcode, volumeCCTriggers[*opcode.parameter], Default::volumeCCRange);
|
|
// break;
|
|
case hash("amplitude"):
|
|
setValueFromOpcode(opcode, amplitude, Default::amplitudeRange);
|
|
break;
|
|
case hash("amplitude_cc"):
|
|
case hash("amplitude_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeCC, Default::amplitudeRange);
|
|
break;
|
|
case hash("pan"):
|
|
setValueFromOpcode(opcode, pan, Default::panRange);
|
|
break;
|
|
case hash("pan_oncc"):
|
|
setCCPairFromOpcode(opcode, panCC, Default::panCCRange);
|
|
break;
|
|
case hash("position"):
|
|
setValueFromOpcode(opcode, position, Default::positionRange);
|
|
break;
|
|
case hash("position_oncc"):
|
|
setCCPairFromOpcode(opcode, positionCC, Default::positionCCRange);
|
|
break;
|
|
case hash("width"):
|
|
setValueFromOpcode(opcode, width, Default::widthRange);
|
|
break;
|
|
case hash("width_oncc"):
|
|
setCCPairFromOpcode(opcode, widthCC, Default::widthCCRange);
|
|
break;
|
|
case hash("amp_keycenter"):
|
|
setValueFromOpcode(opcode, ampKeycenter, Default::keyRange);
|
|
break;
|
|
case hash("amp_keytrack"):
|
|
setValueFromOpcode(opcode, ampKeytrack, Default::ampKeytrackRange);
|
|
break;
|
|
case hash("amp_veltrack"):
|
|
setValueFromOpcode(opcode, ampVeltrack, Default::ampVeltrackRange);
|
|
break;
|
|
case hash("amp_random"):
|
|
setValueFromOpcode(opcode, ampRandom, Default::ampRandomRange);
|
|
gainDistribution.param(std::uniform_real_distribution<float>::param_type(-ampRandom, ampRandom));
|
|
break;
|
|
case hash("amp_velcurve_"):
|
|
if (opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter)) {
|
|
if (auto value = readOpcode(opcode.value, Default::ampVelcurveRange); value)
|
|
velocityPoints.emplace_back(*opcode.parameter, *value);
|
|
}
|
|
break;
|
|
case hash("xfin_lokey"):
|
|
setRangeStartFromOpcode(opcode, crossfadeKeyInRange, Default::keyRange);
|
|
break;
|
|
case hash("xfin_hikey"):
|
|
setRangeEndFromOpcode(opcode, crossfadeKeyInRange, Default::keyRange);
|
|
break;
|
|
case hash("xfout_lokey"):
|
|
setRangeStartFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange);
|
|
break;
|
|
case hash("xfout_hikey"):
|
|
setRangeEndFromOpcode(opcode, crossfadeKeyOutRange, Default::keyRange);
|
|
break;
|
|
case hash("xfin_lovel"):
|
|
setRangeStartFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
|
|
break;
|
|
case hash("xfin_hivel"):
|
|
setRangeEndFromOpcode(opcode, crossfadeVelInRange, Default::velocityRange);
|
|
break;
|
|
case hash("xfout_lovel"):
|
|
setRangeStartFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
|
|
break;
|
|
case hash("xfout_hivel"):
|
|
setRangeEndFromOpcode(opcode, crossfadeVelOutRange, Default::velocityRange);
|
|
break;
|
|
case hash("xf_keycurve"):
|
|
switch (hash(opcode.value)) {
|
|
case hash("power"):
|
|
crossfadeKeyCurve = SfzCrossfadeCurve::power;
|
|
break;
|
|
case hash("gain"):
|
|
crossfadeKeyCurve = SfzCrossfadeCurve::gain;
|
|
break;
|
|
default:
|
|
DBG("Unknown crossfade power curve: " << std::string(opcode.value));
|
|
}
|
|
break;
|
|
case hash("xf_velcurve"):
|
|
switch (hash(opcode.value)) {
|
|
case hash("power"):
|
|
crossfadeVelCurve = SfzCrossfadeCurve::power;
|
|
break;
|
|
case hash("gain"):
|
|
crossfadeVelCurve = SfzCrossfadeCurve::gain;
|
|
break;
|
|
default:
|
|
DBG("Unknown crossfade power curve: " << std::string(opcode.value));
|
|
}
|
|
break;
|
|
|
|
// Performance parameters: pitch
|
|
case hash("pitch_keycenter"):
|
|
setValueFromOpcode(opcode, pitchKeycenter, Default::keyRange);
|
|
break;
|
|
case hash("pitch_keytrack"):
|
|
setValueFromOpcode(opcode, pitchKeytrack, Default::pitchKeytrackRange);
|
|
break;
|
|
case hash("pitch_veltrack"):
|
|
setValueFromOpcode(opcode, pitchVeltrack, Default::pitchVeltrackRange);
|
|
break;
|
|
case hash("pitch_random"):
|
|
setValueFromOpcode(opcode, pitchRandom, Default::pitchRandomRange);
|
|
pitchDistribution.param(std::uniform_int_distribution<int>::param_type(-pitchRandom, pitchRandom));
|
|
break;
|
|
case hash("transpose"):
|
|
setValueFromOpcode(opcode, transpose, Default::transposeRange);
|
|
break;
|
|
case hash("tune"):
|
|
setValueFromOpcode(opcode, tune, Default::tuneRange);
|
|
break;
|
|
|
|
// Amplitude Envelope
|
|
case hash("ampeg_attack"):
|
|
setValueFromOpcode(opcode, amplitudeEG.attack, Default::egTimeRange);
|
|
break;
|
|
case hash("ampeg_decay"):
|
|
setValueFromOpcode(opcode, amplitudeEG.decay, Default::egTimeRange);
|
|
break;
|
|
case hash("ampeg_delay"):
|
|
setValueFromOpcode(opcode, amplitudeEG.delay, Default::egTimeRange);
|
|
break;
|
|
case hash("ampeg_hold"):
|
|
setValueFromOpcode(opcode, amplitudeEG.hold, Default::egTimeRange);
|
|
break;
|
|
case hash("ampeg_release"):
|
|
setValueFromOpcode(opcode, amplitudeEG.release, Default::egTimeRange);
|
|
break;
|
|
case hash("ampeg_start"):
|
|
setValueFromOpcode(opcode, amplitudeEG.start, Default::egPercentRange);
|
|
break;
|
|
case hash("ampeg_sustain"):
|
|
setValueFromOpcode(opcode, amplitudeEG.sustain, Default::egPercentRange);
|
|
break;
|
|
case hash("ampeg_vel2attack"):
|
|
setValueFromOpcode(opcode, amplitudeEG.vel2attack, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_vel2decay"):
|
|
setValueFromOpcode(opcode, amplitudeEG.vel2decay, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_vel2delay"):
|
|
setValueFromOpcode(opcode, amplitudeEG.vel2delay, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_vel2hold"):
|
|
setValueFromOpcode(opcode, amplitudeEG.vel2hold, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_vel2release"):
|
|
setValueFromOpcode(opcode, amplitudeEG.vel2release, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_vel2sustain"):
|
|
setValueFromOpcode(opcode, amplitudeEG.vel2sustain, Default::egOnCCPercentRange);
|
|
break;
|
|
case hash("ampeg_attack_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccAttack, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_decay_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccDecay, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_delay_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccDelay, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_hold_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccHold, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_release_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccRelease, Default::egOnCCTimeRange);
|
|
break;
|
|
case hash("ampeg_start_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccStart, Default::egOnCCPercentRange);
|
|
break;
|
|
case hash("ampeg_sustain_oncc"):
|
|
setCCPairFromOpcode(opcode, amplitudeEG.ccSustain, Default::egOnCCPercentRange);
|
|
break;
|
|
// Ignored opcodes
|
|
case hash("ampeg_depth"):
|
|
case hash("ampeg_vel2depth"):
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool sfz::Region::isSwitchedOn() const noexcept
|
|
{
|
|
return keySwitched && previousKeySwitched && sequenceSwitched && pitchSwitched && bpmSwitched && aftertouchSwitched && allCCSwitched;
|
|
}
|
|
|
|
bool sfz::Region::registerNoteOn(int channel, int noteNumber, uint8_t velocity, float randValue) noexcept
|
|
{
|
|
const bool chanOk = channelRange.containsWithEnd(channel);
|
|
if (!chanOk)
|
|
return false;
|
|
|
|
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
|
if (keyswitch) {
|
|
if (*keyswitch == noteNumber)
|
|
keySwitched = true;
|
|
else
|
|
keySwitched = false;
|
|
}
|
|
|
|
if (keyswitchDown && *keyswitchDown == noteNumber)
|
|
keySwitched = true;
|
|
|
|
if (keyswitchUp && *keyswitchUp == noteNumber)
|
|
keySwitched = false;
|
|
}
|
|
|
|
const bool keyOk = keyRange.containsWithEnd(noteNumber);
|
|
if (keyOk) {
|
|
// Update the number of notes playing for the region
|
|
activeNotesInRange++;
|
|
|
|
// Sequence activation
|
|
sequenceCounter += 1;
|
|
if ((sequenceCounter % sequenceLength) == sequencePosition - 1)
|
|
sequenceSwitched = true;
|
|
else
|
|
sequenceSwitched = false;
|
|
|
|
// Velocity memory for release_key and for sw_vel=previous
|
|
if (trigger == SfzTrigger::release_key || velocityOverride == SfzVelocityOverride::previous)
|
|
lastNoteVelocities[noteNumber] = velocity;
|
|
|
|
if (previousNote) {
|
|
if (*previousNote == noteNumber)
|
|
previousKeySwitched = true;
|
|
else
|
|
previousKeySwitched = false;
|
|
}
|
|
}
|
|
|
|
if (!isSwitchedOn())
|
|
return false;
|
|
|
|
if (previousNote && !(previousKeySwitched && noteNumber != *previousNote))
|
|
return false;
|
|
|
|
const bool velOk = velocityRange.containsWithEnd(velocity);
|
|
const bool randOk = randRange.contains(randValue) || (randValue == 1.0f && randRange.getEnd() == 1.0f);
|
|
const bool firstLegatoNote = (trigger == SfzTrigger::first && activeNotesInRange == 0);
|
|
const bool attackTrigger = (trigger == SfzTrigger::attack);
|
|
const bool notFirstLegatoNote = (trigger == SfzTrigger::legato && activeNotesInRange > 0);
|
|
|
|
return keyOk && velOk && chanOk && randOk && (attackTrigger || firstLegatoNote || notFirstLegatoNote);
|
|
}
|
|
|
|
bool sfz::Region::registerNoteOff(int channel, int noteNumber, uint8_t velocity [[maybe_unused]], float randValue) noexcept
|
|
{
|
|
const bool chanOk = channelRange.containsWithEnd(channel);
|
|
if (!chanOk)
|
|
return false;
|
|
|
|
if (keyswitchRange.containsWithEnd(noteNumber)) {
|
|
if (keyswitchDown && *keyswitchDown == noteNumber)
|
|
keySwitched = false;
|
|
|
|
if (keyswitchUp && *keyswitchUp == noteNumber)
|
|
keySwitched = true;
|
|
}
|
|
|
|
const bool keyOk = keyRange.containsWithEnd(noteNumber);
|
|
|
|
// Update the number of notes playing for the region
|
|
if (keyOk)
|
|
activeNotesInRange--;
|
|
|
|
if (!isSwitchedOn())
|
|
return false;
|
|
|
|
const bool randOk = randRange.contains(randValue);
|
|
const bool releaseTrigger = (trigger == SfzTrigger::release || trigger == SfzTrigger::release_key);
|
|
return keyOk && chanOk && randOk && releaseTrigger;
|
|
}
|
|
|
|
bool sfz::Region::registerCC(int channel, int ccNumber, uint8_t ccValue) noexcept
|
|
{
|
|
if (!channelRange.containsWithEnd(channel))
|
|
return false;
|
|
|
|
if (ccConditions.getWithDefault(ccNumber).containsWithEnd(ccValue))
|
|
ccSwitched.set(ccNumber, true);
|
|
else
|
|
ccSwitched.set(ccNumber, false);
|
|
|
|
if (ccSwitched.all())
|
|
allCCSwitched = true;
|
|
else
|
|
allCCSwitched = false;
|
|
|
|
if (ccTriggers.contains(ccNumber) && ccTriggers.at(ccNumber).containsWithEnd(ccValue))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void sfz::Region::registerPitchWheel(int channel, int pitch) noexcept
|
|
{
|
|
if (!channelRange.containsWithEnd(channel))
|
|
return;
|
|
|
|
if (bendRange.containsWithEnd(pitch))
|
|
pitchSwitched = true;
|
|
else
|
|
pitchSwitched = false;
|
|
}
|
|
|
|
void sfz::Region::registerAftertouch(int channel, uint8_t aftertouch) noexcept
|
|
{
|
|
if (!channelRange.containsWithEnd(channel))
|
|
return;
|
|
|
|
if (aftertouchRange.containsWithEnd(aftertouch))
|
|
aftertouchSwitched = true;
|
|
else
|
|
aftertouchSwitched = false;
|
|
}
|
|
|
|
void sfz::Region::registerTempo(float secondsPerQuarter) noexcept
|
|
{
|
|
const float bpm = 60.0f / secondsPerQuarter;
|
|
if (bpmRange.containsWithEnd(bpm))
|
|
bpmSwitched = true;
|
|
else
|
|
bpmSwitched = false;
|
|
}
|
|
|
|
float sfz::Region::getBasePitchVariation(int noteNumber, uint8_t velocity) noexcept
|
|
{
|
|
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 += pitchDistribution(Random::randomGenerator); // random pitch changes
|
|
return centsFactor(pitchVariationInCents);
|
|
}
|
|
|
|
float sfz::Region::getBaseGain() noexcept
|
|
{
|
|
float baseGaindB { volume };
|
|
baseGaindB += gainDistribution(Random::randomGenerator);
|
|
return db2mag(baseGaindB);
|
|
}
|
|
|
|
uint32_t sfz::Region::getOffset() noexcept
|
|
{
|
|
return offset + offsetDistribution(Random::randomGenerator);
|
|
}
|
|
|
|
uint32_t sfz::Region::getDelay() noexcept
|
|
{
|
|
return delay + delayDistribution(Random::randomGenerator);
|
|
}
|
|
|
|
uint32_t sfz::Region::trueSampleEnd() const noexcept
|
|
{
|
|
return min(sampleEnd, loopRange.getEnd());
|
|
}
|
|
|
|
bool sfz::Region::canUsePreloadedData() const noexcept
|
|
{
|
|
if (preloadedData == nullptr)
|
|
return false;
|
|
|
|
return trueSampleEnd() < static_cast<uint32_t>(preloadedData->getNumFrames());
|
|
}
|
|
|
|
bool sfz::Region::isStereo() const noexcept
|
|
{
|
|
return this->numChannels == 2;
|
|
} |