#pragma once #include "Helpers.h" #include "SfzHelpers.h" #include "Defaults.h" #include "Range.h" #include #include // charconv support is still sketchy with clang/gcc so we use abseil's numbers #include "absl/strings/numbers.h" namespace sfz { struct Opcode { Opcode() = delete; Opcode(std::string_view inputOpcode, std::string_view inputValue); std::string_view opcode{}; std::string_view value{}; // This is to handle the integer parameter of some opcodes std::optional parameter; }; template inline std::optional readOpcode(std::string_view value, const Range& validRange) { if constexpr(std::is_integral::value) { int64_t returnedValue; if (!absl::SimpleAtoi(value, &returnedValue)) return {}; if (returnedValue > std::numeric_limits::max()) returnedValue = std::numeric_limits::max(); if (returnedValue < std::numeric_limits::min()) returnedValue = std::numeric_limits::min(); return validRange.clamp(static_cast(returnedValue)); } else { float returnedValue; if (!absl::SimpleAtof(value, &returnedValue)) return std::nullopt; return validRange.clamp(returnedValue); } } template inline void setValueFromOpcode(const Opcode& opcode, ValueType& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (!value) // Try and read a note rather than a number value = readNoteValue(opcode.value); if (value) target = *value; } template inline void setValueFromOpcode(const Opcode& opcode, std::optional& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (!value) // Try and read a note rather than a number value = readNoteValue(opcode.value); if (value) target = *value; } template inline void setRangeEndFromOpcode(const Opcode& opcode, Range& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (!value) // Try and read a note rather than a number value = readNoteValue(opcode.value); if (value) target.setEnd(*value); } template inline void setRangeStartFromOpcode(const Opcode& opcode, Range& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (!value) // Try and read a note rather than a number value = readNoteValue(opcode.value); if (value) target.setStart(*value); } template inline void setCCPairFromOpcode(const Opcode& opcode, std::optional& target, const Range& validRange) { auto value = readOpcode(opcode.value, validRange); if (value && opcode.parameter && Default::ccRange.containsWithEnd(*opcode.parameter)) target = std::make_pair(*opcode.parameter, *value); else target = {}; } }