libpromeki 1.0.0-alpha
PROfessional MEdia toolKIt
 
Loading...
Searching...
No Matches
audiomarker.h
Go to the documentation of this file.
1
8#pragma once
9
10
11#include <promeki/config.h>
12#if PROMEKI_ENABLE_PROAV
13#include <cstdint>
14#include <functional>
15#include <promeki/namespace.h>
16#include <promeki/datastream.h>
17#include <promeki/enums.h>
18#include <promeki/error.h>
19#include <promeki/list.h>
20#include <promeki/result.h>
21#include <promeki/sharedptr.h>
22#include <promeki/string.h>
23
24PROMEKI_NAMESPACE_BEGIN
25
58class AudioMarker {
59 public:
61 AudioMarker() = default;
62
64 AudioMarker(int64_t offset, int64_t length, AudioMarkerType type)
65 : _offset(offset), _length(length), _type(type) {}
66
68 int64_t offset() const { return _offset; }
69
71 void setOffset(int64_t v) { _offset = v; }
72
74 int64_t length() const { return _length; }
75
77 void setLength(int64_t v) { _length = v; }
78
80 AudioMarkerType type() const { return _type; }
81
83 void setType(AudioMarkerType v) { _type = v; }
84
92 bool operator==(const AudioMarker &o) const {
93 return _offset == o._offset && _length == o._length && _type == o._type;
94 }
95 bool operator!=(const AudioMarker &o) const { return !(*this == o); }
96
105 String toString() const {
106 if (_length == 0) {
107 return _type.valueName() + "@" + String::number(_offset);
108 }
109 return _type.valueName() + "@" + String::number(_offset) + "+" + String::number(_length);
110 }
111
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('+');
130 String offsetStr;
131 String lengthStr;
132 if (plus == String::npos) {
133 offsetStr = tail;
134 lengthStr = String("0");
135 } else {
136 offsetStr = tail.substr(0, plus);
137 lengthStr = tail.substr(plus + 1);
138 }
139 Error ce;
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));
147 }
148
149 private:
150 int64_t _offset = 0;
151 int64_t _length = 0;
152 AudioMarkerType _type;
153};
154
190class AudioMarkerList {
191 PROMEKI_SHARED_FINAL(AudioMarkerList)
192 public:
193 PROMEKI_DATATYPE(AudioMarkerList, DataTypeAudioMarkerList, 1)
194
195
196 using Entry = AudioMarker;
197
199 using EntryList = ::promeki::List<Entry>;
200
202 using List = ::promeki::List<AudioMarkerList>;
203
205 using Ptr = SharedPtr<AudioMarkerList>;
206
208 using PtrList = ::promeki::List<Ptr>;
209
211 AudioMarkerList() = default;
212
214 explicit AudioMarkerList(EntryList entries) : _entries(std::move(entries)) {}
215
217 AudioMarkerList(std::initializer_list<Entry> il) : _entries(il) {}
218
220 const EntryList &entries() const { return _entries; }
221
223 void setEntries(EntryList entries) { _entries = std::move(entries); }
224
226 void append(const Entry &e) { _entries.pushToBack(e); }
227
229 void append(int64_t offset, int64_t length, AudioMarkerType type) {
230 _entries.pushToBack(Entry(offset, length, type));
231 }
232
234 size_t size() const { return _entries.size(); }
235
237 bool isEmpty() const { return _entries.isEmpty(); }
238
240 void clear() { _entries.clear(); }
241
250 int64_t totalLengthFor(AudioMarkerType type) const {
251 int64_t sum = 0;
252 for (const auto &e : _entries) {
253 if (e.type() == type) sum += e.length();
254 }
255 return sum;
256 }
257
259 size_t countFor(AudioMarkerType type) const {
260 size_t n = 0;
261 for (const auto &e : _entries) {
262 if (e.type() == type) ++n;
263 }
264 return n;
265 }
266
274 String toString() const {
275 if (_entries.isEmpty()) return String();
276 String out;
277 bool first = true;
278 for (const auto &e : _entries) {
279 if (!first) out += ", ";
280 out += e.toString();
281 first = false;
282 }
283 return out;
284 }
285
297 static Result<AudioMarkerList> fromString(const String &str) {
298 String trimmed = str.trim();
299 if (trimmed.isEmpty()) return makeResult(AudioMarkerList());
300 EntryList entries;
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));
308 }
309 return makeResult(AudioMarkerList(std::move(entries)));
310 }
311
313 bool operator==(const AudioMarkerList &o) const { return _entries == o._entries; }
314 bool operator!=(const AudioMarkerList &o) const { return !(*this == o); }
315
324 Error writeToStream(DataStream &s) const;
325
330 template <uint32_t V> static Result<AudioMarkerList> readFromStream(DataStream &s);
331
332 private:
333 EntryList _entries;
334};
335
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());
342 }
343 return s.status() == DataStream::Ok ? Error::Ok : s.toError();
344}
345
346template <>
347inline Result<AudioMarkerList> AudioMarkerList::readFromStream<1>(DataStream &s) {
348 uint32_t count = 0;
349 s >> count;
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) {
354 int64_t offset = 0;
355 int64_t length = 0;
356 int32_t typeValue = 0;
357 s >> offset >> length >> typeValue;
358 entries.pushToBack(AudioMarker(offset, length, AudioMarkerType(typeValue)));
359 }
360 if (s.status() != DataStream::Ok) return makeError<AudioMarkerList>(s.toError());
361 return makeResult(AudioMarkerList(std::move(entries)));
362}
363
364PROMEKI_NAMESPACE_END
365
366PROMEKI_FORMAT_VIA_TOSTRING(promeki::AudioMarker);
367PROMEKI_FORMAT_VIA_TOSTRING(promeki::AudioMarkerList);
368
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));
384 }
385};
386
393template <> struct std::hash<promeki::AudioMarkerList> {
394 size_t operator()(const promeki::AudioMarkerList &v) const noexcept {
395 size_t h = 0;
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));
399 }
400 return h;
401 }
402};
403
404#endif // PROMEKI_ENABLE_PROAV