1use sva_formula::Codomain;
4use sva_samples::{
5 BLOCK, Buffer, Detail, Dropped, Extent, Grid, Label, PSYCHOACOUSTIC_V1, Rule, Source, block_end,
6};
7
8use sva_formula::{FOURIER_DUAL_RULES_VERSION, Hash};
9
10use super::Stored;
11use super::stored::{Cut, Header, Laid, Samples};
12
13pub const STORE_FORMAT: u32 = 46;
15
16fn entry_tag() -> Vec<u8> {
19 [
20 &b"SVAh"[..],
21 &STORE_FORMAT.to_le_bytes(),
22 &FOURIER_DUAL_RULES_VERSION.to_le_bytes(),
23 ]
24 .concat()
25}
26
27const STAGED_RUN: &[u8; 4] = b"SVAc";
28
29fn chunks(start: i64, len: usize) -> Option<usize> {
33 if len == 0 {
34 return Some(0);
35 }
36 let last = start.checked_add(i64::try_from(len).ok()? - 1)?;
37 usize::try_from(last.div_euclid(BLOCK) - start.div_euclid(BLOCK) + 1).ok()
38}
39
40fn before(run: &Laid, k: usize) -> usize {
42 let edge = match k {
43 0 => run.start,
44 k => (run.start.div_euclid(BLOCK) + k as i64).saturating_mul(BLOCK),
45 };
46 (edge.min(run.start + run.len as i64) - run.start) as usize
47}
48
49pub(crate) fn chunks_over(run: &Laid, over: Extent) -> (usize, usize) {
51 let first = run.start.div_euclid(BLOCK);
52 let at = |n: i64| (n.div_euclid(BLOCK) - first) as usize;
53 (at(over.start), at(over.end - 1) + 1)
54}
55
56pub(crate) fn entry(head: &Header, runs: &[Buffer]) -> Vec<u8> {
59 let mut laid = Vec::new();
60 let mut body = Vec::new();
61 for run in runs {
62 let mut sums = Vec::new();
63 let at = body.len() as u64;
64 let mut from = 0;
65 while from < run.len() {
66 let to = (block_end(run.start + from as i64) - run.start).min(run.len() as i64);
67 let to = to as usize;
68 let chunk = body.len();
69 for plane in &run.planes {
70 for sample in &plane[from..to] {
71 word(&mut body, sample.to_bits());
72 }
73 }
74 sums.push(checksum(&body[chunk..]));
75 from = to;
76 }
77 laid.push(Laid {
78 rate: run.rate,
79 start: run.start,
80 width: run.width(),
81 len: run.len(),
82 at,
83 sums,
84 });
85 }
86 let head = sealed(header(head, &laid));
87 let mut out = Vec::with_capacity(8 + head.len() + body.len());
88 word(&mut out, head.len() as u64);
89 out.extend_from_slice(&head);
90 out.extend_from_slice(&body);
91 out
92}
93
94fn header(head: &Header, laid: &[Laid]) -> Vec<u8> {
95 let stored = head.stored();
96 let mut out = entry_tag();
97 word(&mut out, stored.key.0);
98 word(&mut out, stored.key.1);
99 word(&mut out, stored.identity.0);
100 word(&mut out, stored.identity.1);
101 labelled(&mut out, &stored.label);
102 out.push(stored.width);
103 out.push(match stored.codomain {
104 Codomain::Real => 0,
105 Codomain::Complex => 1,
106 });
107 maybe(&mut out, stored.rate.map(u64::from));
108 word(&mut out, u64::from(stored.grid.rate));
109 out.extend_from_slice(&stored.grid.a.to_le_bytes());
110 out.extend_from_slice(&stored.grid.d.to_le_bytes());
111 word(&mut out, stored.support.start as u64);
112 word(&mut out, stored.support.end as u64);
113 word(&mut out, stored.moved.to_bits());
114 out.push(u8::from(stored.readable));
115 out.push(u8::from(stored.sampled));
116 match stored.cut {
117 None => out.push(0),
118 Some(cut) => {
119 out.push(1);
120 word(&mut out, cut.silence_threshold_dbfs.to_bits());
121 maybe(
122 &mut out,
123 cut.treated_as_silent_from_sample.map(|at| at as u64),
124 );
125 }
126 }
127 match head.samples() {
128 &Samples::Of { key, by } => {
129 out.push(1);
130 word(&mut out, key.0);
131 word(&mut out, key.1);
132 word(&mut out, by as u64);
133 }
134 _ => out.push(0),
135 }
136 word(&mut out, laid.len() as u64);
137 for run in laid {
138 word(&mut out, u64::from(run.rate));
139 word(&mut out, run.start as u64);
140 word(&mut out, run.width as u64);
141 word(&mut out, run.len as u64);
142 for sum in &run.sums {
143 word(&mut out, *sum);
144 }
145 }
146 out
147}
148
149pub(crate) fn head_len(first: &[u8]) -> Option<usize> {
151 let len = u64::from_le_bytes(first.get(..8)?.try_into().ok()?);
152 8usize.checked_add(usize::try_from(len).ok()?)
153}
154
155pub(crate) fn read_head(bytes: &[u8], file: Hash) -> Option<(Header, u64)> {
158 let span = head_len(bytes)?;
159 let mut r = Reader(opened(bytes.get(8..span)?, &entry_tag())?);
160 let key = Hash(r.word()?, r.word()?);
161 let identity = Hash(r.word()?, r.word()?);
162 let label = r.label()?;
163 let width = r.byte()?;
164 let codomain = match r.byte()? {
165 0 => Codomain::Real,
166 1 => Codomain::Complex,
167 _ => return None,
168 };
169 let rate = match r.maybe()? {
170 Some(rate) => Some(u32::try_from(rate).ok()?),
171 None => None,
172 };
173 let grid = Grid {
174 rate: u32::try_from(r.word()?).ok()?,
175 a: r.wide()? as i128,
176 d: r.wide()? as i128,
177 };
178 let (start, end) = (r.word()? as i64, r.word()? as i64);
179 let support = (start <= end).then(|| Extent::new(start, end))?;
180 let moved = f64::from_bits(r.word()?);
181 let readable = r.flag()?;
182 let sampled = r.flag()?;
183 let cut = match r.byte()? {
184 0 => None,
185 1 => Some(Cut {
186 silence_threshold_dbfs: f64::from_bits(r.word()?),
187 treated_as_silent_from_sample: r.maybe()?.map(|at| at as i64),
188 }),
189 _ => return None,
190 };
191 let of = match r.byte()? {
192 0 => None,
193 1 => Some(Samples::Of {
194 key: Hash(r.word()?, r.word()?),
195 by: r.word()? as i64,
196 }),
197 _ => return None,
198 };
199 let count = r.word()? as usize;
200 let mut runs = Vec::new();
201 let mut at = span as u64;
202 for _ in 0..count.min(r.0.len()) {
203 let rate = u32::try_from(r.word()?).ok()?;
204 let start = r.word()? as i64;
205 let (width, len) = (r.word()? as usize, r.word()? as usize);
206 let count = chunks(start, len)?;
207 let sums = (0..count.min(r.0.len()))
208 .map(|_| r.word())
209 .collect::<Option<Vec<u64>>>()?;
210 if sums.len() != count {
211 return None;
212 }
213 runs.push(Laid {
214 rate,
215 start,
216 width,
217 len,
218 at,
219 sums,
220 });
221 at = at.checked_add((width.checked_mul(len)?.checked_mul(8)?) as u64)?;
222 }
223 let samples = match (of, runs.is_empty()) {
224 (Some(of), true) => of,
225 (Some(_), false) => return None,
226 (None, true) => Samples::None,
227 (None, false) => Samples::Entry {
228 file,
229 runs,
230 shift: 0,
231 },
232 };
233 let stored = Stored {
234 key,
235 identity,
236 label,
237 width,
238 codomain,
239 rate,
240 grid,
241 support,
242 moved,
243 readable,
244 sampled,
245 cut,
246 held: Vec::new(),
247 };
248 let whole = runs_whole(count, &samples) && r.0.is_empty();
249 whole.then(|| (Header::new(stored, samples), at))
250}
251
252fn runs_whole(count: usize, samples: &Samples) -> bool {
253 match samples {
254 Samples::Entry { runs, .. } => runs.len() == count,
255 _ => count == 0,
256 }
257}
258
259pub(crate) fn span_of(run: &Laid, from: usize, to: usize) -> (u64, u64) {
261 let bytes = |k: usize| (before(run, k) * run.width * 8) as u64;
262 (run.at + bytes(from), bytes(to) - bytes(from))
263}
264
265pub(crate) fn read_chunks(bytes: &[u8], run: &Laid, from: usize, to: usize) -> Option<Buffer> {
267 let first = before(run, from);
268 let len = before(run, to).checked_sub(first)?;
269 if bytes.len() != len * run.width * 8 {
270 return None;
271 }
272 let mut planes = vec![Vec::with_capacity(len); run.width];
273 let mut at = 0;
274 for (k, sum) in run.sums[from..to].iter().enumerate() {
275 let n = before(run, from + k + 1) - before(run, from + k);
276 let chunk = &bytes[at..at + n * run.width * 8];
277 if checksum(chunk) != *sum {
278 return None;
279 }
280 for (c, plane) in planes.iter_mut().enumerate() {
281 let own = &chunk[c * n * 8..(c + 1) * n * 8];
282 plane.extend(
283 own.chunks_exact(8).map(|b| {
284 f64::from_bits(u64::from_le_bytes(b.try_into().expect("eight bytes")))
285 }),
286 );
287 }
288 at += chunk.len();
289 }
290 let mut out = Buffer::of_planes(run.rate, planes);
291 out.start = run.start + first as i64;
292 Some(out)
293}
294
295pub(crate) fn read_runs(bytes: &[u8], key: Hash) -> Option<Vec<Buffer>> {
297 let (head, _) = read_head(bytes, key)?;
298 let Samples::Entry { runs, .. } = head.samples() else {
299 return Some(Vec::new());
300 };
301 let read = |run: &Laid| {
302 let n = chunks(run.start, run.len)?;
303 let (at, len) = span_of(run, 0, n);
304 let span = bytes.get(at as usize..(at + len) as usize)?;
305 read_chunks(span, run, 0, n)
306 };
307 runs.iter().map(read).collect()
308}
309
310pub(crate) fn chunk(samples: &Buffer) -> Vec<u8> {
311 let mut out = STAGED_RUN.to_vec();
312 segments(&mut out, std::slice::from_ref(samples));
313 sealed(out)
314}
315
316pub(crate) fn read_chunk(bytes: &[u8]) -> Option<Buffer> {
317 let mut r = Reader(opened(bytes, STAGED_RUN)?);
318 let mut parts = r.segments()?;
319 (r.0.is_empty() && parts.len() == 1).then(|| parts.remove(0))
320}
321
322fn sealed(mut out: Vec<u8>) -> Vec<u8> {
323 let sum = checksum(&out);
324 word(&mut out, sum);
325 out
326}
327
328fn opened<'b>(bytes: &'b [u8], magic: &[u8]) -> Option<&'b [u8]> {
330 let body = bytes.len().checked_sub(8)?;
331 let (body, sum) = bytes.split_at(body);
332 (checksum(body).to_le_bytes() == sum && body.starts_with(magic)).then(|| &body[magic.len()..])
333}
334
335fn segments(out: &mut Vec<u8>, parts: &[Buffer]) {
336 word(out, parts.len() as u64);
337 for part in parts {
338 word(out, u64::from(part.rate));
339 word(out, part.start as u64);
340 word(out, part.width() as u64);
341 word(out, part.len() as u64);
342 for plane in &part.planes {
343 for sample in plane {
344 word(out, sample.to_bits());
345 }
346 }
347 }
348}
349
350fn checksum(bytes: &[u8]) -> u64 {
351 bytes.iter().fold(0xcbf2_9ce4_8422_2325, |h, b| {
352 (h ^ u64::from(*b)).wrapping_mul(0x0100_0000_01b3)
353 })
354}
355
356fn word(out: &mut Vec<u8>, w: u64) {
357 out.extend_from_slice(&w.to_le_bytes());
358}
359
360fn float(out: &mut Vec<u8>, v: Option<f64>) {
361 maybe(out, v.map(f64::to_bits));
362}
363
364fn maybe(out: &mut Vec<u8>, w: Option<u64>) {
365 match w {
366 None => out.push(0),
367 Some(w) => {
368 out.push(1);
369 word(out, w);
370 }
371 }
372}
373
374fn text(out: &mut Vec<u8>, s: &str) {
375 word(out, s.len() as u64);
376 out.extend_from_slice(s.as_bytes());
377}
378
379fn labelled(out: &mut Vec<u8>, label: &Label) {
380 out.push(match label.source {
381 Source::Exact => 0,
382 Source::Measured => 1,
383 });
384 text(out, label.profile);
385 word(out, u64::from(label.rate));
386 detailed(out, &label.detail);
387 float(out, label.moved);
388 match &label.cutting_below_silence_threshold {
389 None => out.push(0),
390 Some(cutting) => {
391 out.push(1);
392 word(out, cutting.silence_threshold_dbfs.to_bits());
393 word(out, cutting.treated_as_silent_from_sample.len() as u64);
394 for (node, at) in &cutting.treated_as_silent_from_sample {
395 text(out, node);
396 word(out, *at as u64);
397 }
398 }
399 }
400}
401
402fn detailed(out: &mut Vec<u8>, detail: &Detail) {
403 let rule = |out: &mut Vec<u8>, rule: &Rule| text(out, rule.as_str());
404 match detail {
405 Detail::Lines {
406 rule: r,
407 summed,
408 dropped,
409 dropped_more,
410 tail_db,
411 } => {
412 out.push(0);
413 rule(out, r);
414 word(out, *summed as u64);
415 word(out, dropped.len() as u64);
416 for d in dropped {
417 word(out, d.hz.to_bits());
418 word(out, d.db.to_bits());
419 }
420 word(out, *dropped_more as u64);
421 float(out, *tail_db);
422 }
423 Detail::Continuous { rule: r } => {
424 out.push(1);
425 rule(out, r);
426 }
427 Detail::Cropped { rule: r, tail_db } => {
428 out.push(2);
429 rule(out, r);
430 float(out, *tail_db);
431 }
432 Detail::Point {
433 rule: r,
434 alias_db,
435 tail_db,
436 } => {
437 out.push(3);
438 rule(out, r);
439 float(out, *alias_db);
440 float(out, *tail_db);
441 }
442 Detail::Roundtrip { rule: r, edited } => {
443 out.push(5);
444 rule(out, r);
445 out.push(u8::from(*edited));
446 }
447 Detail::Reading { rule: r } => {
448 out.push(6);
449 rule(out, r);
450 }
451 Detail::Added { parts } => {
452 out.push(7);
453 word(out, parts.len() as u64);
454 for part in parts {
455 detailed(out, part);
456 }
457 }
458 }
459}
460
461struct Reader<'b>(&'b [u8]);
462
463impl Reader<'_> {
464 fn take(&mut self, n: usize) -> Option<&[u8]> {
465 if self.0.len() < n {
466 return None;
467 }
468 let (head, rest) = self.0.split_at(n);
469 self.0 = rest;
470 Some(head)
471 }
472
473 fn byte(&mut self) -> Option<u8> {
474 self.take(1).map(|b| b[0])
475 }
476
477 fn flag(&mut self) -> Option<bool> {
478 match self.byte()? {
479 0 => Some(false),
480 1 => Some(true),
481 _ => None,
482 }
483 }
484
485 fn word(&mut self) -> Option<u64> {
486 Some(u64::from_le_bytes(self.take(8)?.try_into().ok()?))
487 }
488
489 fn segments(&mut self) -> Option<Vec<Buffer>> {
490 let count = self.word()? as usize;
491 let mut parts = Vec::new();
492 for _ in 0..count.min(self.0.len()) {
493 let rate = u32::try_from(self.word()?).ok()?;
494 let start = self.word()? as i64;
495 let (width, len) = (self.word()? as usize, self.word()? as usize);
496 if width.saturating_mul(len).saturating_mul(8) > self.0.len() {
497 return None;
498 }
499 let planes = (0..width)
500 .map(|_| (0..len).map(|_| self.word().map(f64::from_bits)).collect())
501 .collect::<Option<Vec<Vec<f64>>>>()?;
502 let mut part = Buffer::of_planes(rate, planes);
503 part.start = start;
504 parts.push(part);
505 }
506 (parts.len() == count).then_some(parts)
507 }
508
509 fn wide(&mut self) -> Option<u128> {
510 Some(u128::from_le_bytes(self.take(16)?.try_into().ok()?))
511 }
512
513 fn maybe(&mut self) -> Option<Option<u64>> {
514 match self.byte()? {
515 0 => Some(None),
516 1 => Some(Some(self.word()?)),
517 _ => None,
518 }
519 }
520
521 fn float(&mut self) -> Option<Option<f64>> {
522 Some(self.maybe()?.map(f64::from_bits))
523 }
524
525 fn text(&mut self) -> Option<&str> {
526 let len = self.word()? as usize;
527 std::str::from_utf8(self.take(len)?).ok()
528 }
529
530 fn rule(&mut self) -> Option<Rule> {
531 Rule::named(self.text()?)
532 }
533
534 fn label(&mut self) -> Option<Label> {
535 let source = match self.byte()? {
536 0 => Source::Exact,
537 1 => Source::Measured,
538 _ => return None,
539 };
540 let profile = match self.text()? {
541 name if name == PSYCHOACOUSTIC_V1.name => PSYCHOACOUSTIC_V1.name,
542 _ => return None,
543 };
544 let rate = u32::try_from(self.word()?).ok()?;
545 let detail = self.detail()?;
546 let moved = self.float()?;
547 let cutting_below_silence_threshold = match self.byte()? {
548 0 => None,
549 1 => {
550 let silence_threshold_dbfs = f64::from_bits(self.word()?);
551 let count = usize::try_from(self.word()?).ok()?;
552 let treated_as_silent_from_sample = (0..count)
553 .map(|_| Some((self.text()?.to_string(), self.word()? as i64)))
554 .collect::<Option<Vec<_>>>()?;
555 Some(sva_samples::CuttingBelowSilenceThreshold {
556 silence_threshold_dbfs,
557 treated_as_silent_from_sample,
558 })
559 }
560 _ => return None,
561 };
562 Some(Label {
563 source,
564 profile,
565 rate,
566 detail,
567 moved,
568 cutting_below_silence_threshold,
569 })
570 }
571
572 fn detail(&mut self) -> Option<Detail> {
573 Some(match self.byte()? {
574 0 => {
575 let rule = self.rule()?;
576 let summed = self.word()? as usize;
577 let count = self.word()? as usize;
578 let dropped = (0..count.min(self.0.len()))
579 .map(|_| {
580 Some(Dropped {
581 hz: f64::from_bits(self.word()?),
582 db: f64::from_bits(self.word()?),
583 })
584 })
585 .collect::<Option<Vec<_>>>()?;
586 if dropped.len() != count {
587 return None;
588 }
589 Detail::Lines {
590 rule,
591 summed,
592 dropped,
593 dropped_more: self.word()? as usize,
594 tail_db: self.float()?,
595 }
596 }
597 1 => Detail::Continuous { rule: self.rule()? },
598 2 => Detail::Cropped {
599 rule: self.rule()?,
600 tail_db: self.float()?,
601 },
602 3 => Detail::Point {
603 rule: self.rule()?,
604 alias_db: self.float()?,
605 tail_db: self.float()?,
606 },
607 5 => Detail::Roundtrip {
608 rule: self.rule()?,
609 edited: match self.byte()? {
610 0 => false,
611 1 => true,
612 _ => return None,
613 },
614 },
615 6 => Detail::Reading { rule: self.rule()? },
616 7 => {
617 let count = self.word()? as usize;
618 let parts = (0..count.min(self.0.len()))
619 .map(|_| self.detail())
620 .collect::<Option<Vec<_>>>()?;
621 if parts.len() != count {
622 return None;
623 }
624 Detail::Added { parts }
625 }
626 _ => return None,
627 })
628 }
629}