#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Inspired by https://www.youtube.com/watch?v=_aIf4WUCNZU using namespace psemek; std::vector>> fibonacci_cycles(int n) { util::ndarray cycle({n, n}, -1); std::vector>> result; for (int i = 0; i < n; ++i) { for (int j = 0; j < n; ++j) { if (cycle(i, j) != -1) continue; int cycle_id = result.size(); auto & current = result.emplace_back(); math::point p{i, j}; while (true) { current.push_back(p); cycle(p[0], p[1]) = cycle_id; p = {p[1], (p[0] + p[1]) % n}; if (p == math::point{i, j}) break; } } } return result; } std::vector major_scale() { return {0, 2, 4, 5, 7, 9, 11}; } std::vector minor_scale() { return {0, 2, 3, 5, 7, 8, 10}; } std::shared_ptr note(int i, float duration) { return audio::fade_in(audio::fade_out(audio::karplus_strong(audio::midi_frequency(69 + i)), duration), duration / 16.f); } std::shared_ptr generate(std::vector const & scale, std::vector> const & cycle, float speed) { auto mixer = audio::make_mixer(); for (int i = 0; i < cycle.size(); ++i) mixer->add(audio::concat({audio::truncate(audio::silence(), speed * i), note(scale[cycle[i][0] % scale.size()] + 12 * (cycle[i][0] / scale.size()), speed)})); return audio::record(mixer, speed * cycle.size()); } struct fibonacci_music_box_app : app::application_base { fibonacci_music_box_app(options const &, context const &) { auto cycles = fibonacci_cycles(16); auto scale = minor_scale(); float speed = 0.25f; log::info() << "Found cycles of length:"; for (int i = 0; i < cycles.size(); ++i) { log::info() << " #" << i << ": " << cycles[i].size(); cycles_.push_back(audio::loop(generate(scale, cycles[i], speed))); } channels_.resize(cycles_.size()); engine_ = audio::make_engine(); mixer_ = audio::make_mixer(); auto compressor = audio::compressor(mixer_, audio::from_db(-2.f), 0.95f, 0.002f, 1.f, audio::from_db(1.f)); engine_->output()->stream(compressor); } void on_event(app::key_event const & event) override { app::application_base::on_event(event); if (event.down && event.key >= app::keycode::NUM_1 && event.key <= app::keycode::NUM_0) { int i; if (event.key == app::keycode::NUM_0) i = 0; else i = 1 + (int(event.key) - int(app::keycode::NUM_1)); log::info() << "Pressed " << i; if (i < channels_.size()) { if (channels_[i]) { channels_[i]->stop(); channels_[i] = nullptr; } else { channels_[i] = mixer_->add(cycles_[i]); } } } } void update() override {} void present() override {} private: std::unique_ptr engine_; audio::mixer_ptr mixer_; std::vector cycles_; std::vector channels_; }; namespace psemek::app { std::unique_ptr make_application_factory() { return default_application_factory({.name = "Fibonacci music box"}); } }