Add fibonacci music box example
This commit is contained in:
parent
63008d62ff
commit
d1975dd917
1 changed files with 167 additions and 0 deletions
167
examples/fibonacci_music_box.cpp
Normal file
167
examples/fibonacci_music_box.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
#include <psemek/audio/engine.hpp>
|
||||
#include <psemek/audio/wave/silence.hpp>
|
||||
#include <psemek/audio/wave/sine.hpp>
|
||||
#include <psemek/audio/wave/sawtooth.hpp>
|
||||
#include <psemek/audio/wave/square.hpp>
|
||||
#include <psemek/audio/wave/triangle.hpp>
|
||||
#include <psemek/audio/wave/karplus_strong.hpp>
|
||||
#include <psemek/audio/effect/volume.hpp>
|
||||
#include <psemek/audio/effect/fade_in.hpp>
|
||||
#include <psemek/audio/effect/fade_out.hpp>
|
||||
#include <psemek/audio/effect/compressor.hpp>
|
||||
#include <psemek/audio/effect/pause.hpp>
|
||||
#include <psemek/audio/effect/truncate.hpp>
|
||||
#include <psemek/audio/combine/concat.hpp>
|
||||
#include <psemek/audio/combine/loop.hpp>
|
||||
#include <psemek/audio/effect/pitch.hpp>
|
||||
#include <psemek/audio/effect/distortion.hpp>
|
||||
#include <psemek/audio/combine/duplicate.hpp>
|
||||
#include <psemek/audio/combine/stereo.hpp>
|
||||
#include <psemek/audio/combine/mixer.hpp>
|
||||
#include <psemek/audio/midi.hpp>
|
||||
#include <psemek/app/application_base.hpp>
|
||||
#include <psemek/app/default_application_factory.hpp>
|
||||
#include <psemek/log/log.hpp>
|
||||
#include <psemek/util/array.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
// Inspired by https://www.youtube.com/watch?v=_aIf4WUCNZU
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
std::vector<std::vector<geom::point<int, 2>>> fibonacci_cycles(int n)
|
||||
{
|
||||
util::array<int, 2> cycle({n, n}, -1);
|
||||
|
||||
std::vector<std::vector<geom::point<int, 2>>> 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();
|
||||
|
||||
geom::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 == geom::point{i, j})
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<int> major_scale()
|
||||
{
|
||||
return {0, 2, 4, 5, 7, 9, 11};
|
||||
}
|
||||
|
||||
std::vector<int> minor_scale()
|
||||
{
|
||||
return {0, 2, 3, 5, 7, 8, 10};
|
||||
}
|
||||
|
||||
std::shared_ptr<audio::stream> 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<audio::track> generate(std::vector<int> const & scale, std::vector<geom::point<int, 2>> 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<audio::engine> engine_;
|
||||
audio::mixer_ptr mixer_;
|
||||
std::vector<audio::stream_ptr> cycles_;
|
||||
std::vector<audio::channel_ptr> channels_;
|
||||
};
|
||||
|
||||
namespace psemek::app
|
||||
{
|
||||
|
||||
std::unique_ptr<application::factory> make_application_factory()
|
||||
{
|
||||
return default_application_factory<fibonacci_music_box_app>({.name = "Fibonacci music box"});
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue