Merge pull request #212 from jpcima/opcode-cleanup

Opcode cleanup
This commit is contained in:
JP Cimalando 2020-05-05 11:43:39 +02:00 committed by GitHub
commit b0c84ad99a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 2128 additions and 102 deletions

11
scripts/generate_re2c.sh Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
set -e
if ! test -d "src"; then
echo "Please run this in the project root directory."
exit 1
fi
for src in src/sfizz/OpcodeCleanup.re; do
re2c -o "${src%.re}.cpp" "$src"
done

View file

@ -45,7 +45,7 @@ sfizz_add_simd_sources (SFIZZ_SOURCES ".")
# Parser core library
add_library (sfizz_parser STATIC)
target_sources (sfizz_parser PRIVATE
sfizz/Parser.cpp sfizz/Opcode.cpp sfizz/SfzHelpers.cpp
sfizz/Parser.cpp sfizz/Opcode.cpp sfizz/OpcodeCleanup.cpp sfizz/SfzHelpers.cpp
sfizz/parser/Parser.cpp sfizz/parser/ParserPrivate.cpp)
target_include_directories (sfizz_parser PUBLIC sfizz)
target_include_directories (sfizz_parser PUBLIC external)

View file

@ -36,6 +36,18 @@ enum OpcodeCategory {
kOpcodeSmoothCcN,
};
/**
* @brief A scope where an opcode may appear.
*/
enum OpcodeScope {
//! unknown scope or other
kOpcodeScopeGeneric,
//! region scope
kOpcodeScopeRegion,
//! effect scope
kOpcodeScopeEffect,
};
/**
* @brief Opcode description class. The class parses the parameters
* of the opcode on construction.
@ -51,6 +63,15 @@ struct Opcode {
std::vector<uint16_t> parameters;
OpcodeCategory category;
/*
* @brief Normalize in order to make the ampersand-name unique, and
* facilitate subsequent processing.
*
* @param scope scope where this opcode appears
* @return normalized opcode
*/
Opcode cleanUp(OpcodeScope scope) const;
/*
* @brief Get the derived opcode name to convert it to another category.
*

1797
src/sfizz/OpcodeCleanup.cpp Normal file

File diff suppressed because it is too large Load diff

172
src/sfizz/OpcodeCleanup.re Normal file
View file

@ -0,0 +1,172 @@
/* -*- mode: c++; -*- */
// 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 "Opcode.h"
#include "absl/strings/string_view.h"
#include "absl/strings/str_cat.h"
#include <string>
namespace sfz {
static std::string cleanUpOpcodeName(absl::string_view rawOpcode, OpcodeScope scope)
{
std::string opcode { rawOpcode };
/*!re2c
re2c:flags:posix-captures = 1;
re2c:define:YYCTYPE = char;
re2c:yyfill:enable = 0;
*/
/*!maxnmatch:re2c*/
const char* YYCURSOR;
const char* YYMARKER;
const char* yypmatch[2 * YYMAXNMATCH];
size_t yynmatch;
/*!stags:re2c format = "const char* @@; "; */
auto group = [&yypmatch](size_t i) -> absl::string_view {
const char *beg = yypmatch[2 * i];
const char *end = yypmatch[2 * i + 1];
return absl::string_view(beg, end - beg);
};
/*!re2c
digit = [0-9];
number = digit+;
any = [^\x00]*;
END = [\x00];
*/
//--------------------------------------------------------------------------
YYCURSOR = opcode.c_str();
/*!re2c
(any) "_cc" (number) END {
opcode = absl::StrCat(group(1), "_oncc", group(2));
goto end_generic;
}
* {
goto end_generic;
}
*/
end_generic:
/* end */;
//--------------------------------------------------------------------------
if (scope == kOpcodeScopeRegion) {
YYCURSOR = opcode.c_str();
/*!re2c
lfoV1 = "amplfo"|"fillfo"|"pitchlfo";
egV1 = "ampeg"|"fileg"|"pitcheg";
eqV1 = "eq" number;
lfoV2 = "lfo" number;
(lfoV1) "_" ("depth"|"freq"|"fade") "cc" (number) END {
opcode = absl::StrCat(group(1), "_", group(2), "_oncc", group(3));
goto end_region;
}
(egV1) "_" ("delay"|"start"|"attack"|"hold"|"decay"|"sustain"|"release") "cc" (number) END {
opcode = absl::StrCat(group(1), "_", group(2), "_oncc", group(3));
goto end_region;
}
(eqV1) "_" ("bw"|"freq"|"gain") "cc" (number) END {
opcode = absl::StrCat(group(1), "_", group(2), "_oncc", group(3));
goto end_region;
}
(lfoV2) "_" ("wave"|"offset"|"ratio"|"scale") END {
opcode = absl::StrCat(group(1), "_", group(2), "1");
goto end_region;
}
"loop" ("mode"|"start"|"end") END {
opcode = absl::StrCat("loop_", group(1));
goto end_region;
}
"off" ("by"|"mode") END {
opcode = absl::StrCat("off_", group(1));
goto end_region;
}
"bend" ("up"|"down") END {
opcode = absl::StrCat("bend_", group(1));
goto end_region;
}
"filtype" END {
opcode = absl::StrCat("fil1_type");
goto end_region;
}
"fil" (number) "type" END {
opcode = absl::StrCat("fil", group(1), "_type");
goto end_region;
}
"polyphony_group" END {
opcode = "group";
goto end_region;
}
"gain" ("_" any)? END {
opcode = absl::StrCat("volume", group(1));
goto end_region;
}
"tune" ("_" any)? END {
opcode = absl::StrCat("pitch", group(1));
goto end_region;
}
"on_" ("hi"|"lo") "cc" (number) END {
opcode = absl::StrCat("start_", group(1), "cc", group(2));
goto end_region;
}
"fil_" (any) END {
opcode = absl::StrCat("fil1_", group(1));
goto end_region;
}
"cutoff" ("_" any)? END {
opcode = absl::StrCat("cutoff1", group(1));
goto end_region;
}
"resonance" ("_" any)? END {
opcode = absl::StrCat("resonance1", group(1));
goto end_region;
}
* {
goto end_region;
}
*/
end_region:
/* end */;
} // scope == kOpcodeScopeRegion
//--------------------------------------------------------------------------
#undef YYMAXNMATCH
return opcode;
}
Opcode Opcode::cleanUp(OpcodeScope scope) const
{
return Opcode(cleanUpOpcodeName(opcode, scope), value);
}
} // namespace sfz

View file

@ -31,8 +31,10 @@ bool extendIfNecessary(std::vector<T>& vec, unsigned size, unsigned defaultCapac
return true;
}
bool sfz::Region::parseOpcode(const Opcode& opcode)
bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
{
const Opcode opcode = rawOpcode.cleanUp(kOpcodeScopeRegion);
switch (opcode.lettersOnlyHash) {
// Sound source: sample playback
case hash("sample"):
@ -65,7 +67,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("offset_random"):
setValueFromOpcode(opcode, offsetRandom, Default::offsetRange);
break;
case hash("offset_cc&"):
case hash("offset_oncc&"): // also offset_cc&
if (opcode.parameters.back() > config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::offsetCCRange))
@ -77,8 +79,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("count"):
setValueFromOpcode(opcode, sampleCount, Default::sampleCountRange);
break;
case hash("loopmode"): // fallthrough
case hash("loop_mode"):
case hash("loop_mode"): // also loopmode
switch (hash(opcode.value)) {
case hash("no_loop"):
loopMode = SfzLoopMode::no_loop;
@ -96,12 +97,10 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
DBG("Unkown loop mode:" << std::string(opcode.value));
}
break;
case hash("loopend"): // fallthrough
case hash("loop_end"):
case hash("loop_end"): // also loopend
setRangeEndFromOpcode(opcode, loopRange, Default::loopRange);
break;
case hash("loopstart"): // fallthrough
case hash("loop_start"):
case hash("loop_start"): // also loopstart
setRangeStartFromOpcode(opcode, loopRange, Default::loopRange);
break;
@ -121,15 +120,13 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break;
// Instrument settings: voice lifecycle
case hash("group"): // fallthrough
case hash("polyphony_group"):
case hash("group"): // also polyphony_group
setValueFromOpcode(opcode, group, Default::groupRange);
break;
case hash("offby"): // fallthrough
case hash("off_by"):
case hash("off_by"): // also offby
setValueFromOpcode(opcode, offBy, Default::groupRange);
break;
case hash("off_mode"):
case hash("off_mode"): // also offmode
switch (hash(opcode.value)) {
case hash("fast"):
offMode = SfzOffMode::fast;
@ -292,15 +289,13 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
DBG("Unknown trigger mode: " << std::string(opcode.value));
}
break;
case hash("on_locc&"): // fallthrough
case hash("start_locc&"):
case hash("start_locc&"): // also on_locc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::midi7Range))
ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value));
break;
case hash("on_hicc&"): // fallthrough
case hash("start_hicc&"):
case hash("start_hicc&"): // also on_hicc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::midi7Range))
@ -308,30 +303,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break;
// Performance parameters: amplifier
case hash("volume"):
case hash("volume"): // also gain
setValueFromOpcode(opcode, volume, Default::volumeRange);
break;
case hash("volume_curvecc&"):
case hash("volume_curvecc&"): // also gain_curvecc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::curveCCRange))
volumeCC[opcode.parameters.back()].curve = *value;
break;
case hash("volume_stepcc&"):
case hash("volume_stepcc&"): // also gain_stepcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::volumeStepRange))
volumeCC[opcode.parameters.back()].step = *value;
break;
case hash("volume_smoothcc&"):
case hash("volume_smoothcc&"): // also gain_smoothcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
volumeCC[opcode.parameters.back()].smooth = *value;
break;
case hash("gain_cc&"):
case hash("gain_oncc&"): // fallthrough
case hash("volume_oncc&"):
case hash("volume_oncc&"): // also gain_oncc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::volumeCCRange))
@ -359,8 +352,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
amplitudeCC[opcode.parameters.back()].smooth = *value;
break;
case hash("amplitude_cc&"): // fallthrough
case hash("amplitude_oncc&"):
case hash("amplitude_oncc&"): // also amplitude_cc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::amplitudeRange))
@ -388,8 +380,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
panCC[opcode.parameters.back()].smooth = *value;
break;
case hash("pan_cc&"):
case hash("pan_oncc&"):
case hash("pan_oncc&"): // also pan_cc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::panCCRange))
@ -417,8 +408,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
positionCC[opcode.parameters.back()].smooth = *value;
break;
case hash("position_cc&"): // fallthrough
case hash("position_oncc&"):
case hash("position_oncc&"): // also position_cc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::positionCCRange))
@ -567,8 +557,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break;
// Performance parameters: filters
case hash("cutoff"): // fallthrough
case hash("cutoff&"):
case hash("cutoff&"): // also cutoff
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1);
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
@ -576,8 +565,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, filters[filterIndex].cutoff, Default::filterCutoffRange);
}
break;
case hash("resonance"): // fallthrough
case hash("resonance&"):
case hash("resonance&"): // also resonance
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1);
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
@ -585,12 +573,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, filters[filterIndex].resonance, Default::filterResonanceRange);
}
break;
case hash("cutoff_oncc&"):
case hash("cutoff_cc&"):
case hash("cutoff&_oncc&"): // fallthrough
case hash("cutoff&_cc&"):
case hash("cutoff&_oncc&"): // also cutoff_oncc&, cutoff_cc&, cutoff&_cc&
{
const auto filterIndex = opcode.parameters.size() == 1 ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
@ -601,12 +586,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
);
}
break;
case hash("resonance&_oncc&"):
case hash("resonance&_cc&"):
case hash("resonance_oncc&"): // fallthrough
case hash("resonance_cc&"):
case hash("resonance&_oncc&"): // also resonance_oncc&, resonance_cc&, resonance&_cc&
{
const auto filterIndex = opcode.parameters.size() == 1 ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
@ -617,60 +599,54 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
);
}
break;
case hash("fil_keytrack"): // fallthrough
case hash("fil&_keytrack"):
case hash("fil&_keytrack"): // also fil_keytrack
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
setValueFromOpcode(opcode, filters[filterIndex].keytrack, Default::filterKeytrackRange);
}
break;
case hash("fil_keycenter"): // fallthrough
case hash("fil&_keycenter"):
case hash("fil&_keycenter"): // also fil_keycenter
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
setValueFromOpcode(opcode, filters[filterIndex].keycenter, Default::keyRange);
}
break;
case hash("fil_veltrack"): // fallthrough
case hash("fil&_veltrack"):
case hash("fil&_veltrack"): // also fil_veltrack
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
setValueFromOpcode(opcode, filters[filterIndex].veltrack, Default::filterVeltrackRange);
}
break;
case hash("fil_random"): // fallthrough
case hash("fil&_random"):
case hash("fil&_random"): // also fil_random
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
setValueFromOpcode(opcode, filters[filterIndex].random, Default::filterRandomRange);
}
break;
case hash("fil_gain"): // fallthrough
case hash("fil&_gain"):
case hash("fil&_gain"): // also fil_gain
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
setValueFromOpcode(opcode, filters[filterIndex].gain, Default::filterGainRange);
}
break;
case hash("fil_gaincc&"): // fallthrough
case hash("fil&_gaincc&"):
case hash("fil&_gain_oncc&"): // also fil_gain_oncc&
{
const auto filterIndex = opcode.parameters.size() == 1 ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
@ -681,10 +657,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
);
}
break;
case hash("fil_type"): // fallthrough
case hash("fil&_type"):
case hash("fil&_type"): // also fil_type, filtype
{
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.front() - 1);
const auto filterIndex = opcode.parameters.front() - 1;
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false;
@ -710,8 +685,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, equalizers[eqNumber - 1].bandwidth, Default::eqBandwidthRange);
}
break;
case hash("eq&_bw_oncc&"): // fallthrough
case hash("eq&_bwcc&"):
case hash("eq&_bw_oncc&"): // also eq&_bwcc&
{
const auto eqNumber = opcode.parameters.front();
if (eqNumber == 0)
@ -732,8 +706,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, equalizers[eqNumber - 1].frequency, Default::eqFrequencyRange);
}
break;
case hash("eq&_freq_oncc&"): // fallthrough
case hash("eq&_freqcc&"):
case hash("eq&_freq_oncc&"): // also eq&_freqcc&
{
const auto eqNumber = opcode.parameters.front();
if (eqNumber == 0)
@ -767,8 +740,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, equalizers[eqNumber - 1].gain, Default::eqGainRange);
}
break;
case hash("eq&_gain_oncc&"): // fallthrough
case hash("eq&_gaincc&"):
case hash("eq&_gain_oncc&"): // also eq&_gaincc&
{
const auto eqNumber = opcode.parameters.front();
if (eqNumber == 0)
@ -827,44 +799,37 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("transpose"):
setValueFromOpcode(opcode, transpose, Default::transposeRange);
break;
case hash("tune"): // fallthrough
case hash("pitch"):
case hash("pitch"): // also tune
setValueFromOpcode(opcode, tune, Default::tuneRange);
break;
case hash("pitch_curvecc&"): // fallthrough
case hash("tune_curvecc&"):
case hash("pitch_curvecc&"): // also tune_curvecc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::curveCCRange))
tuneCC[opcode.parameters.back()].curve = *value;
break;
case hash("pitch_stepcc&"): // fallthrough
case hash("tune_stepcc&"):
case hash("pitch_stepcc&"): // also tune_stepcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::tuneStepRange))
tuneCC[opcode.parameters.back()].step = *value;
break;
case hash("pitch_smoothcc&"): // fallthrough
case hash("tune_smoothcc&"):
case hash("pitch_smoothcc&"): // also tune_smoothcc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
tuneCC[opcode.parameters.back()].smooth = *value;
break;
case hash("tune_cc&"):
case hash("tune_oncc&"):
case hash("pitch_cc&"):
case hash("pitch_oncc&"):
case hash("pitch_oncc&"): // also pitch_cc&, tune_cc&, tune_oncc&
if (opcode.parameters.back() >= config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::tuneCCRange))
tuneCC[opcode.parameters.back()].value = *value;
break;
case hash("bend_up"):
case hash("bend_up"): // also bendup
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
break;
case hash("bend_down"):
case hash("bend_down"): // also benddown
setValueFromOpcode(opcode, bendDown, Default::bendBoundRange);
break;
case hash("bend_step"):
@ -923,32 +888,25 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
return false; // Was not vel2...
setValueFromOpcode(opcode, amplitudeEG.vel2sustain, Default::egOnCCPercentRange);
break;
case hash("ampeg_attackcc&"): // fallthrough
case hash("ampeg_attack_oncc&"):
case hash("ampeg_attack_oncc&"): // also ampeg_attackcc&
setCCPairFromOpcode(opcode, amplitudeEG.ccAttack, Default::egOnCCTimeRange);
break;
case hash("ampeg_decaycc&"): // fallthrough
case hash("ampeg_decay_oncc&"):
case hash("ampeg_decay_oncc&"): // also ampeg_decaycc&
setCCPairFromOpcode(opcode, amplitudeEG.ccDecay, Default::egOnCCTimeRange);
break;
case hash("ampeg_delaycc&"): // fallthrough
case hash("ampeg_delay_oncc&"):
case hash("ampeg_delay_oncc&"): // also ampeg_delaycc&
setCCPairFromOpcode(opcode, amplitudeEG.ccDelay, Default::egOnCCTimeRange);
break;
case hash("ampeg_holdcc&"): // fallthrough
case hash("ampeg_hold_oncc&"):
case hash("ampeg_hold_oncc&"): // also ampeg_holdcc&
setCCPairFromOpcode(opcode, amplitudeEG.ccHold, Default::egOnCCTimeRange);
break;
case hash("ampeg_releasecc&"): // fallthrough
case hash("ampeg_release_oncc&"):
case hash("ampeg_release_oncc&"): // also ampeg_releasecc&
setCCPairFromOpcode(opcode, amplitudeEG.ccRelease, Default::egOnCCTimeRange);
break;
case hash("ampeg_startcc&"): // fallthrough
case hash("ampeg_start_oncc&"):
case hash("ampeg_start_oncc&"): // also ampeg_startcc&
setCCPairFromOpcode(opcode, amplitudeEG.ccStart, Default::egOnCCPercentRange);
break;
case hash("ampeg_sustaincc&"): // fallthrough
case hash("ampeg_sustain_oncc&"):
case hash("ampeg_sustain_oncc&"): // also ampeg_sustaincc&
setCCPairFromOpcode(opcode, amplitudeEG.ccSustain, Default::egOnCCPercentRange);
break;

View file

@ -236,7 +236,7 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
}
}
void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& members)
void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& rawMembers)
{
absl::string_view busName = "main";
@ -252,6 +252,11 @@ void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& members)
return *bus;
};
std::vector<Opcode> members;
members.reserve(rawMembers.size());
for (const Opcode& opcode : rawMembers)
members.push_back(opcode.cleanUp(kOpcodeScopeEffect));
for (const Opcode& opcode : members) {
switch (opcode.lettersOnlyHash) {
case hash("bus"):

View file

@ -158,3 +158,65 @@ TEST_CASE("[Opcode] Derived names")
REQUIRE(sfz::Opcode("noise_level_smoothcc55", "").getDerivedName(sfz::kOpcodeStepCcN) == "noise_level_stepcc55");
REQUIRE(sfz::Opcode("sample", "").getDerivedName(sfz::kOpcodeSmoothCcN, 66) == "sample_smoothcc66");
}
TEST_CASE("[Opcode] Normalization")
{
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == "foo_oncc7");
REQUIRE(sfz::Opcode("foo_cc7", "").cleanUp(sfz::kOpcodeScopeRegion).opcode == "foo_oncc7");
static const std::pair<absl::string_view, absl::string_view> regionSpecific[] = {
// LFO SFZv1
{"amplfo_depthcc1", "amplfo_depth_oncc1"},
{"fillfo_freqcc2", "fillfo_freq_oncc2"},
{"pitchlfo_fadecc3", "pitchlfo_fade_oncc3"},
// EG SFZv1
{"ampeg_delaycc4", "ampeg_delay_oncc4"},
{"fileg_startcc5", "fileg_start_oncc5"},
{"pitcheg_attackcc6", "pitcheg_attack_oncc6"},
{"ampeg_holdcc7", "ampeg_hold_oncc7"},
{"fileg_decaycc8", "fileg_decay_oncc8"},
{"pitcheg_sustaincc9", "pitcheg_sustain_oncc9"},
{"ampeg_releasecc10", "ampeg_release_oncc10"},
// EQ SFZv1
{"eq11_bwcc12", "eq11_bw_oncc12"},
{"eq13_freqcc14", "eq13_freq_oncc14"},
{"eq15_gaincc16", "eq15_gain_oncc16"},
// LFO SFZv2
{"lfo17_wave", "lfo17_wave1"},
{"lfo18_offset", "lfo18_offset1"},
{"lfo19_ratio", "lfo19_ratio1"},
{"lfo20_scale", "lfo20_scale1"},
// LinuxSampler aliases
{"loopmode", "loop_mode"},
{"loopstart", "loop_start"},
{"loopend", "loop_end"},
{"offby", "off_by"},
{"offmode", "off_mode"},
{"bendup", "bend_up"},
{"benddown", "bend_down"},
{"filtype", "fil1_type"},
{"fil21type", "fil21_type"},
// ARIA aliases
{"polyphony_group", "group"},
{"gain", "volume"},
{"gain_foobar", "volume_foobar"},
{"tune", "pitch"},
{"tune_foobar", "pitch_foobar"},
// SFZv2 aliases
{"on_hicc22", "start_hicc22"},
{"on_locc23", "start_locc23"},
// Filter SFZv1
{"fil_foobar", "fil1_foobar"},
{"cutoff", "cutoff1"},
{"cutoff_foobar", "cutoff1_foobar"},
{"resonance", "resonance1"},
{"resonance_foobar", "resonance1_foobar"},
};
for (auto pair : regionSpecific) {
absl::string_view input = pair.first;
absl::string_view expected = pair.second;
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeRegion).opcode == expected);
REQUIRE(sfz::Opcode(input, "").cleanUp(sfz::kOpcodeScopeGeneric).opcode == input);
}
}

View file

@ -1199,7 +1199,7 @@ TEST_CASE("[Region] Parsing opcodes")
region.parseOpcode({ "resonance3_cc24", "10" });
REQUIRE(region.filters[2].resonanceCC.contains(24));
REQUIRE(region.filters[2].resonanceCC[24] == 10);
region.parseOpcode({ "fil2_gaincc12", "-50" });
region.parseOpcode({ "fil2_gain_oncc12", "-50" });
REQUIRE(region.filters[1].gainCC.contains(12));
REQUIRE(region.filters[1].gainCC[12] == -50.0f);