#include #include #include #include #include #include #include using std::int16_t; using std::int8_t; using std::uint16_t; using std::uint8_t; struct RangesType { uint8_t low; uint8_t high; }; static constexpr struct None_t { } None; // abs zero, min, min useful, max, max useful, default, default value static constexpr std::tuple defaults_table{ uint16_t{0}, // 0 int16_t{0}, // 1 int16_t{0}, // 2 int16_t{0}, // 3 int16_t{0}, // 4 int16_t{0}, // 5 int16_t{0}, // 6 int16_t{0}, // 7 uint16_t{13500}, // 8 uint16_t{0}, // 9 int16_t{0}, // 10 int16_t{0}, // 11 int16_t{0}, // 12 int16_t{0}, // 13 None, // 14 uint16_t{0}, // 15 uint16_t{0}, // 16 int16_t{0}, // 17 None, // 18 None, // 19 None, // 20 int16_t{-12000}, // 21 int16_t{0}, // 22 int16_t{-12000}, // 23 int16_t{0}, // 24 int16_t{-12000}, // 25 int16_t{-12000}, // 26 int16_t{-12000}, // 27 int16_t{-12000}, // 28 uint16_t{0}, // 29 int16_t{-12000}, // 30 int16_t{0}, // 31 int16_t{0}, // 32 int16_t{-12000}, // 33 int16_t{-12000}, // 34 int16_t{-12000}, // 35 int16_t{-12000}, // 36 uint16_t{0}, // 37 int16_t{-12000}, // 38 int16_t{0}, // 39 int16_t{0}, // 40 None, // 41 instrument None, // 42 RangesType{0, 127}, // 43 RangesType{0, 127}, // 44 int16_t{0}, // 45 uint8_t(-1), // 46 uint8_t(-1), // 47 uint16_t{0}, // 48 None, // 49 int16_t{0}, // 50 int8_t{0}, // 51 int8_t{0}, // 52 None, // 53 sampleID int16_t{0}, // 54 None, // 55 uint16_t{100}, // 56 uint8_t{0}, // 57 uint8_t(-1), // 58 None, // 59 None, // 60 None, // 61? }; struct generator_values { enum names { // Generators with a default value of 0 (unsigned 16 bit) startAddressOffset = 0, initialFilterFC = 8, initialFilterQ = 9, chorusEffectsSend = 15, reverbEffectsSend = 16, sustainModEnv = 29, sustainVolEnv = 37, instrument = 41, initialAttenuation = 48, scaleTuning = 56, // Generators with a default value of 0 (signed 16 bit) endAddressOffset = 1, startLoopAddressOffset = 2, endLoopAddressOffset = 3, startAddressCoarseOffset = 4, modLfoToPitch = 5, vibLfoToPitch = 6, modEnvToPitch = 7, modLfoToFilterFc = 10, modEnvToFilterFc = 11, endAddrsCoarseOffset = 12, modLfoToVolume = 13, pan = 17, freqModLFO = 22, freqVibLFO = 24, keynumToModEnvHold = 31, keynumToModEnvDecay = 32, keynumToVolEnvHold = 39, keynumToVolEnvDecay = 40, startloopAddrsCoarseOffset = 45, endloopAddrsCoarseOffset = 50, sampleModes = 54, // Generators with a default value of -12,000 (signed 16 bit) delayModLFO = 21, delayVibLFO = 23, delayModEnv = 25, attackModEnv = 26, holdModEnv = 27, decayModEnv = 28, releaseModEnv = 30, delayVolEnv = 33, attackVolEnv = 34, holdVolEnv = 35, decayVolEnv = 36, releaseVolEnv = 38, keyRange = 43, velRange = 44, // Generators with a default value of -1 (limited to range 0-127 oddly // enough) keynum = 46, velocity = 47, overridingRootKey = 58, // Generators with a default value of 0 (unsigned 8-bit) sampleID = 53, exclusiveClass = 57, // Generators with a default value of 0 (signed 8-bit) coarseTune = 51, fineTune = 52, // all other generators which aren't used. unused1 = 14, unused2 = 18, unused3 = 19, unused4 = 20, unused5 = 59, reserved1 = 42, reserved2 = 49, reserved3 = 55, endOper = 60, initialPitch = 61, }; std::tuple t; template friend auto get(T&& x) noexcept -> decltype(auto) { return std::get(std::forward(x).t); } }; struct amounts { int16_t shortAmount; uint16_t wordAmount; RangesType range; }; struct GeneratorListEntry { generator_values generatorValues; amounts genAmount; }; template auto lambda(bool fromPreset, T& lhs, const T rhs) noexcept -> void { if (fromPreset) { lhs += rhs; } else { lhs = rhs; } } template <> auto lambda(bool fromPreset, RangesType& lhs, RangesType rhs) noexcept -> void { if (fromPreset) { lhs.high += rhs.high; lhs.low += rhs.low; } else { lhs.high = rhs.high; lhs.low = rhs.low; } } template void assign(T&& lhs, U&& rhs) { lhs = static_cast(rhs); } template void assign(T&&, None_t) {} template void do_assign(std::integral_constant, T& lhs) { assign(lhs, std::tuple_element_t{}); } namespace detail { int8_t extract(int8_t, amounts& amt) { return static_cast(amt.shortAmount); } int16_t extract(int16_t, amounts& amt) { return amt.shortAmount; } uint8_t extract(uint8_t, amounts& amt) { return static_cast(amt.wordAmount); } uint16_t extract(uint16_t, amounts& amt) { return amt.wordAmount; } RangesType extract(RangesType, amounts& amt) { return amt.range; } } // namespace detail namespace impl { template void do_op(T& vals, [[maybe_unused]] std::index_sequence is, const std::size_t idx, Op op, Args&&... args) { static_assert(I != std::tuple_size_v, "index out of range"); if (idx == I) { op(std::integral_constant{}, std::get(vals), std::forward(args)...); } else if constexpr (I + 1 < std::tuple_size_v) { do_op(vals, is, idx, op, std::forward(args)...); } else { throw std::logic_error("Index out of range"); } } template constexpr std::array make_jump_table(std::index_sequence) noexcept { return {+[](T& vals, Op op, Args&&... args) { return op(std::integral_constant{}, std::get(vals), std::forward(args)...); }...}; } template constexpr void do_op2(T& vals, [[maybe_unused]] std::index_sequence is, const std::size_t idx, Op op, Args&&... args) { constexpr auto jump_table = make_jump_table(is); jump_table.at(idx)(vals, op, std::forward(args)...); } } // namespace impl // TMP API: template void apply_op(std::tuple& vals, std::size_t idx, Op op, Args&&... args) { impl::do_op2(vals, std::index_sequence_for{}, idx, op, std::forward(args)...); } // API use examples: void parseGenerator(GeneratorListEntry& generator, std::size_t idx, bool fromPreset) { apply_op( generator.generatorValues.t, idx, [](auto, auto& lhs, bool fromPreset, amounts& amt) { lambda(fromPreset, lhs, detail::extract(lhs, amt)); }, fromPreset, generator.genAmount); } void default_fill(generator_values& vals) { for (std::size_t i = 0; i <= generator_values::names::initialPitch; ++i) { apply_op(vals.t, i, [](auto I, auto& lhs) { assign(lhs, std::get(defaults_table)); }); } } std::ostream& operator<<(std::ostream& os, RangesType rhs) { return os << +rhs.low << '-' << +rhs.high; } RangesType operator+(RangesType x) { return x; } void print(generator_values& vals) { for (std::size_t i = 0; i <= generator_values::names::initialPitch; ++i) { apply_op(vals.t, i, [](auto, auto& lhs) { std::cout << +lhs << ' '; }); if (i % 16 == 15) { std::cout << '\n'; } } std::cout << '\n'; } int tuples_main(int, char**) { GeneratorListEntry g{}; print(g.generatorValues); std::cout << '\n'; default_fill(g.generatorValues); print(g.generatorValues); return 0; }