diff --git a/common/math.hpp b/common/math.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9e62b473fcd42e683c1b1ca80fda73f0c4b8cd70 --- /dev/null +++ b/common/math.hpp @@ -0,0 +1,170 @@ +#ifndef COMMON_MATH_HPP +#define COMMON_MATH_HPP +#include "simple/support.hpp" +#include "simple/geom.hpp" + +namespace common +{ + +using namespace simple; + +constexpr auto half = geom::vector<float,2>::one(.5f); +const float tau = 2*std::acos(-1); + +template <typename Number, typename Ratio = float> +[[nodiscard]] constexpr +Number lerp(Number from, Number to, Ratio ratio) +{ + return from + (to - from) * ratio; +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector project_on_unit(Vector x, Vector surface) +{ + return surface * surface(x); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector project(Vector x, Vector surface) +{ + return project_on_unit(x,surface) / surface.magnitude(); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector reject_from_unit(Vector x, Vector surface) +{ + return x - project_on_unit(x,surface); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector reject(Vector x, Vector surface) +{ + return x - project(x,surface); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector reflect_in_unit(Vector x, Vector surface) +{ + return project_on_unit(x,surface) - reject_from_unit(x,surface); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector reflect(Vector x, Vector surface) +{ + return project(x,surface) - reject(x,surface); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector rotate_scale(Vector x, Vector half_angle) +{ + return reflect_in_unit( + reflect_in_unit(x, Vector::i()), + half_angle ); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector rotate(Vector x, Vector half_angle) +{ + return reflect( reflect(x, Vector::i()), half_angle ); +} + +template <typename Vector> +[[nodiscard]] constexpr +Vector normalize(Vector x) +{ + return x / support::root2(x.quadrance()); +} + +template <size_t Exponent = 3, typename Value = float> +class protractor +{ + public: + using vector = geom::vector<Value,2>; + + using array = std::array<vector, (size_t(1)<<Exponent) + 1>; + + static constexpr array circle = []() + { + using support::midpoint; + + array points{}; + + points.front() = vector::i(); + points.back() = -vector::i(); + + auto bisect = [](auto begin, auto end, + auto& self) + { + if(end - begin <= 2) + return; + + auto middle = midpoint(begin, end); + *(middle) = normalize(midpoint(*begin, *(end - 1))); + + self(begin, middle + 1, + self); + self(middle, end, + self); + }; + + if(Exponent > 0) + { + auto middle = midpoint(points.begin(), points.end()); + *middle = vector::j(); + + bisect(points.begin(), middle + 1, + bisect); + bisect(middle, points.end(), + bisect); + } + + return points; + }(); + + + static constexpr vector tau(Value factor) + { + assert(Value{0} <= factor && factor < Value{1}); + Value index = factor * (protractor::circle.size() - 1); + int whole = index; + Value fraction = index - whole; + return lerp(circle[whole], circle[whole+1], fraction); + } + + static constexpr vector tau() + { + return -vector::i(); + } + + static constexpr vector small_radian(Value value) + { + return {support::root2(Value{1} - value*value), value}; + } + + static vector radian(Value angle) + { + return {std::cos(angle), std::sin(angle)}; + } + + static constexpr vector angle(Value tau_factor) + { + if(tau_factor < (Value{1}/(circle.size()-1))/16 ) + return small_radian(tau_factor * common::tau); + else + return protractor::tau(tau_factor); + } + +}; + +} // namespace common + +#endif /* end of include guard */ + diff --git a/common/motion.hpp b/common/motion.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4d1c4f1e39f473c2f897ae5501a34bc689a5f3cf --- /dev/null +++ b/common/motion.hpp @@ -0,0 +1,192 @@ +// what even is a tween? +// shorhand for between?? +// what even is between??? + +#ifndef COMMON_MOTION_HPP +#define COMMON_MOTION_HPP + +float linear_curve(float x) { return x; }; +float quadratic_curve(float x) { return x*x; }; +float cubic_curve(float x) { return x*x*x; }; + +using curve_t = decltype(&linear_curve); + +struct advance_result +{ + bool success = false; + Program::duration remaining = 0s; + explicit operator bool() { return success; } +}; +struct multi_advance_result : public advance_result +{ + range<size_t> updated = range<size_t>{0,0}; + using advance_result::operator bool; +}; + +template <typename Type, curve_t curve = linear_curve> +struct motion +{ + Type start; + Type end; + Program::duration total = 0s; + Program::duration elapsed = 0s; + + decltype(auto) value() + { + auto ratio = total == 0s ? 0 : elapsed.count()/total.count(); + return lerp(start, end, curve(ratio)); + } + + bool done() + { + return elapsed >= total; + } + + advance_result advance(Program::duration delta) + { + elapsed += delta; + if(done()) + { + elapsed = total; + return {false}; + } + return {true}; + } + + void reset() + { + elapsed = 0s; + } + + bool move(Type& target, Program::duration delta) + { + bool success = advance(delta).success; + target = value(); + return !success; + } +}; + +// welcome to proper looping +// TODO: for know total duration mod(remaining, total) +// TODO: for unknown total while(move, !success){delta = remaining} +template <typename Motion, typename Target> +bool loop(Target&& target, Motion& motion, Program::duration delta) +{ + if(auto [success, remaining] = motion.move(std::forward<Target>(target), delta); + !success) + { + motion.reset(); + motion.advance(remaining); + return true; + } + return false; +} + +template <typename... Motions> +class ensemble // TODO +{ + std::tuple<Motions...> instruments; + bool done(); + bool advance(Program::duration delta); + void reset(); + template <typename T> + bool move(T& target, Program::duration delta); +}; + +template <typename... Motions> +class symphony +{ + std::tuple<Motions...> movements; + size_t current_index = 0; + + template<typename F, size_t I = sizeof...(Motions) - 1> + void for_all(F&& f) + { + static_assert(I >= 0 && I < sizeof...(Motions)); + std::forward<F>(f)(std::get<I>(movements)); + if constexpr (I > 0) + for_all<F, I-1>(std::forward<F>(f)); + } + + multi_advance_result advance(Program::duration delta, range<size_t> updated) + { + auto [success, remaining] = support::apply_for(current_index, [&delta](auto&& movement) + { + return movement.advance(delta); + }, movements); + + if(!success) + { + if(current_index == sizeof...(Motions) - 1) + return {success, remaining, updated}; + + ++current_index; + + if(remaining > 0s) + return advance(remaining, {updated.lower(), updated.upper()+1}); + else + return {true, remaining, updated}; + } + else + { + return {success, remaining, updated}; + } + } + + public: + + symphony() = default; + symphony(Motions... motions) : movements{motions...} {} + + bool done() + { + return std::get<sizeof...(Motions) - 1>(movements).done(); + } + + auto advance(Program::duration delta) + { + return advance(delta, {current_index, current_index + 1}); + } + + template <size_t... I> + std::tuple<decltype(std::declval<Motions>().value())...> value(std::index_sequence<I...>) + { + return {std::get<I>(movements).value()...}; + } + + std::tuple<decltype(std::declval<Motions>().value())...> value() + { + return value(std::make_index_sequence<sizeof...(Motions)>{}); + } + + void reset() + { + for_all([](auto& movement) {movement.reset();}); + current_index = 0; + } + + template <typename T> + advance_result move(T& target, Program::duration delta) + { + auto result = advance(delta); + support::apply_for(current_index, [&target](auto&& movement) + { + target = movement.value(); + }, movements); + return result; + } + + template <typename... T, + std::enable_if_t<sizeof...(T) == sizeof...(Motions)>* = nullptr> + advance_result move(std::tuple<T...>&& targets, Program::duration delta) + { + auto r = advance(delta); + support::apply_for(r.updated, [](auto&& movement, auto&& target) + { + target = movement.value(); + }, movements, std::forward<std::tuple<T...>>(targets)); + return r; + } +}; + +#endif /* end of include guard */ diff --git a/common/sketchbook.hpp b/common/sketchbook.hpp index 4adb4a409fb67873249f5f025fa526e66bdbca55..bdcdb0a72547acd7eb3c439c392b986aa0702208 100644 --- a/common/sketchbook.hpp +++ b/common/sketchbook.hpp @@ -1,6 +1,7 @@ #include <cstring> #include <cstdio> #include <thread> +#include <mutex> #include <functional> #include <iostream> #include <random> @@ -9,11 +10,13 @@ #include "simple/support.hpp" #include "simple/graphical.hpp" +#include "simple/musical.hpp" #include "simple/interactive/initializer.h" #include "simple/interactive/event.h" #include "simple_vg.h" #include "simple_vg.cpp" +#include "math.hpp" #if defined __EMSCRIPTEN__ #include <emscripten.h> @@ -39,8 +42,7 @@ using rgba32 = graphical::rgba_pixel; using scancode = interactive::scancode; using keycode = interactive::keycode; using mouse_button = interactive::mouse_button; - -using std::vector; +using common::lerp; constexpr int max_int = std::numeric_limits<int>::max(); @@ -57,13 +59,6 @@ auto trand_float(decltype(tiny_float_dist)::param_type range = {0, 1}) auto trand_float2() { return float2(trand_float(), trand_float()); }; -template <typename Number, typename Ratio = float> -constexpr -Number lerp(Number from, Number to, Ratio ratio) -{ - return from + (to - from) * ratio; -} - template <size_t FPS> class framerate { @@ -91,6 +86,16 @@ class Program using mouse_move_fun = std::function<void(float2, float2)>; using mouse_button_fun = std::function<void(float2, mouse_button)>; + using wave_fun = std::function<float(float ratio)>; + struct wave + { + wave_fun function; + duration total; + duration remaining; + explicit operator bool() { return bool(function); } + }; + std::array<wave, 32> waves = {}; + std::mutex dam; bool run = true; Program(const int argc, const char * const * const argv) : argc(argc), argv(argv) {} @@ -115,10 +120,48 @@ class Program bool running() { return run; } void end() { run = false; } + + auto request_wave(const wave& w, size_t canal) + { + return request_wave(wave{w}, canal); + } + + bool request_wave(wave&& wave, size_t canal) + { + std::scoped_lock lock(dam); + + assert(canal < waves.size()); + + auto& current = waves[canal]; + + if(current && current.remaining != 0s) + { + return false; // { false, waves[canal].remaining } + } + current = std::move(wave); + current.remaining = current.total; + return true; + } + + void require_wave(wave wave, size_t canal) + { + std::scoped_lock lock(dam); + + assert(canal < waves.size()); + auto& current = waves[canal]; + current = std::move(wave); + current.remaining = current.total; + } + + + friend int main(int argc, char const* argv[]); }; inline void process_events(Program&); +// TODO: move dependent stuff out of here and include this at the top +#include "motion.hpp" + void start(Program&); int main(int argc, char const* argv[]) try @@ -129,6 +172,44 @@ int main(int argc, char const* argv[]) try graphical::initializer graphics; sdlcore::initializer interactions(sdlcore::system_flag::event); + // TODO: make optional + musical::initializer music; + using namespace musical; + device_with_callback ocean + ( + basic_device_parameters{spec{} + .set_channels(spec::channels::mono) + .set_format(format::int8) + }, + [&program](auto& device, auto buffer) + { + std::fill(buffer.begin(), buffer.end(), device.silence()); + + auto lock = std::scoped_lock(program.dam); + const float tick = 1.f/device.obtained().get_frequency(); + for(auto&& wave : program.waves) + { + if(wave && wave.remaining.count() >= tick) + { + float remaining = wave.remaining.count() / wave.total.count(); + float progress = 1.f - remaining; + int remaining_ticks = std::min(buffer.size, int(wave.remaining.count() / tick)); + auto step = tick/wave.total.count(); + std::transform(buffer.begin(), buffer.begin() + remaining_ticks, buffer.begin(), [&](auto v) + { + + progress += step; + return v + (wave.function(progress) * 127); + }); + wave.remaining = (1.f - progress) * wave.total; + if(wave.remaining.count() < tick) + wave.remaining = 0s; + } + } + } + ); + ocean.play(); + gl_window::global.require<gl_window::attribute::major_version>(2); gl_window::global.request<gl_window::attribute::stencil>(8); gl_window win(program.name, program.size, gl_window::flags::borderless); diff --git a/tools/setup/init.sh b/tools/setup/init.sh index 7416ae516549aaa87ca91b3a69b5e155410dfc3c..e4dd73f22ab03ed12d5cf90489000b88f0786eb5 100755 --- a/tools/setup/init.sh +++ b/tools/setup/init.sh @@ -9,6 +9,7 @@ git clone https://notabug.org/namark/libsimple_geom git clone https://notabug.org/namark/libsimple_sdlcore git clone https://notabug.org/namark/libsimple_graphical git clone https://notabug.org/namark/libsimple_interactive +git clone https://notabug.org/namark/libsimple_musical git clone https://notabug.org/namark/nanovg cd .. ./tools/setup/install.sh "$@" diff --git a/tools/setup/install.sh b/tools/setup/install.sh index 6d603749181a51d5796d84e6d9e8afc48dd81f27..28b2a397065297a9e4aecd29dbde68566d5646ff 100755 --- a/tools/setup/install.sh +++ b/tools/setup/install.sh @@ -7,6 +7,7 @@ make PREFIX=../libsimple_geom ../libsimple_geom/include/make_templates/header_on make PREFIX=../libsimple_sdlcore ../libsimple_sdlcore/include/make_templates/static_lib make PREFIX=../libsimple_graphical ../libsimple_graphical/include/make_templates/static_lib make PREFIX=../libsimple_interactive ../libsimple_interactive/include/make_templates/static_lib +make PREFIX=../libsimple_musical ../libsimple_interactive/include/make_templates/static_lib make PREFIX=../nanovg ../nanovg/include/make_templates/static_lib cd .. @@ -15,6 +16,7 @@ make install PREFIX=../../ "$@" make install PREFIX=../libsimple_sdlcore "$@" make install PREFIX=../libsimple_graphical "$@" make install PREFIX=../libsimple_interactive "$@" +make install PREFIX=../libsimple_musical "$@" cd .. cd libsimple_geom @@ -22,12 +24,14 @@ make install PREFIX=../../ "$@" make install PREFIX=../libsimple_sdlcore "$@" make install PREFIX=../libsimple_graphical "$@" make install PREFIX=../libsimple_interactive "$@" +make install PREFIX=../libsimple_musical "$@" cd .. cd libsimple_sdlcore make install PREFIX=../../ "$@" make install PREFIX=../libsimple_graphical "$@" make install PREFIX=../libsimple_interactive "$@" +make install PREFIX=../libsimple_musical "$@" cd .. cd libsimple_graphical @@ -38,6 +42,10 @@ cd libsimple_interactive make install PREFIX=../../ "$@" cd .. +cd libsimple_musical +make install PREFIX=../../ "$@" +cd .. + cd nanovg make install GL=2 PREFIX=../../ "$@" cd .. diff --git a/tools/setup/update.sh b/tools/setup/update.sh index 91c5574097737a1b6f3bce6ffc68a578db6af112..c5d368c56f9ad263c179a3e9a1a6424e3c1004d1 100755 --- a/tools/setup/update.sh +++ b/tools/setup/update.sh @@ -26,6 +26,10 @@ cd libsimple_interactive git pull -r cd .. +cd libsimple_musical +git pull -r +cd .. + cd nanovg git pull -r cd ..