diff --git a/examples/Advanced/Utilities/ArrayCombiner.hpp b/examples/Advanced/Utilities/ArrayCombiner.hpp new file mode 100644 index 00000000..6c974663 --- /dev/null +++ b/examples/Advanced/Utilities/ArrayCombiner.hpp @@ -0,0 +1,122 @@ + +#pragma once +#include +#include +#include +#include +#include + +namespace ao +{ +enum class ArrayCombinerMode +{ + Sum, + Append, + Product, + Intersperse +}; +struct ArrayCombiner +{ + halp_meta(name, "Array Combiner") + halp_meta(c_name, "avnd_array_combiner") + halp_meta(author, "Jean-Michaël Celerier") + halp_meta(category, "Control/Mappings") + halp_meta(description, "Combine the input arrays") + halp_meta(manual_url, "https://ossia.io/score-docs/processes/array-utilities.html#sum") + halp_meta(uuid, "f5b8e20e-a016-4e19-a396-39d780179533") + + struct + { + struct : halp::spinbox_i32<"Input count", halp::range{0, 1024, 1}> + { + static std::function on_controller_interaction() + { + return [](ArrayCombiner& object, int value) { + object.inputs.in_i.request_port_resize(value); + }; + } + } controller; + + halp::enum_t mode; + halp::dynamic_port>> in_i; + } inputs; + + struct + { + halp::val_port<"Output", std::vector> out; + } outputs; + + void operator()() + { + if(inputs.in_i.ports.empty()) + return; + + auto& out = outputs.out.value; + + out.clear(); + switch(inputs.mode) + { + case ao::ArrayCombinerMode::Sum: { + for(auto& v : inputs.in_i.ports) + { + if(v.value.size() > out.size()) + { + out.resize(v.value.size()); + } + for(std::size_t i = 0; i < v.value.size(); i++) + { + out[i] += v.value[i]; + } + } + + break; + } + case ao::ArrayCombinerMode::Product: { + for(auto& v : inputs.in_i.ports) + { + if(v.value.size() > out.size()) + { + out.resize(v.value.size(), 1.f); + } + for(std::size_t i = 0; i < v.value.size(); i++) + { + out[i] *= v.value[i]; + } + } + + break; + } + case ao::ArrayCombinerMode::Append: { + std::size_t total_n = 0; + for(auto& v : inputs.in_i.ports) + total_n += v.value.size(); + out.reserve(total_n); + + for(auto& v : inputs.in_i.ports) + out.insert(out.end(), v.value.begin(), v.value.end()); + + break; + } + case ao::ArrayCombinerMode::Intersperse: { + std::size_t max_n = 0; + for(auto& v : inputs.in_i.ports) + max_n = std::max(max_n, v.value.size()); + + const auto ports = inputs.in_i.ports.size(); + out.resize(max_n * ports); + + for(std::size_t p = 0; p < ports; p++) + { + auto& in = inputs.in_i.ports[p].value; + for(std::size_t i = 0; i < max_n; i++) + { + if(in.size() > i) + out[i * ports + p] = in[i]; + } + } + break; + } + } + } +}; +} diff --git a/examples/Advanced/Utilities/ArrayTool.hpp b/examples/Advanced/Utilities/ArrayTool.hpp new file mode 100644 index 00000000..44eda0e9 --- /dev/null +++ b/examples/Advanced/Utilities/ArrayTool.hpp @@ -0,0 +1,211 @@ +#pragma once +#include "MapTool.hpp" +namespace ao +{ +/** + * Things we want: + * + * - A LED combination object to lay them out, reverse, etc + * -> dynamic + * + * - Basic LED pattern generation + * - Combine r, g, b channels from separate input arrays + * + * - W support + * + * - Some object to generate a pattern from characters ? + * + * - Some object to generate a pattern by drawing an array, like wavecycle? + * + * - Rotate a pattern ? + * + * - Array tool ? + * * rotate + * * repeat + * * reverse + * * window + * * stride + * * stride mode: 0, midpoint, keep value + * * gain + * * contrast + * + * + * - Array crush + * * + noise + * * + rand + * + * - HSV ? + */ + +enum class ArrayToolStrideMode +{ + Zero, + Keep +}; +struct ArrayTool +{ + halp_meta(name, "Array tool") + halp_meta(c_name, "array_tool") + halp_meta(category, "Control/Mappings") + halp_meta(author, "Jean-Michaël Celerier") + halp_meta(description, "Tool for processing arrays") + halp_meta( + manual_url, "https://ossia.io/score-docs/processes/array-utilities.html#tool") + halp_meta(uuid, "dc51200e-87d3-483e-8cb5-81491cad7384") + + struct ins + { + struct : halp::spinbox_f32<"Min", halp::free_range_min<>> + { + halp_flag(class_attribute); + std::function update_controller; + } in_min; + struct : halp::spinbox_f32<"Max", halp::free_range_max<>> + { + halp_flag(class_attribute); + std::function update_controller; + } in_max; + + halp::toggle<"Learn min"> min_learn; + halp::toggle<"Learn max"> max_learn; + + halp::knob_f32<"Gain", halp::range{-10., 10., 1.}> gain; + halp::knob_f32<"Brightness", halp::range{-10., 10., 0.}> bright; + // halp::knob_f32<"Contrast", halp::range{-1., 1., 0.}> contrast; + halp::enum_t range_behaviour; + halp::enum_t shape_behaviour; + halp::toggle<"Invert"> invert; + halp::toggle<"Absolute value"> absolute; + // halp::hslider_f32<"Window"> window; + // halp::hslider_f32<"Window Strength"> window_str; + + halp::spinbox_i32<"Pre-padding L", halp::range{-256, 256, 0}> in_pad_l; + halp::spinbox_i32<"Pre-padding R", halp::range{-256, 256, 0}> in_pad_r; + halp::toggle<"Reverse"> reverse; + halp::hslider_f32<"Rotate", halp::range{0., 1., 0.}> rotate; + halp::spinbox_i32<"Repeat", halp::range{0, 256, 1}> repeat; + halp::spinbox_i32<"Stride", halp::range{0, 256, 1}> stride; + halp::enum_t stridemode; + halp::spinbox_i32<"Post-padding L", halp::range{-256, 256, 0}> out_pad_l; + halp::spinbox_i32<"Post-padding R", halp::range{-256, 256, 0}> out_pad_r; + + halp::toggle<"Normalize"> normalize; + } inputs; + + struct messages + { + struct msg + { + halp_meta(name, "Array") + void operator()(ArrayTool& self, std::vector v) + { + self.process(v); + self.outputs.v(std::move(v)); + } + } m; + }; + + struct outs + { + halp::callback<"Array", std::vector> v; + } outputs; + + void process(std::vector& v) + { + if(v.empty()) + return; + /// Learn min / max + // TODO think of a better way to have host feature detection? + if(inputs.in_min.update_controller) + { + // FIXME if (inputs.min_learn && v < inputs.in_min) + // FIXME { + // FIXME inputs.in_min.value = v; + // FIXME inputs.in_min.update_controller(v); + // FIXME } + // FIXME if (inputs.max_learn && v > inputs.in_max) + // FIXME { + // FIXME inputs.in_max.value = v; + // FIXME inputs.in_max.update_controller(v); + // FIXME } + } + + for(float& val : v) + { + val = inputs.absolute ? std::abs(val) : val; + val = ao::rescale(val, inputs.in_min, inputs.in_max) * inputs.gain + inputs.bright; + val = ao::wrap(inputs.range_behaviour.value, val); + val = ao::shape(inputs.shape_behaviour.value, val); + val = inputs.invert ? 1.f - val : val; + val = inputs.normalize ? val + : val * (inputs.in_max - inputs.in_min) + inputs.in_min; + } + if(inputs.in_pad_l > 0) + v.insert(v.begin(), inputs.in_pad_l, 0.); + else if(inputs.in_pad_l < 0) + v.erase(v.begin(), v.begin() + std::min(int(v.size()), -inputs.in_pad_l)); + + if(inputs.in_pad_r > 0) + v.resize(v.size() + inputs.in_pad_r, 0.); + else if(inputs.in_pad_r < 0) + v.erase( + v.rbegin().base(), + (v.rbegin() + std::min(int(v.size()), -inputs.in_pad_r)).base()); + + if(inputs.reverse) + { + std::reverse(v.begin(), v.end()); + } + + if(std::size_t rotate_n = inputs.rotate * v.size(); + rotate_n > 0 && rotate_n < v.size()) + { + std::rotate(v.begin(), v.begin() + rotate_n, v.end()); + } + + if(int stride = inputs.stride; stride > 1) + { + int orig_sz = v.size(); + auto copy = v; + v.clear(); + v.resize(orig_sz * stride); + switch(this->inputs.stridemode) + { + case ArrayToolStrideMode::Zero: { + for(int i = 0; i < orig_sz; i++) + v[i * stride] = copy[i]; + break; + } + case ArrayToolStrideMode::Keep: { + for(int i = 0; i < orig_sz; i++) + for(int s = 0; s < stride; s++) + v[i * stride + s] = copy[i]; + break; + } + } + } + + if(int rpt = std::abs(inputs.repeat); rpt > 1) + { + int orig_sz = v.size(); + v.resize(rpt * orig_sz); + for(int i = 1; i < rpt; i++) + { + std::copy_n(v.begin(), orig_sz, v.begin() + i * orig_sz); + } + } + + if(inputs.out_pad_l > 0) + v.insert(v.begin(), inputs.out_pad_l, 0.); + else if(inputs.out_pad_l < 0) + v.erase(v.begin(), v.begin() + std::min(int(v.size()), -inputs.out_pad_l)); + + if(inputs.out_pad_r > 0) + v.resize(v.size() + inputs.out_pad_r, 0.); + else if(inputs.out_pad_r < 0) + v.erase( + v.rbegin().base(), + (v.rbegin() + std::min(int(v.size()), -inputs.out_pad_r)).base()); + } +}; +} diff --git a/examples/Advanced/Utilities/Combine.hpp b/examples/Advanced/Utilities/Combine.hpp index d4efb0f1..fe65ec65 100644 --- a/examples/Advanced/Utilities/Combine.hpp +++ b/examples/Advanced/Utilities/Combine.hpp @@ -20,7 +20,7 @@ struct Combine struct { - struct : halp::spinbox_i32<"Control", halp::range{0, 1024, 1}> + struct : halp::spinbox_i32<"Input count", halp::range{0, 1024, 1}> { static std::function on_controller_interaction() { diff --git a/examples/Advanced/Utilities/MapTool.hpp b/examples/Advanced/Utilities/MapTool.hpp index 5b353095..064453df 100644 --- a/examples/Advanced/Utilities/MapTool.hpp +++ b/examples/Advanced/Utilities/MapTool.hpp @@ -23,6 +23,48 @@ enum MapToolShapeMode Asym }; +// FIXME refactor with calibrator +static constexpr double rescale(double v, double min, double max) noexcept +{ + return (v - min) / (max - min); +} + +static inline double wrap(MapToolWrapMode mode, double f) +{ + using mode_t = MapToolWrapMode; + switch(mode) + { + case mode_t::Free: + return f; + default: + case mode_t::Clip: + return ossia::clamp(f, 0., 1.); + case mode_t::Wrap: + return ossia::wrap(f, 0., 1.); + case mode_t::Fold: + return ossia::fold(f, 0., 1.); + } +} + +static inline double shape(MapToolShapeMode mode, double f) +{ + using mode_t = MapToolShapeMode; + switch(mode) + { + default: + case mode_t::None: + return f; + case mode_t::Tanh: + return (std::tanh(f * 2. - 1.) + 1.) / 2.; + case mode_t::Sin: + return (std::sin(f * 2. - 1.) + 1.) / 2.; + case mode_t::Asym: { + double x = f * 2. - 1.; + double shape = (std::exp(x) - std::exp(-x * 1.2)) / (std::exp(x) + std::exp(-x)); + return (shape + 1.) / 2.; + } + } +} struct MapTool { halp_meta(name, "Mapping tool") @@ -68,48 +110,6 @@ struct MapTool { } outputs; - // FIXME refactor with calibrator - static constexpr double rescale(double v, double min, double max) noexcept - { - return (v - min) / (max - min); - } - - double wrap(double f) - { - using mode_t = MapToolWrapMode; - switch(inputs.range_behaviour.value) - { - case mode_t::Free: - return f; - default: - case mode_t::Clip: - return ossia::clamp(f, 0., 1.); - case mode_t::Wrap: - return ossia::wrap(f, 0., 1.); - case mode_t::Fold: - return ossia::fold(f, 0., 1.); - } - } - - double shape(double f) - { - using mode_t = MapToolShapeMode; - switch(inputs.shape_behaviour.value) - { - default: - case mode_t::None: - return f; - case mode_t::Tanh: - return (std::tanh(f * 2. - 1.) + 1.) / 2.; - case mode_t::Sin: - return (std::sin(f * 2. - 1.) + 1.) / 2.; - case mode_t::Asym: { - double x = f * 2. - 1.; - double shape = (std::exp(x) - std::exp(-x * 1.2)) / (std::exp(x) + std::exp(-x)); - return (shape + 1.) / 2.; - } - } - } double operator()(double v) { /// Learn min / max @@ -146,10 +146,10 @@ struct MapTool /// Apply operations // - Wrap - to_01 = wrap(to_01); + to_01 = wrap(inputs.range_behaviour.value, to_01); // - Shape - to_01 = shape(to_01); + to_01 = shape(inputs.shape_behaviour.value, to_01); // - Curve if(std::abs(inputs.curve.value) > 1e-12) diff --git a/examples/Advanced/Utilities/Spread.hpp b/examples/Advanced/Utilities/Spread.hpp index 6a297b96..c80f4536 100644 --- a/examples/Advanced/Utilities/Spread.hpp +++ b/examples/Advanced/Utilities/Spread.hpp @@ -20,7 +20,7 @@ struct Spread struct { - struct : halp::spinbox_i32<"Control", halp::range{0, 1024, 1}> + struct : halp::spinbox_i32<"Output count", halp::range{0, 1024, 1}> { static std::function on_controller_interaction() { diff --git a/include/avnd/binding/ossia/from_value.hpp b/include/avnd/binding/ossia/from_value.hpp index 9ff0d9c9..476d9138 100644 --- a/include/avnd/binding/ossia/from_value.hpp +++ b/include/avnd/binding/ossia/from_value.hpp @@ -1320,6 +1320,20 @@ inline bool from_ossia_value(const ossia::value& src, const char*& dst) } } +inline bool from_ossia_value(const ossia::value& src, std::string& dst) +{ + if(auto p = src.target()) + { + dst = std::move(*p); + return true; + } + else + { + dst = ""; + return false; + } +} + template < template typename T, typename Val, std::size_t N> diff --git a/include/avnd/concepts/generic.hpp b/include/avnd/concepts/generic.hpp index 6e191b89..f9598088 100644 --- a/include/avnd/concepts/generic.hpp +++ b/include/avnd/concepts/generic.hpp @@ -161,7 +161,8 @@ template concept pointer = std::is_pointer_v>; template -concept string_ish = span_ish && requires(T t, std::string_view v) { v = t; }; +concept string_ish = (span_ish && requires(T t, std::string_view v) { v = t; }) + || std::same_as; template concept int_ish = std::is_same_v, signed int>