From a354fd4dbf01e49987108e5143c4659f8e8827dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Wed, 8 May 2024 19:13:26 -0400 Subject: [PATCH] [examples] Add an audio particle generator --- .../AudioParticles/AudioParticles.cpp | 96 +++++++++++++++++++ .../AudioParticles/AudioParticles.hpp | 63 ++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 examples/Advanced/AudioParticles/AudioParticles.cpp create mode 100644 examples/Advanced/AudioParticles/AudioParticles.hpp diff --git a/examples/Advanced/AudioParticles/AudioParticles.cpp b/examples/Advanced/AudioParticles/AudioParticles.cpp new file mode 100644 index 00000000..a23c2b77 --- /dev/null +++ b/examples/Advanced/AudioParticles/AudioParticles.cpp @@ -0,0 +1,96 @@ +#include "AudioDice.hpp" + +#include + +#include +namespace Example +{ +void AudioDice::prepare(halp::setup info) +{ + this->rate = info.rate; + // Initialization, this method will be called with buffer size, etc. + m_sounds.clear(); + m_sounds.reserve(1000); + + score::for_all_files(inputs.folder.value, [this](std::string_view v) { + if(!v.ends_with(".wav")) + return; + + const QString& path = QString::fromUtf8(v.data(), v.size()); + + auto dec = Media::AudioDecoder::decode_synchronous(path, this->rate); + if(!dec) + return; + + m_sounds.push_back(std::move(dec->second)); + }); +} + +std::optional +frame_in_interval(int start_frame, int end_frame, double rate, double freq) +{ + if(rate <= 0.) + return std::nullopt; + + int64_t start_ns = 1e9 * start_frame / rate; + int64_t end_ns = 1e9 * end_frame / rate; + int64_t itv_ns = 1e9 / freq; + auto start_div = std::div(start_ns, itv_ns); + auto end_div = std::div(end_ns, itv_ns); + if(end_div.quot > start_div.quot) + { + return 1; + } + return std::nullopt; +} +void AudioDice::operator()(const halp::tick_musical& t) +{ + if(this->outputs.audio.channels != this->inputs.channels.value) + { + this->outputs.audio.request_channels(this->inputs.channels.value); + return; + } + + if(m_sounds.empty()) + return; + + // FIXME range is not respected + + if(inputs.frequency > 0.) + // Trigger new sounds + if(frame_in_interval( + t.position_in_frames, t.position_in_frames + t.frames, rate, + 1. / (1. - inputs.frequency))) + { + if((1. - inputs.density) < std::exponential_distribution()(m_rng)) + m_playheads.push_back(Playhead{ + 0, uint16_t(unsigned(rand()) % m_sounds.size()), + uint16_t(unsigned(rand()) % this->outputs.audio.channels)}); + } + + for(auto& playhead : m_playheads) + { + SCORE_ASSERT(m_sounds.size() > playhead.index); + auto& sound = m_sounds[playhead.index]; + int sound_frames = sound[0].size(); + + SCORE_ASSERT(outputs.audio.channels > playhead.channel); + auto channel = outputs.audio.channel(playhead.channel, t.frames); + + if(sound_frames - playhead.frame > t.frames) + for(int i = playhead.frame; i < playhead.frame + t.frames; i++) + channel[i - playhead.frame] += sound[0][i]; + else + { + for(int i = playhead.frame; i < sound_frames; i++) + channel[i - playhead.frame] += sound[0][i]; + } + playhead.frame += t.frames; + } + + ossia::remove_erase_if(m_playheads, [this](const auto& playhead) { + auto& sound = m_sounds[playhead.index]; + return playhead.frame >= sound[0].size(); + }); +} +} diff --git a/examples/Advanced/AudioParticles/AudioParticles.hpp b/examples/Advanced/AudioParticles/AudioParticles.hpp new file mode 100644 index 00000000..f5c8fadc --- /dev/null +++ b/examples/Advanced/AudioParticles/AudioParticles.hpp @@ -0,0 +1,63 @@ +#pragma once +#include + +#include + +#include +#include +#include +#include +#include +namespace Example +{ + +class AudioParticles +{ +public: + halp_meta(name, "Audio particles") + halp_meta(category, "Audio/Generator") + halp_meta(c_name, "avnd_audio_particles") + halp_meta(uuid, "e7f2b091-0de0-49cd-a581-d4087d901fbb") + + struct ins + { + halp::lineedit<"Folder", ""> folder; + halp::spinbox_i32<"Channels", halp::range{.min = 0., .max = 128, .init = 16}> + channels; + struct : halp::time_chooser<"Frequency", halp::range{0.001, 30., 0.2}> + { + using mapper = halp::log_mapper>; + } frequency; + halp::knob_f32<"Density", halp::range{0.001, 1., 0.7}> density; + } inputs; + + struct + { + halp::variable_audio_bus<"Output", double> audio; + } outputs; + + using setup = halp::setup; + void prepare(halp::setup info); + + // Do our processing for N samples + using tick = halp::tick_musical; + + // Defined in the .cpp + void operator()(const halp::tick_musical& t); + + std::vector m_sounds; + + struct Playhead + { + int frame{}; + uint16_t index{}; + uint16_t channel{}; + }; + + std::vector m_playheads; + double rate{}; + std::random_device m_rdev; + rnd::pcg m_rng{m_rdev}; +}; + +}