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 # Parser core library
add_library (sfizz_parser STATIC) add_library (sfizz_parser STATIC)
target_sources (sfizz_parser PRIVATE 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) sfizz/parser/Parser.cpp sfizz/parser/ParserPrivate.cpp)
target_include_directories (sfizz_parser PUBLIC sfizz) target_include_directories (sfizz_parser PUBLIC sfizz)
target_include_directories (sfizz_parser PUBLIC external) target_include_directories (sfizz_parser PUBLIC external)

View file

@ -36,6 +36,18 @@ enum OpcodeCategory {
kOpcodeSmoothCcN, 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 * @brief Opcode description class. The class parses the parameters
* of the opcode on construction. * of the opcode on construction.
@ -51,6 +63,15 @@ struct Opcode {
std::vector<uint16_t> parameters; std::vector<uint16_t> parameters;
OpcodeCategory category; 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. * @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; 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) { switch (opcode.lettersOnlyHash) {
// Sound source: sample playback // Sound source: sample playback
case hash("sample"): case hash("sample"):
@ -65,7 +67,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("offset_random"): case hash("offset_random"):
setValueFromOpcode(opcode, offsetRandom, Default::offsetRange); setValueFromOpcode(opcode, offsetRandom, Default::offsetRange);
break; break;
case hash("offset_cc&"): case hash("offset_oncc&"): // also offset_cc&
if (opcode.parameters.back() > config::numCCs) if (opcode.parameters.back() > config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::offsetCCRange)) if (auto value = readOpcode(opcode.value, Default::offsetCCRange))
@ -77,8 +79,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("count"): case hash("count"):
setValueFromOpcode(opcode, sampleCount, Default::sampleCountRange); setValueFromOpcode(opcode, sampleCount, Default::sampleCountRange);
break; break;
case hash("loopmode"): // fallthrough case hash("loop_mode"): // also loopmode
case hash("loop_mode"):
switch (hash(opcode.value)) { switch (hash(opcode.value)) {
case hash("no_loop"): case hash("no_loop"):
loopMode = SfzLoopMode::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)); DBG("Unkown loop mode:" << std::string(opcode.value));
} }
break; break;
case hash("loopend"): // fallthrough case hash("loop_end"): // also loopend
case hash("loop_end"):
setRangeEndFromOpcode(opcode, loopRange, Default::loopRange); setRangeEndFromOpcode(opcode, loopRange, Default::loopRange);
break; break;
case hash("loopstart"): // fallthrough case hash("loop_start"): // also loopstart
case hash("loop_start"):
setRangeStartFromOpcode(opcode, loopRange, Default::loopRange); setRangeStartFromOpcode(opcode, loopRange, Default::loopRange);
break; break;
@ -121,15 +120,13 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break; break;
// Instrument settings: voice lifecycle // Instrument settings: voice lifecycle
case hash("group"): // fallthrough case hash("group"): // also polyphony_group
case hash("polyphony_group"):
setValueFromOpcode(opcode, group, Default::groupRange); setValueFromOpcode(opcode, group, Default::groupRange);
break; break;
case hash("offby"): // fallthrough case hash("off_by"): // also offby
case hash("off_by"):
setValueFromOpcode(opcode, offBy, Default::groupRange); setValueFromOpcode(opcode, offBy, Default::groupRange);
break; break;
case hash("off_mode"): case hash("off_mode"): // also offmode
switch (hash(opcode.value)) { switch (hash(opcode.value)) {
case hash("fast"): case hash("fast"):
offMode = SfzOffMode::fast; offMode = SfzOffMode::fast;
@ -292,15 +289,13 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
DBG("Unknown trigger mode: " << std::string(opcode.value)); DBG("Unknown trigger mode: " << std::string(opcode.value));
} }
break; break;
case hash("on_locc&"): // fallthrough case hash("start_locc&"): // also on_locc&
case hash("start_locc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::midi7Range)) if (auto value = readOpcode(opcode.value, Default::midi7Range))
ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value)); ccTriggers[opcode.parameters.back()].setStart(normalizeCC(*value));
break; break;
case hash("on_hicc&"): // fallthrough case hash("start_hicc&"): // also on_hicc&
case hash("start_hicc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::midi7Range)) if (auto value = readOpcode(opcode.value, Default::midi7Range))
@ -308,30 +303,28 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break; break;
// Performance parameters: amplifier // Performance parameters: amplifier
case hash("volume"): case hash("volume"): // also gain
setValueFromOpcode(opcode, volume, Default::volumeRange); setValueFromOpcode(opcode, volume, Default::volumeRange);
break; break;
case hash("volume_curvecc&"): case hash("volume_curvecc&"): // also gain_curvecc&
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::curveCCRange)) if (auto value = readOpcode(opcode.value, Default::curveCCRange))
volumeCC[opcode.parameters.back()].curve = *value; volumeCC[opcode.parameters.back()].curve = *value;
break; break;
case hash("volume_stepcc&"): case hash("volume_stepcc&"): // also gain_stepcc&
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::volumeStepRange)) if (auto value = readOpcode(opcode.value, Default::volumeStepRange))
volumeCC[opcode.parameters.back()].step = *value; volumeCC[opcode.parameters.back()].step = *value;
break; break;
case hash("volume_smoothcc&"): case hash("volume_smoothcc&"): // also gain_smoothcc&
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
volumeCC[opcode.parameters.back()].smooth = *value; volumeCC[opcode.parameters.back()].smooth = *value;
break; break;
case hash("gain_cc&"): case hash("volume_oncc&"): // also gain_oncc&
case hash("gain_oncc&"): // fallthrough
case hash("volume_oncc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) 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)) if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
amplitudeCC[opcode.parameters.back()].smooth = *value; amplitudeCC[opcode.parameters.back()].smooth = *value;
break; break;
case hash("amplitude_cc&"): // fallthrough case hash("amplitude_oncc&"): // also amplitude_cc&
case hash("amplitude_oncc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) 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)) if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
panCC[opcode.parameters.back()].smooth = *value; panCC[opcode.parameters.back()].smooth = *value;
break; break;
case hash("pan_cc&"): case hash("pan_oncc&"): // also pan_cc&
case hash("pan_oncc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::panCCRange)) 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)) if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
positionCC[opcode.parameters.back()].smooth = *value; positionCC[opcode.parameters.back()].smooth = *value;
break; break;
case hash("position_cc&"): // fallthrough case hash("position_oncc&"): // also position_cc&
case hash("position_oncc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::positionCCRange)) if (auto value = readOpcode(opcode.value, Default::positionCCRange))
@ -567,8 +557,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break; break;
// Performance parameters: filters // Performance parameters: filters
case hash("cutoff"): // fallthrough case hash("cutoff&"): // also cutoff
case hash("cutoff&"):
{ {
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1); const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1);
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) 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); setValueFromOpcode(opcode, filters[filterIndex].cutoff, Default::filterCutoffRange);
} }
break; break;
case hash("resonance"): // fallthrough case hash("resonance&"): // also resonance
case hash("resonance&"):
{ {
const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1); const auto filterIndex = opcode.parameters.empty() ? 0 : (opcode.parameters.back() - 1);
if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters)) 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); setValueFromOpcode(opcode, filters[filterIndex].resonance, Default::filterResonanceRange);
} }
break; break;
case hash("cutoff_oncc&"): case hash("cutoff&_oncc&"): // also cutoff_oncc&, cutoff_cc&, cutoff&_cc&
case hash("cutoff_cc&"):
case hash("cutoff&_oncc&"): // fallthrough
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
@ -601,12 +586,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
); );
} }
break; break;
case hash("resonance&_oncc&"): case hash("resonance&_oncc&"): // also resonance_oncc&, resonance_cc&, resonance&_cc&
case hash("resonance&_cc&"):
case hash("resonance_oncc&"): // fallthrough
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
@ -617,60 +599,54 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
); );
} }
break; break;
case hash("fil_keytrack"): // fallthrough case hash("fil&_keytrack"): // also fil_keytrack
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
setValueFromOpcode(opcode, filters[filterIndex].keytrack, Default::filterKeytrackRange); setValueFromOpcode(opcode, filters[filterIndex].keytrack, Default::filterKeytrackRange);
} }
break; break;
case hash("fil_keycenter"): // fallthrough case hash("fil&_keycenter"): // also fil_keycenter
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
setValueFromOpcode(opcode, filters[filterIndex].keycenter, Default::keyRange); setValueFromOpcode(opcode, filters[filterIndex].keycenter, Default::keyRange);
} }
break; break;
case hash("fil_veltrack"): // fallthrough case hash("fil&_veltrack"): // also fil_veltrack
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
setValueFromOpcode(opcode, filters[filterIndex].veltrack, Default::filterVeltrackRange); setValueFromOpcode(opcode, filters[filterIndex].veltrack, Default::filterVeltrackRange);
} }
break; break;
case hash("fil_random"): // fallthrough case hash("fil&_random"): // also fil_random
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
setValueFromOpcode(opcode, filters[filterIndex].random, Default::filterRandomRange); setValueFromOpcode(opcode, filters[filterIndex].random, Default::filterRandomRange);
} }
break; break;
case hash("fil_gain"): // fallthrough case hash("fil&_gain"): // also fil_gain
case hash("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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
setValueFromOpcode(opcode, filters[filterIndex].gain, Default::filterGainRange); setValueFromOpcode(opcode, filters[filterIndex].gain, Default::filterGainRange);
} }
break; break;
case hash("fil_gaincc&"): // fallthrough case hash("fil&_gain_oncc&"): // also fil_gain_oncc&
case hash("fil&_gaincc&"):
{ {
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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
@ -681,10 +657,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
); );
} }
break; break;
case hash("fil_type"): // fallthrough case hash("fil&_type"): // also fil_type, filtype
case hash("fil&_type"):
{ {
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)) if (!extendIfNecessary(filters, filterIndex + 1, Default::numFilters))
return false; return false;
@ -710,8 +685,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, equalizers[eqNumber - 1].bandwidth, Default::eqBandwidthRange); setValueFromOpcode(opcode, equalizers[eqNumber - 1].bandwidth, Default::eqBandwidthRange);
} }
break; break;
case hash("eq&_bw_oncc&"): // fallthrough case hash("eq&_bw_oncc&"): // also eq&_bwcc&
case hash("eq&_bwcc&"):
{ {
const auto eqNumber = opcode.parameters.front(); const auto eqNumber = opcode.parameters.front();
if (eqNumber == 0) if (eqNumber == 0)
@ -732,8 +706,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, equalizers[eqNumber - 1].frequency, Default::eqFrequencyRange); setValueFromOpcode(opcode, equalizers[eqNumber - 1].frequency, Default::eqFrequencyRange);
} }
break; break;
case hash("eq&_freq_oncc&"): // fallthrough case hash("eq&_freq_oncc&"): // also eq&_freqcc&
case hash("eq&_freqcc&"):
{ {
const auto eqNumber = opcode.parameters.front(); const auto eqNumber = opcode.parameters.front();
if (eqNumber == 0) if (eqNumber == 0)
@ -767,8 +740,7 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
setValueFromOpcode(opcode, equalizers[eqNumber - 1].gain, Default::eqGainRange); setValueFromOpcode(opcode, equalizers[eqNumber - 1].gain, Default::eqGainRange);
} }
break; break;
case hash("eq&_gain_oncc&"): // fallthrough case hash("eq&_gain_oncc&"): // also eq&_gaincc&
case hash("eq&_gaincc&"):
{ {
const auto eqNumber = opcode.parameters.front(); const auto eqNumber = opcode.parameters.front();
if (eqNumber == 0) if (eqNumber == 0)
@ -827,44 +799,37 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
case hash("transpose"): case hash("transpose"):
setValueFromOpcode(opcode, transpose, Default::transposeRange); setValueFromOpcode(opcode, transpose, Default::transposeRange);
break; break;
case hash("tune"): // fallthrough case hash("pitch"): // also tune
case hash("pitch"):
setValueFromOpcode(opcode, tune, Default::tuneRange); setValueFromOpcode(opcode, tune, Default::tuneRange);
break; break;
case hash("pitch_curvecc&"): // fallthrough case hash("pitch_curvecc&"): // also tune_curvecc&
case hash("tune_curvecc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::curveCCRange)) if (auto value = readOpcode(opcode.value, Default::curveCCRange))
tuneCC[opcode.parameters.back()].curve = *value; tuneCC[opcode.parameters.back()].curve = *value;
break; break;
case hash("pitch_stepcc&"): // fallthrough case hash("pitch_stepcc&"): // also tune_stepcc&
case hash("tune_stepcc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::tuneStepRange)) if (auto value = readOpcode(opcode.value, Default::tuneStepRange))
tuneCC[opcode.parameters.back()].step = *value; tuneCC[opcode.parameters.back()].step = *value;
break; break;
case hash("pitch_smoothcc&"): // fallthrough case hash("pitch_smoothcc&"): // also tune_smoothcc&
case hash("tune_smoothcc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::smoothCCRange)) if (auto value = readOpcode(opcode.value, Default::smoothCCRange))
tuneCC[opcode.parameters.back()].smooth = *value; tuneCC[opcode.parameters.back()].smooth = *value;
break; break;
case hash("tune_cc&"): case hash("pitch_oncc&"): // also pitch_cc&, tune_cc&, tune_oncc&
case hash("tune_oncc&"):
case hash("pitch_cc&"):
case hash("pitch_oncc&"):
if (opcode.parameters.back() >= config::numCCs) if (opcode.parameters.back() >= config::numCCs)
return false; return false;
if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) if (auto value = readOpcode(opcode.value, Default::tuneCCRange))
tuneCC[opcode.parameters.back()].value = *value; tuneCC[opcode.parameters.back()].value = *value;
break; break;
case hash("bend_up"): case hash("bend_up"): // also bendup
setValueFromOpcode(opcode, bendUp, Default::bendBoundRange); setValueFromOpcode(opcode, bendUp, Default::bendBoundRange);
break; break;
case hash("bend_down"): case hash("bend_down"): // also benddown
setValueFromOpcode(opcode, bendDown, Default::bendBoundRange); setValueFromOpcode(opcode, bendDown, Default::bendBoundRange);
break; break;
case hash("bend_step"): case hash("bend_step"):
@ -923,32 +888,25 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
return false; // Was not vel2... return false; // Was not vel2...
setValueFromOpcode(opcode, amplitudeEG.vel2sustain, Default::egOnCCPercentRange); setValueFromOpcode(opcode, amplitudeEG.vel2sustain, Default::egOnCCPercentRange);
break; break;
case hash("ampeg_attackcc&"): // fallthrough case hash("ampeg_attack_oncc&"): // also ampeg_attackcc&
case hash("ampeg_attack_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccAttack, Default::egOnCCTimeRange); setCCPairFromOpcode(opcode, amplitudeEG.ccAttack, Default::egOnCCTimeRange);
break; break;
case hash("ampeg_decaycc&"): // fallthrough case hash("ampeg_decay_oncc&"): // also ampeg_decaycc&
case hash("ampeg_decay_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccDecay, Default::egOnCCTimeRange); setCCPairFromOpcode(opcode, amplitudeEG.ccDecay, Default::egOnCCTimeRange);
break; break;
case hash("ampeg_delaycc&"): // fallthrough case hash("ampeg_delay_oncc&"): // also ampeg_delaycc&
case hash("ampeg_delay_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccDelay, Default::egOnCCTimeRange); setCCPairFromOpcode(opcode, amplitudeEG.ccDelay, Default::egOnCCTimeRange);
break; break;
case hash("ampeg_holdcc&"): // fallthrough case hash("ampeg_hold_oncc&"): // also ampeg_holdcc&
case hash("ampeg_hold_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccHold, Default::egOnCCTimeRange); setCCPairFromOpcode(opcode, amplitudeEG.ccHold, Default::egOnCCTimeRange);
break; break;
case hash("ampeg_releasecc&"): // fallthrough case hash("ampeg_release_oncc&"): // also ampeg_releasecc&
case hash("ampeg_release_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccRelease, Default::egOnCCTimeRange); setCCPairFromOpcode(opcode, amplitudeEG.ccRelease, Default::egOnCCTimeRange);
break; break;
case hash("ampeg_startcc&"): // fallthrough case hash("ampeg_start_oncc&"): // also ampeg_startcc&
case hash("ampeg_start_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccStart, Default::egOnCCPercentRange); setCCPairFromOpcode(opcode, amplitudeEG.ccStart, Default::egOnCCPercentRange);
break; break;
case hash("ampeg_sustaincc&"): // fallthrough case hash("ampeg_sustain_oncc&"): // also ampeg_sustaincc&
case hash("ampeg_sustain_oncc&"):
setCCPairFromOpcode(opcode, amplitudeEG.ccSustain, Default::egOnCCPercentRange); setCCPairFromOpcode(opcode, amplitudeEG.ccSustain, Default::egOnCCPercentRange);
break; 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"; absl::string_view busName = "main";
@ -252,6 +252,11 @@ void sfz::Synth::handleEffectOpcodes(const std::vector<Opcode>& members)
return *bus; 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) { for (const Opcode& opcode : members) {
switch (opcode.lettersOnlyHash) { switch (opcode.lettersOnlyHash) {
case hash("bus"): 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("noise_level_smoothcc55", "").getDerivedName(sfz::kOpcodeStepCcN) == "noise_level_stepcc55");
REQUIRE(sfz::Opcode("sample", "").getDerivedName(sfz::kOpcodeSmoothCcN, 66) == "sample_smoothcc66"); 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" }); region.parseOpcode({ "resonance3_cc24", "10" });
REQUIRE(region.filters[2].resonanceCC.contains(24)); REQUIRE(region.filters[2].resonanceCC.contains(24));
REQUIRE(region.filters[2].resonanceCC[24] == 10); 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.contains(12));
REQUIRE(region.filters[1].gainCC[12] == -50.0f); REQUIRE(region.filters[1].gainCC[12] == -50.0f);