Skip to content

Commit 7a40ef0

Browse files
committed
[examples] Add an audio particle generator
1 parent 7763a02 commit 7a40ef0

2 files changed

Lines changed: 157 additions & 0 deletions

File tree

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#pragma once
2+
#include <Media/AudioDecoder.hpp>
3+
#include <halp/audio.hpp>
4+
#include <halp/controls.hpp>
5+
#include <halp/mappers.hpp>
6+
#include <halp/meta.hpp>
7+
#include <rnd/random.hpp>
8+
9+
#include <QFile>
10+
namespace ao
11+
{
12+
class AudioParticles
13+
{
14+
public:
15+
halp_meta(name, "Audio particles")
16+
halp_meta(category, "Audio/Generator")
17+
halp_meta(c_name, "avnd_audio_particles")
18+
halp_meta(uuid, "e7f2b091-0de0-49cd-a581-d4087d901fbb")
19+
20+
struct ins
21+
{
22+
halp::lineedit<"Folder", ""> folder;
23+
halp::spinbox_i32<"Channels", halp::range{.min = 0., .max = 128, .init = 16}>
24+
channels;
25+
struct : halp::time_chooser<"Frequency", halp::range{0.001, 30., 0.2}>
26+
{
27+
using mapper = halp::log_mapper<std::ratio<95, 100>>;
28+
} frequency;
29+
halp::knob_f32<"Density", halp::range{0.001, 1., 0.7}> density;
30+
} inputs;
31+
32+
struct
33+
{
34+
halp::variable_audio_bus<"Output", double> audio;
35+
} outputs;
36+
37+
using setup = halp::setup;
38+
void prepare(halp::setup info);
39+
40+
// Do our processing for N samples
41+
using tick = halp::tick_musical;
42+
43+
// Defined in the .cpp
44+
void operator()(const halp::tick_musical& t);
45+
46+
std::vector<Media::audio_array> m_sounds;
47+
48+
struct Playhead
49+
{
50+
int frame{};
51+
uint16_t index{};
52+
uint16_t channel{};
53+
};
54+
55+
std::vector<Playhead> m_playheads;
56+
double rate{};
57+
std::random_device m_rdev;
58+
rnd::pcg m_rng{m_rdev};
59+
};
60+
61+
}

0 commit comments

Comments
 (0)