Loading...
Searching...
No Matches
ValueTake.hpp
1#pragma once
2#include <Fx/Looper/TakeWriter.hpp>
3#include <Fx/Looper/ValueLoop.hpp>
4
5#include <ossia/network/value/value.hpp>
6
7#include <fmt/format.h>
8
9#include <algorithm>
10#include <array>
11#include <atomic>
12#include <cmath>
13#include <filesystem>
14#include <fstream>
15#include <memory>
16#include <string>
17
18namespace Nodes::Looper
19{
52{
53 static constexpr int max_items = value_loop::max_events;
54 struct item
55 {
56 int64_t pos;
57 value_slot value;
58 bool impulse;
59 };
60 std::array<item, max_items> items{};
61 int count{};
62 int64_t length{};
63 double rate{48000.};
64 double tempo{120.};
65 int meter_numerator{4};
66 int meter_denominator{4};
69 std::atomic_bool in_use{};
70
71 void clear() noexcept
72 {
73 for(int i = 0; i < count; i++)
74 items[i].value.reset();
75 count = 0;
76 }
77 void add(int64_t pos, const value_slot& v, bool impulse)
78 {
79 if(count >= max_items)
80 return;
81 auto& it = items[count++];
82 it.pos = pos;
83 it.value = v;
84 it.impulse = impulse;
85 }
86
89 static constexpr int max_components = 32;
90
91 static bool is_number(const ossia::value& v) noexcept
92 {
93 const auto t = v.get_type();
94 return t == ossia::val_type::FLOAT || t == ossia::val_type::INT
95 || t == ossia::val_type::BOOL;
96 }
97
101 static int numbers_of(const ossia::value& v) noexcept
102 {
103 switch(v.get_type())
104 {
105 case ossia::val_type::FLOAT:
106 case ossia::val_type::INT:
107 case ossia::val_type::BOOL:
108 return 1;
109 case ossia::val_type::VEC2F:
110 return 2;
111 case ossia::val_type::VEC3F:
112 return 3;
113 case ossia::val_type::VEC4F:
114 return 4;
115 case ossia::val_type::LIST: {
116 const auto& l = v.get<std::vector<ossia::value>>();
117 for(const auto& e : l)
118 if(!is_number(e))
119 return -1;
120 return int(l.size());
121 }
122 default:
123 return -1;
124 }
125 }
127 static double number(const ossia::value& v, int component) noexcept
128 {
129 switch(v.get_type())
130 {
131 case ossia::val_type::LIST: {
132 const auto& l = v.get<std::vector<ossia::value>>();
133 return component >= 0 && component < int(l.size()) ? number(l[component], 0) : 0.;
134 }
135 case ossia::val_type::FLOAT:
136 return v.get<float>();
137 case ossia::val_type::INT:
138 return v.get<int>();
139 case ossia::val_type::BOOL:
140 return v.get<bool>() ? 1. : 0.;
141 case ossia::val_type::VEC2F:
142 return v.get<ossia::vec2f>()[std::clamp(component, 0, 1)];
143 case ossia::val_type::VEC3F:
144 return v.get<ossia::vec3f>()[std::clamp(component, 0, 2)];
145 case ossia::val_type::VEC4F:
146 return v.get<ossia::vec4f>()[std::clamp(component, 0, 3)];
147 default:
148 return 0.;
149 }
150 }
151
155 int components() const noexcept
156 {
157 int n = 0;
158 for(int i = 0; i < count; i++)
159 {
160 if(items[i].impulse)
161 return 0;
162 const int k = numbers_of(items[i].value.get());
163 if(k < 0)
164 return 0;
165 n = std::max(n, k);
166 }
167 return n <= max_components ? n : 0;
168 }
169
170 std::string to_json() const
171 {
172 std::string out;
173 out.reserve(256 + std::size_t(count) * 32);
174 out += "{\n \"format\": \"score-value-take\",\n \"version\": 1,\n \"rate\": ";
175 write_number(out, rate);
176 out += ",\n \"frames\": ";
177 out += std::to_string(length);
178 out += ",\n \"tempo\": ";
179 write_number(out, tempo);
180 out += ",\n \"meter\": [";
181 out += std::to_string(meter_numerator);
182 out += ", ";
183 out += std::to_string(meter_denominator);
184 out += "],\n \"components\": ";
185 const int n = components();
186 out += std::to_string(n);
187 if(n > 0)
188 {
189 std::array<double, max_components> lo, hi;
190 std::array<bool, max_components> seen{};
191 lo.fill(0.);
192 hi.fill(0.);
193 for(int i = 0; i < count; i++)
194 {
195 const auto& v = items[i].value.get();
196 const int k = numbers_of(v);
197 for(int c = 0; c < k; c++)
198 {
199 const double x = number(v, c);
200 lo[c] = !seen[c] ? x : std::min(lo[c], x);
201 hi[c] = !seen[c] ? x : std::max(hi[c], x);
202 seen[c] = true;
203 }
204 }
205 for(const auto& [name, arr] : {std::pair{",\n \"min\": [", &lo}, {"],\n \"max\": [", &hi}})
206 {
207 out += name;
208 for(int c = 0; c < n; c++)
209 {
210 if(c > 0)
211 out += ", ";
212 write_number(out, (*arr)[c]);
213 }
214 }
215 out += "]";
216 }
217 out += ",\n \"events\": [";
218 for(int i = 0; i < count; i++)
219 {
220 out += i == 0 ? "\n [" : ",\n [";
221 out += std::to_string(items[i].pos);
222 if(items[i].impulse)
223 out += ", \"impulse\"";
224 else
225 {
226 out += ", ";
227 write_typed(out, items[i].value.get(), true);
228 }
229 out += "]";
230 }
231 out += count > 0 ? "\n ]\n}\n" : "]\n}\n";
232 return out;
233 }
234
235 static void write_number(std::string& out, double x)
236 {
237 if(!std::isfinite(x))
238 {
239 out += "0";
240 return;
241 }
242 fmt::format_to(std::back_inserter(out), "{}", x);
243 }
244 static void write_number(std::string& out, float x)
245 {
246 if(!std::isfinite(x))
247 {
248 out += "0";
249 return;
250 }
251 fmt::format_to(std::back_inserter(out), "{}", x);
252 }
253 static void write_string(std::string& out, std::string_view s)
254 {
255 out += '"';
256 for(char ch : s)
257 {
258 switch(ch)
259 {
260 case '"':
261 out += "\\\"";
262 break;
263 case '\\':
264 out += "\\\\";
265 break;
266 case '\n':
267 out += "\\n";
268 break;
269 case '\r':
270 out += "\\r";
271 break;
272 case '\t':
273 out += "\\t";
274 break;
275 default:
276 if(static_cast<unsigned char>(ch) < 0x20)
277 {
278 char buf[8];
279 std::snprintf(buf, sizeof(buf), "\\u%04x", ch);
280 out += buf;
281 }
282 else
283 out += ch;
284 }
285 }
286 out += '"';
287 }
288
291 static void write_typed(std::string& out, const ossia::value& v, bool flat)
292 {
293 if(!flat)
294 out += '[';
295 auto vec = [&](const char* name, const auto& a) {
296 out += '"';
297 out += name;
298 out += "\", [";
299 for(std::size_t i = 0; i < a.size(); i++)
300 {
301 if(i > 0)
302 out += ", ";
303 write_number(out, a[i]);
304 }
305 out += ']';
306 };
307 switch(v.get_type())
308 {
309 case ossia::val_type::FLOAT:
310 out += "\"float\", ";
311 write_number(out, v.get<float>());
312 break;
313 case ossia::val_type::INT:
314 out += "\"int\", ";
315 out += std::to_string(v.get<int>());
316 break;
317 case ossia::val_type::BOOL:
318 out += v.get<bool>() ? "\"bool\", true" : "\"bool\", false";
319 break;
320 case ossia::val_type::STRING:
321 out += "\"string\", ";
322 write_string(out, v.get<std::string>());
323 break;
324 case ossia::val_type::VEC2F:
325 vec("vec2f", v.get<ossia::vec2f>());
326 break;
327 case ossia::val_type::VEC3F:
328 vec("vec3f", v.get<ossia::vec3f>());
329 break;
330 case ossia::val_type::VEC4F:
331 vec("vec4f", v.get<ossia::vec4f>());
332 break;
333 case ossia::val_type::LIST: {
334 out += "\"list\", [";
335 bool first = true;
336 for(const auto& e : v.get<std::vector<ossia::value>>())
337 {
338 if(!first)
339 out += ", ";
340 first = false;
341 write_typed(out, e, false);
342 }
343 out += ']';
344 break;
345 }
346 case ossia::val_type::MAP: {
347 out += "\"map\", {";
348 bool first = true;
349 for(const auto& [k, e] : v.get<ossia::value_map_type>())
350 {
351 if(!first)
352 out += ", ";
353 first = false;
354 write_string(out, k);
355 out += ": ";
356 write_typed(out, e, false);
357 }
358 out += '}';
359 break;
360 }
361 case ossia::val_type::IMPULSE:
362 default:
363 out += "\"impulse\"";
364 break;
365 }
366 if(!flat)
367 out += ']';
368 }
369};
370
375 const value_loop& snap, int item, held_timeline& tl, held_scratch& scratch,
376 value_export& e)
377{
378 e.clear();
379 tl.update(snap, 0, scratch, item, item, item < 0, true);
380 // The held values, then the impulses, by position.
381 for(int i = 0; i < tl.count; i++)
382 if(tl.points[i].event >= 0)
383 e.add(tl.points[i].pos, snap.events[tl.points[i].event].value, false);
384 for(int i = 0; i < snap.event_count; i++)
385 {
386 const auto& ev = snap.events[i];
387 if(!ev.impulse || !snap.heard(ev, item < 0) || (item >= 0 && ev.pass != item))
388 continue;
389 const int64_t period = snap.period_for(ev.pass);
390 for(int64_t q = ev.pos % period; q < snap.length; q += period)
391 e.add(q, ev.value, true);
392 }
393 std::sort(e.items.begin(), e.items.begin() + e.count, [](const auto& a, const auto& b) {
394 return a.pos != b.pos ? a.pos < b.pos : a.impulse < b.impulse;
395 });
396 e.length = snap.length;
397}
398
403{
404 value_loop loop;
405 double rate{48000.};
406 double tempo{120.};
407 int meter_numerator{4};
408 int meter_denominator{4};
409 stop_take_notice notice;
410 std::atomic_bool in_use{};
411};
412
414{
415 std::shared_ptr<take_writer> writer;
416 std::shared_ptr<value_export> data;
419 std::shared_ptr<value_stop_take> stop;
420 int slot{};
422 int number{};
423 int layer{-1};
424 bool alone{};
430 bool nothing{};
431
432 bool write(const value_export& e)
433 {
434 const std::string bytes = e.to_json();
435 const auto& path = writer->next_path(slot, number, layer);
436 std::ofstream f{
437 std::filesystem::path{std::u8string_view{
438 reinterpret_cast<const char8_t*>(path.data()), path.size()}},
439 std::ios::binary | std::ios::trunc};
440 f.write(bytes.data(), std::streamsize(bytes.size()));
441 return bool(f);
442 }
443
444 bool run()
445 {
446 if(stop)
447 return run_stop();
448 if(!data)
449 return false;
450 struct release
451 {
452 value_export& e;
453 ~release() { e.in_use = false; }
454 } done{*data};
455 if(!writer || data->length <= 0)
456 return false;
457 components = data->components();
458 nothing = data->count == 0;
459 return write(*data);
460 }
461
464 bool run_stop()
465 {
466 auto& s = *stop;
467 struct release
468 {
470 ~release()
471 {
472 s.loop.clear();
473 s.in_use = false;
474 }
475 } done{s};
476 bool ok = writer && s.loop.length > 0;
477 bool placeable = true;
478 if(ok)
479 {
480 auto e = std::make_unique<value_export>();
481 auto tl = std::make_unique<held_timeline>();
482 auto scratch = std::make_unique<held_scratch>();
483 e->rate = s.rate;
484 e->tempo = s.tempo;
485 e->meter_numerator = s.meter_numerator;
486 e->meter_denominator = s.meter_denominator;
487 number = 0;
488 for(layer = -1; ok && layer < s.loop.count; layer++)
489 {
490 fill_value_export(s.loop, layer, *tl, *scratch, *e);
491 // A take goes in the timeline when each of its files that holds
492 // something does: a layer whose values are all replaced holds none.
493 if(layer < 0 || e->count > 0)
494 placeable = placeable && e->components() > 0;
495 ok = write(*e);
496 }
497 e->clear();
498 }
499 components = placeable ? 1 : 0;
500 nothing = false;
501 if(writer)
502 s.notice.written(*writer, slot, ok, s.loop.count, placeable);
503 return ok;
504 }
505};
506}
Definition ValueLoop.hpp:26
Definition AudioLayers.hpp:14
void fill_value_export(const value_loop &snap, int item, held_timeline &tl, held_scratch &scratch, value_export &e)
Definition ValueTake.hpp:374
Definition ValueLoop.hpp:415
Definition ValueLoop.hpp:500
void update(const value_loop &loop, uint16_t key, held_scratch &scratch, int first=-1, int last=-1, bool own_mute=true, bool force=false) noexcept
Definition ValueLoop.hpp:533
int64_t length
Frames in the loop.
Definition PassStack.hpp:79
Definition TakeWriter.hpp:153
Definition ValueTake.hpp:55
Definition ValueTake.hpp:52
static double number(const ossia::value &v, int component) noexcept
Number component of a value, which it has.
Definition ValueTake.hpp:127
static constexpr int max_components
Definition ValueTake.hpp:89
static void write_typed(std::string &out, const ossia::value &v, bool flat)
Definition ValueTake.hpp:291
int components() const noexcept
Definition ValueTake.hpp:155
static int numbers_of(const ossia::value &v) noexcept
Definition ValueTake.hpp:101
std::atomic_bool in_use
Definition ValueTake.hpp:69
Definition ValueLoop.hpp:117
bool heard(const T &e, bool own_mute=true) const noexcept
Definition ValueLoop.hpp:214
Definition ValueTake.hpp:403
Definition ValueTake.hpp:414
int number
Number of the take, 0 for a new one; -1 for the mix, else the pass.
Definition ValueTake.hpp:422
int components
Definition ValueTake.hpp:429
std::shared_ptr< value_stop_take > stop
Definition ValueTake.hpp:419
bool run_stop()
Definition ValueTake.hpp:464