142 std::string filename;
143 std::vector<ossia::net::node_base*> roots;
144 std::chrono::steady_clock::time_point first_ts;
145 fmt::memory_buffer buf;
147 bool first_is_timestamp =
false;
151 void setSeparator(Separator sep)
noexcept
156 case Separator::Colon:
157 this->separator =
',';
159 case Separator::Semicolon:
160 this->separator =
';';
162 case Separator::Pipe:
163 this->separator =
'|';
168 void setActive(
bool b)
181 f.setFileName(filter_filename(this->filename, context));
182 if(f.fileName().isEmpty())
188 f.open(QIODevice::WriteOnly);
192 f.write(
"timestamp");
194 for(
auto in : this->roots)
196 if(
auto p = in->get_parameter())
198 f.write(&separator, 1);
199 f.write(QByteArray::fromStdString(p->get_node().osc_address()));
207 first_ts = std::chrono::steady_clock::now();
217 using namespace std::chrono;
219 = duration_cast<milliseconds>(steady_clock::now() - first_ts).count();
223 void write(int64_t timestamp)
225 f.write(QString::number(timestamp).toUtf8());
226 std::string separator_bufs =
"\"\n\r\t";
227 separator_bufs += this->separator;
228 for(
auto in : this->roots)
230 if(
auto p = in->get_parameter())
232 f.write(&separator, 1);
236 f.write(buf.data(), buf.size());
252 std::string filename;
253 std::vector<ossia::net::node_base*> roots;
254 std::chrono::steady_clock::time_point first_ts;
255 int64_t nots_index{};
258 boost::container::flat_map<int, ossia::net::parameter_base*> m_map;
259 boost::container::flat_map<int64_t, std::vector<ossia::value>> m_vec_ts;
260 std::vector<std::vector<ossia::value>> m_vec_no_ts;
263 bool first_is_timestamp =
false;
267 void setSeparator(Separator sep)
noexcept
272 case Separator::Colon:
273 this->separator =
',';
275 case Separator::Semicolon:
276 this->separator =
';';
278 case Separator::Pipe:
279 this->separator =
'|';
284 void setActive(
bool b)
293 void setLoops(
bool b) { loops = b; }
295 template <
typename CsvReader>
296 void read(std::string_view data)
300 int columns = r.cols();
302 auto header = r.header();
304 boost::container::flat_map<std::string, ossia::net::parameter_base*> params;
306 for(
auto node : roots)
307 if(
auto p = node->get_parameter())
308 params[node->osc_address()] = p;
313 auto header_it = header.begin();
314 if(first_is_timestamp)
324 for(; header_it != header.end(); ++header_it)
326 auto addr = *header_it;
328 addr.read_raw_value(v);
329 if(
auto it = params.find(v); it != params.end())
331 m_map[i] = it->second;
339 if(first_is_timestamp)
344 m_vec_ts.reserve(r.rows());
345 for(
const auto& row : r)
347 parse_row_with_timestamps(columns, row, v);
353 m_vec_no_ts.reserve(r.rows());
354 for(
const auto& row : r)
356 parse_row_no_timestamps(columns, row, v);
360 first_ts = std::chrono::steady_clock::now();
367 f.setFileName(filter_filename(this->filename, context));
368 if(f.fileName().isEmpty())
374 if(!f.open(QIODevice::ReadOnly))
382 for(
auto in : this->roots)
384 if([[maybe_unused]]
auto p = in->get_parameter())
390 auto data = (
const char*)f.map(0, f.size());
397 read<csv2::Reader<>>({data, data + f.size()});
400 read<csv2::Reader<csv2::delimiter<
';'>>>({data, data + f.size()});
405 void parse_cell_impl(
406 const std::string& v, ossia::net::parameter_base& param, ossia::value& out)
410 std::optional<ossia::value> res;
411 if(v.starts_with(
'"') && v.ends_with(
'"'))
412 res = State::parseValue(std::string_view(v).substr(1, v.size() - 2));
414 res = State::parseValue(v);
418 out = std::move(*res);
419 if(
auto t = param.get_value_type(); out.get_type() != t)
421 ossia::convert(out, t);
428 parse_cell(
const auto& cell, std::string& v, std::vector<ossia::value>& vec,
int i)
430 if(
auto param = m_map[i])
434 parse_cell_impl(v, *param, vec[i]);
439 void parse_row_no_timestamps(
int columns,
auto& row, std::string& v)
441 auto& vec = this->m_vec_no_ts.emplace_back(columns);
444 for(
auto it = row.begin(); it != row.end(); ++it)
446 parse_cell(*it, v, vec, i);
451 void parse_row_with_timestamps(
int columns,
auto& row, std::string& v)
453 if(row.length() <= 1)
456 auto it = row.begin();
457 const auto& ts = *it;
461 auto tstamp = ossia::parse_strict<int64_t>(v);
465 auto& vec = this->m_vec_ts[*tstamp];
466 vec.resize(columns - 1);
469 for(++it; it != row.end(); ++it)
471 parse_cell(*it, v, vec, i);
478 if(first_is_timestamp)
483 using namespace std::chrono;
484 auto ts = duration_cast<milliseconds>(steady_clock::now() - first_ts).count();
486 ts %= m_vec_ts.rbegin()->first + 1;
491 if(m_vec_no_ts.empty())
494 using namespace std::chrono;
495 if(loops && nots_index >= std::ssize(m_vec_no_ts))
497 read_no_ts(nots_index++);
501 void read_no_ts(int64_t timestamp)
505 if(timestamp >= std::ssize(m_vec_no_ts))
507 auto it = m_vec_no_ts.begin() + timestamp;
508 if(it != m_vec_no_ts.end())
515 if(
auto p = m_map.find(i); p != m_map.end())
517 p->second->push_value(v);
525 void read_ts(int64_t timestamp)
527 auto it = m_vec_ts.lower_bound(timestamp);
528 if(it != m_vec_ts.end())
530 if(it != m_vec_ts.begin())
533 for(
auto& v : it->second)
537 if(
auto p = m_map.find(i); p != m_map.end())
539 p->second->push_value(v);
548 for(
auto& v : m_vec_ts.rbegin()->second)
552 if(
auto p = m_map.find(i); p != m_map.end())
554 p->second->push_value(v);
592 std::shared_ptr<recorder_thread> recorder;
593 std::shared_ptr<player_thread> player;
595 std::vector<ossia::net::node_base*> roots;
596 bool first_is_timestamp{};
597 Separator separator{};
602 swap(recorder->filename, path);
603 swap(recorder->roots, roots);
604 player->filename = recorder->filename;
605 player->roots = recorder->roots;
606 player->first_is_timestamp = first_is_timestamp;
607 player->setSeparator(separator);
609 recorder->first_is_timestamp = first_is_timestamp;
610 recorder->setSeparator(separator);