49 std::vector<ossia::net::node_base*> roots;
50 std::chrono::steady_clock::time_point first_ts;
51 fmt::memory_buffer buf;
53 bool first_is_timestamp =
false;
57 void setSeparator(Separator sep)
noexcept
62 case Separator::Colon:
63 this->separator =
',';
65 case Separator::Semicolon:
66 this->separator =
';';
69 this->separator =
'|';
74 void setActive(
bool b)
87 f.setFileName(filter_filename(this->filename, context));
88 if(f.fileName().isEmpty())
94 f.open(QIODevice::WriteOnly);
100 for(
auto in : this->roots)
102 if(
auto p = in->get_parameter())
104 f.write(&separator, 1);
105 f.write(QByteArray::fromStdString(p->get_node().osc_address()));
113 first_ts = std::chrono::steady_clock::now();
123 using namespace std::chrono;
125 = duration_cast<milliseconds>(steady_clock::now() - first_ts).count();
129 void write(int64_t timestamp)
131 f.write(QString::number(timestamp).toUtf8());
132 std::string separator_bufs =
"\"\n\r\t";
133 separator_bufs += this->separator;
134 for(
auto in : this->roots)
136 if(
auto p = in->get_parameter())
138 f.write(&separator, 1);
141 ossia::apply(ossia::detail::fmt_writer{buf}, p->value());
143 std::string_view sv(buf.data(), buf.data() + buf.size());
144 if(sv.find_first_of(separator_bufs) != std::string_view::npos)
148 f.write(buf.data(), buf.size());
153 f.write(buf.data(), buf.size());
170 std::string filename;
171 std::vector<ossia::net::node_base*> roots;
172 std::chrono::steady_clock::time_point first_ts;
173 int64_t nots_index{};
176 boost::container::flat_map<int, ossia::net::parameter_base*> m_map;
177 boost::container::flat_map<int64_t, std::vector<ossia::value>> m_vec_ts;
178 std::vector<std::vector<ossia::value>> m_vec_no_ts;
181 bool first_is_timestamp =
false;
185 void setSeparator(Separator sep)
noexcept
190 case Separator::Colon:
191 this->separator =
',';
193 case Separator::Semicolon:
194 this->separator =
';';
196 case Separator::Pipe:
197 this->separator =
'|';
202 void setActive(
bool b)
211 void setLoops(
bool b) { loops = b; }
213 template <
typename CsvReader>
214 void read(std::string_view data)
218 int columns = r.cols();
220 auto header = r.header();
222 boost::container::flat_map<std::string, ossia::net::parameter_base*> params;
224 for(
auto node : roots)
225 if(
auto p = node->get_parameter())
226 params[node->osc_address()] = p;
231 auto header_it = header.begin();
232 if(first_is_timestamp)
242 for(; header_it != header.end(); ++header_it)
244 auto addr = *header_it;
246 addr.read_raw_value(v);
247 if(
auto it = params.find(v); it != params.end())
249 m_map[i] = it->second;
257 if(first_is_timestamp)
262 m_vec_ts.reserve(r.rows());
263 for(
const auto& row : r)
265 parse_row_with_timestamps(columns, row, v);
271 m_vec_no_ts.reserve(r.rows());
272 for(
const auto& row : r)
274 parse_row_no_timestamps(columns, row, v);
278 first_ts = std::chrono::steady_clock::now();
285 f.setFileName(filter_filename(this->filename, context));
286 if(f.fileName().isEmpty())
292 if(!f.open(QIODevice::ReadOnly))
300 for(
auto in : this->roots)
302 if([[maybe_unused]]
auto p = in->get_parameter())
308 auto data = (
const char*)f.map(0, f.size());
315 read<csv2::Reader<>>({data, data + f.size()});
318 read<csv2::Reader<csv2::delimiter<
';'>>>({data, data + f.size()});
323 void parse_cell_impl(
324 const std::string& v, ossia::net::parameter_base& param, ossia::value& out)
328 std::optional<ossia::value> res;
329 if(v.starts_with(
'"') && v.ends_with(
'"'))
330 res = State::parseValue(std::string_view(v).substr(1, v.size() - 2));
332 res = State::parseValue(v);
336 out = std::move(*res);
337 if(
auto t = param.get_value_type(); out.get_type() != t)
339 ossia::convert(out, t);
346 parse_cell(
const auto& cell, std::string& v, std::vector<ossia::value>& vec,
int i)
348 if(
auto param = m_map[i])
352 parse_cell_impl(v, *param, vec[i]);
357 void parse_row_no_timestamps(
int columns,
auto& row, std::string& v)
359 auto& vec = this->m_vec_no_ts.emplace_back(columns);
362 for(
auto it = row.begin(); it != row.end(); ++it)
364 parse_cell(*it, v, vec, i);
369 void parse_row_with_timestamps(
int columns,
auto& row, std::string& v)
371 if(row.length() <= 1)
374 auto it = row.begin();
375 const auto& ts = *it;
379 auto tstamp = ossia::parse_strict<int64_t>(v);
383 auto& vec = this->m_vec_ts[*tstamp];
384 vec.resize(columns - 1);
387 for(++it; it != row.end(); ++it)
389 parse_cell(*it, v, vec, i);
396 if(first_is_timestamp)
401 using namespace std::chrono;
402 auto ts = duration_cast<milliseconds>(steady_clock::now() - first_ts).count();
404 ts %= m_vec_ts.rbegin()->first + 1;
409 if(m_vec_no_ts.empty())
412 using namespace std::chrono;
413 if(loops && nots_index >= std::ssize(m_vec_no_ts))
415 read_no_ts(nots_index++);
419 void read_no_ts(int64_t timestamp)
423 if(timestamp >= std::ssize(m_vec_no_ts))
425 auto it = m_vec_no_ts.begin() + timestamp;
426 if(it != m_vec_no_ts.end())
433 if(
auto p = m_map.find(i); p != m_map.end())
435 p->second->push_value(v);
443 void read_ts(int64_t timestamp)
445 auto it = m_vec_ts.lower_bound(timestamp);
446 if(it != m_vec_ts.end())
448 if(it != m_vec_ts.begin())
451 for(
auto& v : it->second)
455 if(
auto p = m_map.find(i); p != m_map.end())
457 p->second->push_value(v);
466 for(
auto& v : m_vec_ts.rbegin()->second)
470 if(
auto p = m_map.find(i); p != m_map.end())
472 p->second->push_value(v);
510 std::shared_ptr<recorder_thread> recorder;
511 std::shared_ptr<player_thread> player;
513 std::vector<ossia::net::node_base*> roots;
514 bool first_is_timestamp{};
515 Separator separator{};
520 swap(recorder->filename, path);
521 swap(recorder->roots, roots);
522 player->filename = recorder->filename;
523 player->roots = recorder->roots;
524 player->first_is_timestamp = first_is_timestamp;
525 player->setSeparator(separator);
527 recorder->first_is_timestamp = first_is_timestamp;
528 recorder->setSeparator(separator);