11#include <promeki/config.h>
12#if PROMEKI_ENABLE_PROAV
24PROMEKI_NAMESPACE_BEGIN
61 AudioMarker() =
default;
64 AudioMarker(int64_t offset, int64_t length, AudioMarkerType type)
65 : _offset(offset), _length(length), _type(type) {}
68 int64_t offset()
const {
return _offset; }
71 void setOffset(int64_t v) { _offset = v; }
74 int64_t length()
const {
return _length; }
77 void setLength(int64_t v) { _length = v; }
80 AudioMarkerType type()
const {
return _type; }
83 void setType(AudioMarkerType v) { _type = v; }
92 bool operator==(
const AudioMarker &o)
const {
93 return _offset == o._offset && _length == o._length && _type == o._type;
95 bool operator!=(
const AudioMarker &o)
const {
return !(*
this == o); }
105 String toString()
const {
107 return _type.valueName() +
"@" + String::number(_offset);
109 return _type.valueName() +
"@" + String::number(_offset) +
"+" + String::number(_length);
124 static Result<AudioMarker> fromString(
const String &str) {
125 size_t at = str.find(
'@');
126 if (at == String::npos)
return makeError<AudioMarker>(Error::Invalid);
127 String typeName = str.substr(0, at);
128 String tail = str.substr(at + 1);
129 size_t plus = tail.find(
'+');
132 if (plus == String::npos) {
134 lengthStr = String(
"0");
136 offsetStr = tail.substr(0, plus);
137 lengthStr = tail.substr(plus + 1);
140 int64_t offset = offsetStr.to<int64_t>(&ce);
141 if (ce.isError())
return makeError<AudioMarker>(Error::Invalid);
142 int64_t length = lengthStr.to<int64_t>(&ce);
143 if (ce.isError())
return makeError<AudioMarker>(Error::Invalid);
144 AudioMarkerType t(typeName);
145 if (!t.hasListedValue())
return makeError<AudioMarker>(Error::Invalid);
146 return makeResult(AudioMarker(offset, length, t));
152 AudioMarkerType _type;
190class AudioMarkerList {
191 PROMEKI_SHARED_FINAL(AudioMarkerList)
193 PROMEKI_DATATYPE(AudioMarkerList, DataTypeAudioMarkerList, 1)
196 using Entry = AudioMarker;
199 using EntryList = ::promeki::List<Entry>;
202 using List = ::promeki::List<AudioMarkerList>;
205 using Ptr = SharedPtr<AudioMarkerList>;
208 using PtrList = ::promeki::List<Ptr>;
211 AudioMarkerList() = default;
214 explicit AudioMarkerList(EntryList entries) : _entries(std::move(entries)) {}
217 AudioMarkerList(std::initializer_list<Entry> il) : _entries(il) {}
220 const EntryList &entries()
const {
return _entries; }
223 void setEntries(EntryList entries) { _entries = std::move(entries); }
226 void append(
const Entry &e) { _entries.pushToBack(e); }
229 void append(int64_t offset, int64_t length, AudioMarkerType type) {
230 _entries.pushToBack(Entry(offset, length, type));
234 size_t size()
const {
return _entries.size(); }
237 bool isEmpty()
const {
return _entries.isEmpty(); }
240 void clear() { _entries.clear(); }
250 int64_t totalLengthFor(AudioMarkerType type)
const {
252 for (
const auto &e : _entries) {
253 if (e.type() == type) sum += e.length();
259 size_t countFor(AudioMarkerType type)
const {
261 for (
const auto &e : _entries) {
262 if (e.type() == type) ++n;
274 String toString()
const {
275 if (_entries.isEmpty())
return String();
278 for (
const auto &e : _entries) {
279 if (!first) out +=
", ";
297 static Result<AudioMarkerList> fromString(
const String &str) {
298 String trimmed = str.trim();
299 if (trimmed.isEmpty())
return makeResult(AudioMarkerList());
301 StringList parts = trimmed.split(
",");
302 for (
const auto &p : parts) {
303 String item = p.trim();
304 if (item.isEmpty())
continue;
305 auto r = AudioMarker::fromString(item);
306 if (error(r).isError())
return makeError<AudioMarkerList>(Error::Invalid);
307 entries.pushToBack(value(r));
309 return makeResult(AudioMarkerList(std::move(entries)));
313 bool operator==(
const AudioMarkerList &o)
const {
return _entries == o._entries; }
314 bool operator!=(
const AudioMarkerList &o)
const {
return !(*
this == o); }
324 Error writeToStream(DataStream &s)
const;
330 template <u
int32_t V>
static Result<AudioMarkerList> readFromStream(DataStream &s);
336inline Error AudioMarkerList::writeToStream(DataStream &s)
const {
337 s << static_cast<uint32_t>(size());
338 for (
const auto &e : entries()) {
339 s << static_cast<int64_t>(e.offset());
340 s << static_cast<int64_t>(e.length());
341 s << static_cast<int32_t>(e.type().value());
343 return s.status() == DataStream::Ok ? Error::Ok : s.toError();
347inline Result<AudioMarkerList> AudioMarkerList::readFromStream<1>(DataStream &s) {
350 if (s.status() != DataStream::Ok)
return makeError<AudioMarkerList>(s.toError());
351 AudioMarkerList::EntryList entries;
352 entries.reserve(count);
353 for (uint32_t i = 0; i < count && s.status() == DataStream::Ok; ++i) {
356 int32_t typeValue = 0;
357 s >> offset >> length >> typeValue;
358 entries.pushToBack(AudioMarker(offset, length, AudioMarkerType(typeValue)));
360 if (s.status() != DataStream::Ok)
return makeError<AudioMarkerList>(s.toError());
361 return makeResult(AudioMarkerList(std::move(entries)));
366PROMEKI_FORMAT_VIA_TOSTRING(promeki::AudioMarker);
367PROMEKI_FORMAT_VIA_TOSTRING(promeki::AudioMarkerList);
377template <>
struct std::hash<promeki::AudioMarker> {
378 size_t operator()(
const promeki::AudioMarker &m)
const noexcept {
379 size_t h = std::hash<int64_t>()(m.offset());
380 size_t l = std::hash<int64_t>()(m.length());
381 size_t t = std::hash<int32_t>()(m.type().value());
382 size_t combined = l ^ (t + 0x9e3779b97f4a7c15ULL + (l << 6) + (l >> 2));
383 return h ^ (combined + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2));
393template <>
struct std::hash<promeki::AudioMarkerList> {
394 size_t operator()(
const promeki::AudioMarkerList &v)
const noexcept {
396 for (
const auto &e : v.entries()) {
397 size_t s = std::hash<promeki::AudioMarker>()(e);
398 h = h ^ (s + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2));