Merge pull request #174 from paulfd/note-names
Add an opcode for note names, and API bindings for note and cc names
This commit is contained in:
commit
0bf02ac14f
13 changed files with 260 additions and 6 deletions
49
src/sfizz.h
49
src/sfizz.h
|
|
@ -380,6 +380,7 @@ SFIZZ_EXPORTED_API void sfizz_all_sound_off(sfizz_synth_t* synth);
|
||||||
* @brief Add external definitions prior to loading;
|
* @brief Add external definitions prior to loading;
|
||||||
* Note that these do not get reset by loading or resetting the synth.
|
* Note that these do not get reset by loading or resetting the synth.
|
||||||
* You need to call sfizz_clear_external_definitions() to erase them.
|
* You need to call sfizz_clear_external_definitions() to erase them.
|
||||||
|
* @since 0.4.0-dev
|
||||||
*
|
*
|
||||||
* @param synth
|
* @param synth
|
||||||
* @param id
|
* @param id
|
||||||
|
|
@ -389,11 +390,59 @@ SFIZZ_EXPORTED_API void sfizz_add_external_definitions(sfizz_synth_t* synth, con
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clears external definitions for the next file loading.
|
* @brief Clears external definitions for the next file loading.
|
||||||
|
* @since 0.4.0-dev
|
||||||
*
|
*
|
||||||
* @param synth
|
* @param synth
|
||||||
*/
|
*/
|
||||||
SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth);
|
SFIZZ_EXPORTED_API void sfizz_clear_external_definitions(sfizz_synth_t* synth);
|
||||||
|
|
||||||
|
#define SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of key labels registered in the current sfz file
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API unsigned int sfizz_get_num_key_labels(sfizz_synth_t* synth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key number for the label registered at index label_index.
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
* @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds.
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API int sfizz_get_key_label_number(sfizz_synth_t* synth, int label_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key text for the label registered at index label_index.
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
* @returns the label or NULL if the index is out of bounds.
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API const char * sfizz_get_key_label_text(sfizz_synth_t* synth, int label_index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of CC labels registered in the current sfz file
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the CC number for the label registered at index label_index.
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
* @returns the number or SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX if the index is out of bounds.
|
||||||
|
*/
|
||||||
|
|
||||||
|
SFIZZ_EXPORTED_API int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label_index);
|
||||||
|
/**
|
||||||
|
* @brief Get the CC text for the label registered at index label_index.
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
* @returns the label or NULL if the index is out of bounds.
|
||||||
|
*/
|
||||||
|
SFIZZ_EXPORTED_API const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
@ -287,7 +288,8 @@ public:
|
||||||
void setLoggingPrefix(const std::string& prefix) noexcept;
|
void setLoggingPrefix(const std::string& prefix) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Disable logging.
|
* @brief
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
void disableLogging() noexcept;
|
void disableLogging() noexcept;
|
||||||
|
|
||||||
|
|
@ -300,6 +302,7 @@ public:
|
||||||
* @brief Add external definitions prior to loading;
|
* @brief Add external definitions prior to loading;
|
||||||
* Note that these do not get reset by loading or resetting the synth.
|
* Note that these do not get reset by loading or resetting the synth.
|
||||||
* You need to call clearExternalDefintions() to erase them.
|
* You need to call clearExternalDefintions() to erase them.
|
||||||
|
* @since 0.4.0-dev
|
||||||
*
|
*
|
||||||
* @param id
|
* @param id
|
||||||
* @param value
|
* @param value
|
||||||
|
|
@ -308,10 +311,23 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Clears external definitions for the next file loading.
|
* @brief Clears external definitions for the next file loading.
|
||||||
|
* @since 0.4.0-dev
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void clearExternalDefinitions();
|
void clearExternalDefinitions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key labels, if any
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const std::vector<std::pair<uint8_t, std::string>>& getKeyLabels() const noexcept;
|
||||||
|
/**
|
||||||
|
* @brief Get the CC labels, if any
|
||||||
|
* @since 0.4.0-dev
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const std::vector<std::pair<uint16_t, std::string>>& getCCLabels() const noexcept;
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<sfz::Synth> synth;
|
std::unique_ptr<sfz::Synth> synth;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -208,6 +208,9 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
|
||||||
setValueFromOpcode(opcode, keyswitch, Default::keyRange);
|
setValueFromOpcode(opcode, keyswitch, Default::keyRange);
|
||||||
keySwitched = false;
|
keySwitched = false;
|
||||||
break;
|
break;
|
||||||
|
case hash("sw_label"):
|
||||||
|
keyswitchLabel = opcode.value;
|
||||||
|
break;
|
||||||
case hash("sw_down"):
|
case hash("sw_down"):
|
||||||
setValueFromOpcode(opcode, keyswitchDown, Default::keyRange);
|
setValueFromOpcode(opcode, keyswitchDown, Default::keyRange);
|
||||||
keySwitched = false;
|
keySwitched = false;
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,7 @@ struct Region {
|
||||||
CCMap<Range<float>> ccConditions { Default::ccValueRange };
|
CCMap<Range<float>> ccConditions { Default::ccValueRange };
|
||||||
Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey
|
Range<uint8_t> keyswitchRange { Default::keyRange }; // sw_hikey and sw_lokey
|
||||||
absl::optional<uint8_t> keyswitch {}; // sw_last
|
absl::optional<uint8_t> keyswitch {}; // sw_last
|
||||||
|
absl::optional<std::string> keyswitchLabel {};
|
||||||
absl::optional<uint8_t> keyswitchUp {}; // sw_up
|
absl::optional<uint8_t> keyswitchUp {}; // sw_up
|
||||||
absl::optional<uint8_t> keyswitchDown {}; // sw_down
|
absl::optional<uint8_t> keyswitchDown {}; // sw_down
|
||||||
absl::optional<uint8_t> previousNote {}; // sw_previous
|
absl::optional<uint8_t> previousNote {}; // sw_previous
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ namespace sfz
|
||||||
{
|
{
|
||||||
|
|
||||||
using CCNamePair = std::pair<uint16_t, std::string>;
|
using CCNamePair = std::pair<uint16_t, std::string>;
|
||||||
|
using NoteNamePair = std::pair<uint8_t, std::string>;
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using MidiNoteArray = std::array<T, 128>;
|
using MidiNoteArray = std::array<T, 128>;
|
||||||
template<class ValueType>
|
template<class ValueType>
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,9 @@ void sfz::Synth::clear()
|
||||||
numMasters = 0;
|
numMasters = 0;
|
||||||
defaultSwitch = absl::nullopt;
|
defaultSwitch = absl::nullopt;
|
||||||
defaultPath = "";
|
defaultPath = "";
|
||||||
ccNames.clear();
|
resources.midiState.reset();
|
||||||
|
ccLabels.clear();
|
||||||
|
keyLabels.clear();
|
||||||
globalOpcodes.clear();
|
globalOpcodes.clear();
|
||||||
masterOpcodes.clear();
|
masterOpcodes.clear();
|
||||||
groupOpcodes.clear();
|
groupOpcodes.clear();
|
||||||
|
|
@ -208,7 +210,11 @@ void sfz::Synth::handleControlOpcodes(const std::vector<Opcode>& members)
|
||||||
case hash("Label_cc&"): // fallthrough
|
case hash("Label_cc&"): // fallthrough
|
||||||
case hash("label_cc&"):
|
case hash("label_cc&"):
|
||||||
if (Default::ccNumberRange.containsWithEnd(member.parameters.back()))
|
if (Default::ccNumberRange.containsWithEnd(member.parameters.back()))
|
||||||
ccNames.emplace_back(member.parameters.back(), std::string(member.value));
|
ccLabels.emplace_back(member.parameters.back(), std::string(member.value));
|
||||||
|
break;
|
||||||
|
case hash("label_key&"):
|
||||||
|
if (Default::keyRange.containsWithEnd(member.parameters.back()))
|
||||||
|
keyLabels.emplace_back(member.parameters.back(), std::string(member.value));
|
||||||
break;
|
break;
|
||||||
case hash("Default_path"):
|
case hash("Default_path"):
|
||||||
// fallthrough
|
// fallthrough
|
||||||
|
|
@ -396,6 +402,10 @@ bool sfz::Synth::loadSfzFile(const fs::path& file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (region->keyswitchLabel && region->keyswitch)
|
||||||
|
keyswitchLabels.push_back({ *region->keyswitch, *region->keyswitchLabel });
|
||||||
|
|
||||||
|
|
||||||
// Some regions had group number but no "group-level" opcodes handled the polyphony
|
// Some regions had group number but no "group-level" opcodes handled the polyphony
|
||||||
while (groupMaxPolyphony.size() <= region->group)
|
while (groupMaxPolyphony.size() <= region->group)
|
||||||
groupMaxPolyphony.push_back(config::maxVoices);
|
groupMaxPolyphony.push_back(config::maxVoices);
|
||||||
|
|
@ -872,12 +882,15 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
||||||
chns.append_child("UsesControlNameList")
|
chns.append_child("UsesControlNameList")
|
||||||
.append_attribute("Name")
|
.append_attribute("Name")
|
||||||
.set_value("Controls");
|
.set_value("Controls");
|
||||||
|
chns.append_child("UsesNoteNameList")
|
||||||
|
.append_attribute("Name")
|
||||||
|
.set_value("Notes");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
pugi::xml_node cns = device.append_child("ControlNameList");
|
pugi::xml_node cns = device.append_child("ControlNameList");
|
||||||
cns.append_attribute("Name").set_value("Controls");
|
cns.append_attribute("Name").set_value("Controls");
|
||||||
for (const CCNamePair& pair : ccNames) {
|
for (const auto& pair : ccLabels) {
|
||||||
pugi::xml_node cn = cns.append_child("Control");
|
pugi::xml_node cn = cns.append_child("Control");
|
||||||
cn.append_attribute("Type").set_value("7bit");
|
cn.append_attribute("Type").set_value("7bit");
|
||||||
cn.append_attribute("Number").set_value(std::to_string(pair.first).c_str());
|
cn.append_attribute("Number").set_value(std::to_string(pair.first).c_str());
|
||||||
|
|
@ -885,6 +898,21 @@ std::string sfz::Synth::exportMidnam(absl::string_view model) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
pugi::xml_node nnl = device.append_child("NoteNameList");
|
||||||
|
nnl.append_attribute("Name").set_value("Notes");
|
||||||
|
for (const auto& pair : keyswitchLabels) {
|
||||||
|
pugi::xml_node nn = nnl.append_child("Note");
|
||||||
|
nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str());
|
||||||
|
nn.append_attribute("Name").set_value(pair.second.c_str());
|
||||||
|
}
|
||||||
|
for (const auto& pair : keyLabels) {
|
||||||
|
pugi::xml_node nn = nnl.append_child("Note");
|
||||||
|
nn.append_attribute("Number").set_value(std::to_string(pair.first).c_str());
|
||||||
|
nn.append_attribute("Name").set_value(pair.second.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
struct string_writer : pugi::xml_writer {
|
struct string_writer : pugi::xml_writer {
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
|
||||||
|
|
@ -388,6 +388,19 @@ public:
|
||||||
*/
|
*/
|
||||||
const Parser& getParser() const noexcept { return parser; }
|
const Parser& getParser() const noexcept { return parser; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the key labels, if any
|
||||||
|
*
|
||||||
|
* @return const std::vector<NoteNamePair>&
|
||||||
|
*/
|
||||||
|
const std::vector<NoteNamePair>& getKeyLabels() const noexcept { return keyLabels; }
|
||||||
|
/**
|
||||||
|
* @brief Get the CC labels, if any
|
||||||
|
*
|
||||||
|
* @return const std::vector<NoteNamePair>&
|
||||||
|
*/
|
||||||
|
const std::vector<CCNamePair>& getCCLabels() const noexcept { return ccLabels; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* @brief The parser callback; this is called by the parent object each time
|
* @brief The parser callback; this is called by the parent object each time
|
||||||
|
|
@ -494,8 +507,12 @@ private:
|
||||||
* @return Voice*
|
* @return Voice*
|
||||||
*/
|
*/
|
||||||
Voice* findFreeVoice() noexcept;
|
Voice* findFreeVoice() noexcept;
|
||||||
// Names for the cc as set by the label_cc opcode
|
|
||||||
std::vector<CCNamePair> ccNames;
|
// Names for the CC and notes as set by label_cc and label_key
|
||||||
|
std::vector<CCNamePair> ccLabels;
|
||||||
|
std::vector<NoteNamePair> keyLabels;
|
||||||
|
std::vector<NoteNamePair> keyswitchLabels;
|
||||||
|
|
||||||
// Default active switch if multiple keyswitchable regions are present
|
// Default active switch if multiple keyswitchable regions are present
|
||||||
absl::optional<uint8_t> defaultSwitch;
|
absl::optional<uint8_t> defaultSwitch;
|
||||||
std::vector<std::string> unknownOpcodes;
|
std::vector<std::string> unknownOpcodes;
|
||||||
|
|
|
||||||
|
|
@ -220,3 +220,13 @@ void sfz::Sfizz::clearExternalDefinitions()
|
||||||
{
|
{
|
||||||
synth->getParser().clearExternalDefinitions();
|
synth->getParser().clearExternalDefinitions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<std::pair<uint8_t, std::string>>& sfz::Sfizz::getKeyLabels() const noexcept
|
||||||
|
{
|
||||||
|
return synth->getKeyLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::pair<uint16_t, std::string>>& sfz::Sfizz::getCCLabels() const noexcept
|
||||||
|
{
|
||||||
|
return synth->getCCLabels();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "Macros.h"
|
#include "Macros.h"
|
||||||
#include "Synth.h"
|
#include "Synth.h"
|
||||||
#include "sfizz.h"
|
#include "sfizz.h"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -268,6 +269,80 @@ void sfizz_clear_external_definitions(sfizz_synth_t* synth)
|
||||||
self->getParser().clearExternalDefinitions();
|
self->getParser().clearExternalDefinitions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int sfizz_get_num_key_labels(sfizz_synth_t* synth)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->getKeyLabels().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfizz_get_key_label_number(sfizz_synth_t* synth, int label_index)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
const auto keyLabels = self->getKeyLabels();
|
||||||
|
if (label_index < 0)
|
||||||
|
return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX;
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(label_index) >= keyLabels.size())
|
||||||
|
return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX;
|
||||||
|
|
||||||
|
// Sanity checks for the future or platforms
|
||||||
|
static_assert(
|
||||||
|
std::numeric_limits<sfz::NoteNamePair::first_type>::max() < std::numeric_limits<int>::max(),
|
||||||
|
"The C API sends back an int but the note index in NoteNamePair can overflow it on this platform"
|
||||||
|
);
|
||||||
|
return static_cast<int>(keyLabels[label_index].first);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * sfizz_get_key_label_text(sfizz_synth_t* synth, int label_index)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
const auto keyLabels = self->getKeyLabels();
|
||||||
|
if (label_index < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(label_index) >= keyLabels.size())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return keyLabels[label_index].second.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int sfizz_get_num_cc_labels(sfizz_synth_t* synth)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
return self->getCCLabels().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int sfizz_get_cc_label_number(sfizz_synth_t* synth, int label_index)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
const auto ccLabels = self->getCCLabels();
|
||||||
|
if (label_index < 0)
|
||||||
|
return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX;
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(label_index) >= ccLabels.size())
|
||||||
|
return SFIZZ_OUT_OF_BOUNDS_LABEL_INDEX;
|
||||||
|
|
||||||
|
// Sanity checks for the future or platforms
|
||||||
|
static_assert(
|
||||||
|
std::numeric_limits<sfz::CCNamePair::first_type>::max() < std::numeric_limits<int>::max(),
|
||||||
|
"The C API sends back an int but the cc index in CCNamePair can overflow it on this platform"
|
||||||
|
);
|
||||||
|
return static_cast<int>(ccLabels[label_index].first);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char * sfizz_get_cc_label_text(sfizz_synth_t* synth, int label_index)
|
||||||
|
{
|
||||||
|
auto self = reinterpret_cast<sfz::Synth*>(synth);
|
||||||
|
const auto ccLabels = self->getCCLabels();
|
||||||
|
if (label_index < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(label_index) >= ccLabels.size())
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return ccLabels[label_index].second.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -563,3 +563,36 @@ TEST_CASE("[Files] Empty file")
|
||||||
REQUIRE(!synth.loadSfzFile({}));
|
REQUIRE(!synth.loadSfzFile({}));
|
||||||
REQUIRE(parser.getIncludedFiles().empty());
|
REQUIRE(parser.getIncludedFiles().empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Files] Labels")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels.sfz");
|
||||||
|
auto keyLabels = synth.getKeyLabels();
|
||||||
|
auto ccLabels = synth.getCCLabels();
|
||||||
|
REQUIRE( keyLabels.size() == 2);
|
||||||
|
REQUIRE( keyLabels[0].first == 12 );
|
||||||
|
REQUIRE( keyLabels[0].second == "Cymbals" );
|
||||||
|
REQUIRE( keyLabels[1].first == 65 );
|
||||||
|
REQUIRE( keyLabels[1].second == "Crash" );
|
||||||
|
REQUIRE( ccLabels.size() == 2);
|
||||||
|
REQUIRE( ccLabels[0].first == 54 );
|
||||||
|
REQUIRE( ccLabels[0].second == "Gain" );
|
||||||
|
REQUIRE( ccLabels[1].first == 2 );
|
||||||
|
REQUIRE( ccLabels[1].second == "Other" );
|
||||||
|
const std::string xmlMidnam = synth.exportMidnam();
|
||||||
|
REQUIRE(xmlMidnam.find("<Note Number=\"12\" Name=\"Cymbals\" />") != xmlMidnam.npos);
|
||||||
|
REQUIRE(xmlMidnam.find("<Note Number=\"65\" Name=\"Crash\" />") != xmlMidnam.npos);
|
||||||
|
REQUIRE(xmlMidnam.find("<Control Type=\"7bit\" Number=\"54\" Name=\"Gain\" />") != xmlMidnam.npos);
|
||||||
|
REQUIRE(xmlMidnam.find("<Control Type=\"7bit\" Number=\"2\" Name=\"Other\" />") != xmlMidnam.npos);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Files] Switch labels")
|
||||||
|
{
|
||||||
|
sfz::Synth synth;
|
||||||
|
synth.loadSfzFile(fs::current_path() / "tests/TestFiles/labels_sw.sfz");
|
||||||
|
const std::string xmlMidnam = synth.exportMidnam();
|
||||||
|
REQUIRE(xmlMidnam.find("<Note Number=\"36\" Name=\"Sine\" />") != xmlMidnam.npos);
|
||||||
|
REQUIRE(xmlMidnam.find("<Note Number=\"38\" Name=\"Triangle\" />") != xmlMidnam.npos);
|
||||||
|
REQUIRE(xmlMidnam.find("<Note Number=\"40\" Name=\"Saw\" />") != xmlMidnam.npos);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,15 @@ TEST_CASE("[Region] Parsing opcodes")
|
||||||
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 0));
|
REQUIRE(region.keyswitchRange == sfz::Range<uint8_t>(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SECTION("sw_label")
|
||||||
|
{
|
||||||
|
REQUIRE(!region.keyswitchLabel);
|
||||||
|
region.parseOpcode({ "sw_label", "note" });
|
||||||
|
REQUIRE(region.keyswitchLabel == "note");
|
||||||
|
region.parseOpcode({ "sw_label", "ring" });
|
||||||
|
REQUIRE(region.keyswitchLabel == "ring");
|
||||||
|
}
|
||||||
|
|
||||||
SECTION("sw_last")
|
SECTION("sw_last")
|
||||||
{
|
{
|
||||||
REQUIRE(!region.keyswitch);
|
REQUIRE(!region.keyswitch);
|
||||||
|
|
|
||||||
7
tests/TestFiles/labels.sfz
Normal file
7
tests/TestFiles/labels.sfz
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<control>
|
||||||
|
label_cc54=Gain
|
||||||
|
label_cc2=Other
|
||||||
|
label_key12=Cymbals
|
||||||
|
label_key65=Crash
|
||||||
|
label_key128=Ignored
|
||||||
|
<region> sample=*sine
|
||||||
4
tests/TestFiles/labels_sw.sfz
Normal file
4
tests/TestFiles/labels_sw.sfz
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<global> sw_lokey=36 sw_hikey=40 sw_default=36
|
||||||
|
<region> sw_last=36 sw_label=Sine lokey=41 sample=*sine
|
||||||
|
<region> sw_last=38 sw_label=Triangle lokey=41 sample=*triangle
|
||||||
|
<region> sw_last=40 sw_label=Saw lokey=41 sample=*saw
|
||||||
Loading…
Add table
Reference in a new issue