22#include <pulp/format/detail/locale_independent_float.hpp>
35#include <system_error>
56 std::istringstream in(text);
58 while (std::getline(in, line)) {
59 auto hash = line.find(
'#');
60 if (hash != std::string::npos) line = line.substr(0, hash);
67 std::string_view rest{line};
68 auto next_token = [&rest]() -> std::string_view {
69 std::size_t b = rest.find_first_not_of(
" \t\r");
70 if (b == std::string_view::npos) { rest = {};
return {}; }
71 std::size_t e = rest.find_first_of(
" \t\r", b);
72 std::string_view tok = (e == std::string_view::npos)
74 : rest.substr(b, e - b);
75 rest = (e == std::string_view::npos) ? std::string_view{}
80 std::string_view sample_tok = next_token();
81 std::string_view op = next_token();
82 if (sample_tok.empty() || op.empty())
continue;
88 auto parse_full = [](std::string_view tok,
auto& out) {
89 if (tok.empty())
return false;
90 using T = std::remove_reference_t<
decltype(out)>;
91 if constexpr (std::is_floating_point_v<T>) {
98 const auto r = detail::parse_double_c_locale(tok, parsed);
99 if (r.consumed != tok.size() || r.range_error)
return false;
100 out =
static_cast<T
>(parsed);
103 const char* first = tok.data();
104 const char* last = first + tok.size();
105 auto r = std::from_chars(first, last, out);
106 return r.ec == std::errc{} && r.ptr == last;
111 if (!parse_full(sample_tok, e.sample))
continue;
116 if (!parse_full(next_token(), e.id))
continue;
117 if (!parse_full(next_token(), e.value))
continue;
118 }
else if (op ==
"note_on") {
121 if (!parse_full(next_token(), e.id))
continue;
123 std::string_view vel = next_token();
124 if (!vel.empty() && !parse_full(vel, e.value))
continue;
125 }
else if (op ==
"note_off") {
127 if (!parse_full(next_token(), e.id))
continue;
143 auto append_num = [&](
auto v) {
144 auto r = std::to_chars(buf, buf +
sizeof(buf), v);
145 out.append(buf, r.ptr);
147 for (
const auto& e :
events) {
148 append_num(e.sample);
173 std::ifstream f(path);
174 std::stringstream ss; ss << f.rdbuf();
175 return parse(ss.str());
191 std::size_t total_samples, std::size_t block_size = 512) {
193 out.
left.resize(total_samples, 0.0f);
194 out.
right.resize(total_samples, 0.0f);
196 std::vector<float> obuf((std::size_t)2 * block_size);
197 const float* in_planes[2] = {
nullptr,
nullptr};
201 for (std::size_t s = 0; s < total_samples; s += block_size) {
202 const std::size_t n = std::min(block_size, total_samples - s);
203 float* planes[2] = {obuf.data(), obuf.data() + n};
208 for (; ev < ps.
events.size() && ps.
events[ev].sample < (std::int64_t)(s + n); ++ev) {
209 const auto& e = ps.
events[ev];
210 if (e.sample < (std::int64_t)s) { }
213 host.state().set_value(e.id, e.value);
217 me.sample_offset = (std::int32_t)std::max<std::int64_t>(0, e.sample - (std::int64_t)s);
223 me.sample_offset = (std::int32_t)std::max<std::int64_t>(0, e.sample - (std::int64_t)s);
230 host.process(out_view, in_view, midi_in, midi_out);
232 std::copy(planes[0], planes[0] + n, out.
left.begin() + s);
233 std::copy(planes[1], planes[1] + n, out.
right.begin() + s);
239inline double segment_rms(
const std::vector<float>& v, std::size_t a, std::size_t b) {
240 double acc = 0; std::size_t n = 0;
241 for (std::size_t i = a; i < b && i < v.size(); ++i) { acc += (double)v[i] * v[i]; ++n; }
242 return n ? std::sqrt(acc / n) : 0.0;
247 std::ofstream o(path, std::ios::binary);
248 if (!o)
return false;
249 const std::uint32_t ch = 2, bits = 32, n = (std::uint32_t)r.frames();
250 const std::uint32_t data_bytes = n * ch * (bits / 8);
251 auto u32 = [&](std::uint32_t v) { o.write((
const char*)&v, 4); };
252 auto u16 = [&](std::uint16_t v) { o.write((
const char*)&v, 2); };
253 o.write(
"RIFF", 4); u32(36 + data_bytes); o.write(
"WAVE", 4);
254 o.write(
"fmt ", 4); u32(16); u16(3); u16((std::uint16_t)ch); u32(sample_rate);
255 u32(sample_rate * ch * (bits / 8)); u16((std::uint16_t)(ch * (bits / 8))); u16((std::uint16_t)bits);
256 o.write(
"data", 4); u32(data_bytes);
257 for (std::uint32_t i = 0; i < n; ++i) { o.write((
const char*)&r.left[i], 4); o.write((
const char*)&r.right[i], 4); }
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Collection of timestamped MIDI events within a single audio buffer period.
Definition buffer.hpp:31
void sort()
Sort events by sample_offset for sample-accurate processing.
Definition buffer.hpp:177
bool add(const MidiEvent &event)
Append a MIDI event to the buffer.
Definition buffer.hpp:98
Headless processing adapter for programmatic plugin use without UI or audio device.
Bounded event contracts shared by host ingress and graph-authored nodes.
Definition anticipation_eligibility.hpp:9
static MidiEvent note_on(uint8_t channel, uint8_t note, uint8_t velocity)
Create a Note On event.
Definition message.hpp:46
static MidiEvent note_off(uint8_t channel, uint8_t note, uint8_t velocity=0)
Create a Note Off event.
Definition message.hpp:54