|
| 1 | +#include "AudioParticles.hpp" |
| 2 | + |
| 3 | +#include <score/tools/RecursiveWatch.hpp> |
| 4 | + |
| 5 | +#include <QDirIterator> |
| 6 | +namespace ao |
| 7 | +{ |
| 8 | +void AudioParticles::prepare(halp::setup info) |
| 9 | +{ |
| 10 | + this->rate = info.rate; |
| 11 | + // Initialization, this method will be called with buffer size, etc. |
| 12 | + m_sounds.clear(); |
| 13 | + m_sounds.reserve(1000); |
| 14 | + |
| 15 | + score::for_all_files(inputs.folder.value, [this](std::string_view v) { |
| 16 | + if(!v.ends_with(".wav")) |
| 17 | + return; |
| 18 | + |
| 19 | + const QString& path = QString::fromUtf8(v.data(), v.size()); |
| 20 | + |
| 21 | + auto dec = Media::AudioDecoder::decode_synchronous(path, this->rate); |
| 22 | + if(!dec) |
| 23 | + return; |
| 24 | + |
| 25 | + m_sounds.push_back(std::move(dec->second)); |
| 26 | + }); |
| 27 | +} |
| 28 | + |
| 29 | +std::optional<int> |
| 30 | +frame_in_interval(int start_frame, int end_frame, double rate, double freq) |
| 31 | +{ |
| 32 | + if(rate <= 0.) |
| 33 | + return std::nullopt; |
| 34 | + |
| 35 | + int64_t start_ns = 1e9 * start_frame / rate; |
| 36 | + int64_t end_ns = 1e9 * end_frame / rate; |
| 37 | + int64_t itv_ns = 1e9 / freq; |
| 38 | + auto start_div = std::div(start_ns, itv_ns); |
| 39 | + auto end_div = std::div(end_ns, itv_ns); |
| 40 | + if(end_div.quot > start_div.quot) |
| 41 | + { |
| 42 | + return 1; |
| 43 | + } |
| 44 | + return std::nullopt; |
| 45 | +} |
| 46 | +void AudioParticles::operator()(const halp::tick_musical& t) |
| 47 | +{ |
| 48 | + if(this->outputs.audio.channels != this->inputs.channels.value) |
| 49 | + { |
| 50 | + this->outputs.audio.request_channels(this->inputs.channels.value); |
| 51 | + return; |
| 52 | + } |
| 53 | + |
| 54 | + if(m_sounds.empty()) |
| 55 | + return; |
| 56 | + |
| 57 | + // FIXME range is not respected |
| 58 | + |
| 59 | + if(inputs.frequency > 0.) |
| 60 | + // Trigger new sounds |
| 61 | + if(frame_in_interval( |
| 62 | + t.position_in_frames, t.position_in_frames + t.frames, rate, |
| 63 | + 1. / (1. - inputs.frequency))) |
| 64 | + { |
| 65 | + if((1. - inputs.density) < std::exponential_distribution<float>()(m_rng)) |
| 66 | + m_playheads.push_back(Playhead{ |
| 67 | + 0, uint16_t(unsigned(rand()) % m_sounds.size()), |
| 68 | + uint16_t(unsigned(rand()) % this->outputs.audio.channels)}); |
| 69 | + } |
| 70 | + |
| 71 | + for(auto& playhead : m_playheads) |
| 72 | + { |
| 73 | + SCORE_ASSERT(m_sounds.size() > playhead.index); |
| 74 | + auto& sound = m_sounds[playhead.index]; |
| 75 | + int sound_frames = sound[0].size(); |
| 76 | + |
| 77 | + SCORE_ASSERT(outputs.audio.channels > playhead.channel); |
| 78 | + auto channel = outputs.audio.channel(playhead.channel, t.frames); |
| 79 | + |
| 80 | + if(sound_frames - playhead.frame > t.frames) |
| 81 | + for(int i = playhead.frame; i < playhead.frame + t.frames; i++) |
| 82 | + channel[i - playhead.frame] += sound[0][i]; |
| 83 | + else |
| 84 | + { |
| 85 | + for(int i = playhead.frame; i < sound_frames; i++) |
| 86 | + channel[i - playhead.frame] += sound[0][i]; |
| 87 | + } |
| 88 | + playhead.frame += t.frames; |
| 89 | + } |
| 90 | + |
| 91 | + ossia::remove_erase_if(m_playheads, [this](const auto& playhead) { |
| 92 | + auto& sound = m_sounds[playhead.index]; |
| 93 | + return playhead.frame >= sound[0].size(); |
| 94 | + }); |
| 95 | +} |
| 96 | +} |
0 commit comments