135 std::string filename;
136 std::vector<ossia::net::node_base*> roots;
137 std::chrono::steady_clock::time_point first_ts;
138 int64_t nots_index{};
141 boost::container::flat_map<int, ossia::net::parameter_base*> m_map;
142 boost::container::flat_map<int64_t, std::vector<ossia::value>> m_vec_ts;
143 std::vector<std::vector<ossia::value>> m_vec_no_ts;
146 bool first_is_timestamp =
false;
149 void setActive(
bool b)
158 void setLoops(
bool b) { loops = b; }
163 auto filename = QByteArray::fromStdString(this->filename).trimmed();
164 filename.replace(
"%t", QDateTime::currentDateTimeUtc().toString().toUtf8());
165 f.setFileName(filename);
166 if(filename.isEmpty())
172 f.open(QIODevice::ReadOnly);
181 for(
auto in : this->roots)
183 if(
auto p = in->get_parameter())
189 auto data = (
const char*)f.map(0, f.size());
193 r.parse_view({data, data + f.size()});
194 int columns = r.cols();
196 auto header = r.header();
198 boost::container::flat_map<std::string, ossia::net::parameter_base*> params;
200 for(
auto node : roots)
201 if(
auto p = node->get_parameter())
202 params[node->osc_address()] = p;
207 auto header_it = header.begin();
208 if(first_is_timestamp)
218 for(; header_it != header.end(); ++header_it)
220 auto addr = *header_it;
222 addr.read_raw_value(v);
223 if(
auto it = params.find(v); it != params.end())
225 m_map[i] = it->second;
233 if(first_is_timestamp)
236 r.parse_view({data, data + f.size()});
237 m_vec_ts.reserve(r.rows());
238 for(
const auto& row : r)
240 parse_row_with_timestamps(columns, row, v);
246 m_vec_no_ts.reserve(r.rows());
247 for(
const auto& row : r)
249 parse_row_no_timestamps(columns, row, v);
253 first_ts = std::chrono::steady_clock::now();
256 void parse_cell_impl(
257 const std::string& v, ossia::net::parameter_base& param, ossia::value& out)
261 std::optional<ossia::value> res;
262 if(v.starts_with(
'"') && v.ends_with(
'"'))
263 res = State::parseValue(std::string_view(v).substr(1, v.size() - 2));
265 res = State::parseValue(v);
269 out = std::move(*res);
270 if(
auto t = param.get_value_type(); out.get_type() != t)
272 ossia::convert(out, t);
279 parse_cell(
const auto& cell, std::string& v, std::vector<ossia::value>& vec,
int i)
281 if(
auto param = m_map[i])
285 parse_cell_impl(v, *param, vec[i]);
290 void parse_row_no_timestamps(
int columns,
auto& row, std::string& v)
292 auto& vec = this->m_vec_no_ts.emplace_back(columns);
295 for(
auto it = row.begin(); it != row.end(); ++it)
297 parse_cell(*it, v, vec, i);
302 void parse_row_with_timestamps(
int columns,
auto& row, std::string& v)
304 if(row.length() <= 1)
307 auto it = row.begin();
308 const auto& ts = *it;
312 auto tstamp = ossia::parse_strict<int64_t>(v);
316 auto& vec = this->m_vec_ts[*tstamp];
317 vec.resize(columns - 1);
320 for(++it; it != row.end(); ++it)
322 parse_cell(*it, v, vec, i);
329 if(first_is_timestamp)
334 using namespace std::chrono;
335 auto ts = duration_cast<milliseconds>(steady_clock::now() - first_ts).count();
337 ts %= m_vec_ts.rbegin()->first + 1;
342 if(m_vec_no_ts.empty())
345 using namespace std::chrono;
346 if(loops && nots_index >= std::ssize(m_vec_no_ts))
348 read_no_ts(nots_index++);
352 void read_no_ts(int64_t timestamp)
356 if(timestamp >= std::ssize(m_vec_no_ts))
358 auto it = m_vec_no_ts.begin() + timestamp;
359 if(it != m_vec_no_ts.end())
366 if(
auto p = m_map.find(i); p != m_map.end())
368 p->second->push_value(v);
376 void read_ts(int64_t timestamp)
378 auto it = m_vec_ts.lower_bound(timestamp);
379 if(it != m_vec_ts.end())
381 if(it != m_vec_ts.begin())
384 for(
auto& v : it->second)
388 if(
auto p = m_map.find(i); p != m_map.end())
390 p->second->push_value(v);
399 for(
auto& v : m_vec_ts.rbegin()->second)
403 if(
auto p = m_map.find(i); p != m_map.end())
405 p->second->push_value(v);
443 std::shared_ptr<recorder_thread> recorder;
444 std::shared_ptr<player_thread> player;
446 std::vector<ossia::net::node_base*> roots;
447 bool first_is_timestamp{};
452 swap(recorder->filename, path);
453 swap(recorder->roots, roots);
454 player->filename = recorder->filename;
455 player->roots = recorder->roots;
456 player->first_is_timestamp = first_is_timestamp;
457 recorder->first_is_timestamp = first_is_timestamp;