use crate::core::{FloFile, FloResult, TocEntry};
use crate::reader::Reader;
use alloc::format;
use alloc::vec;
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct SeekResult {
pub frame_index: u32,
pub byte_offset: u64,
pub timestamp_ms: u32,
pub sample_offset: u32,
pub next_timestamp_ms: u32,
}
pub fn get_toc(flo_data: &[u8]) -> FloResult<Vec<TocEntry>> {
let reader = Reader::new();
let file = reader.read(flo_data)?;
Ok(file.toc)
}
pub fn decode_frame_at(flo_data: &[u8], frame_index: u32) -> FloResult<Vec<f32>> {
let reader = Reader::new();
let file = reader.read(flo_data)?;
if (frame_index as usize) >= file.frames.len() {
return Err(crate::core::FloError::from(format!(
"Frame index {} out of bounds (total frames: {})",
frame_index,
file.frames.len()
)));
}
let frame = &file.frames[frame_index as usize];
let is_transform = frame.frame_type == (crate::FrameType::Transform as u8);
if is_transform {
decode_frame_lossy(&file, frame_index as usize)
} else {
decode_frame_lossless(&file, frame_index as usize)
}
}
pub fn seek_to_time(flo_data: &[u8], target_ms: u32) -> FloResult<SeekResult> {
let reader = Reader::new();
let file = reader.read(flo_data)?;
if file.toc.is_empty() {
return Err("No TOC available for seeking".into());
}
if file.frames.is_empty() {
return Err("TOC contains no decodable frames".into());
}
let mut frame_index = binary_search_frame(&file.toc, target_ms);
if (frame_index as usize) >= file.frames.len() {
frame_index = (file.frames.len() - 1) as u32;
}
let toc_entry = &file.toc[frame_index as usize];
let frame_duration_ms = if frame_index + 1 < (file.toc.len() as u32) {
file.toc[(frame_index + 1) as usize].timestamp_ms - toc_entry.timestamp_ms
} else {
let last_frame_samples = file.frames[frame_index as usize].frame_samples;
(((last_frame_samples as u64) * 1000) / (file.header.sample_rate as u64)) as u32
};
let ms_into_frame = target_ms.saturating_sub(toc_entry.timestamp_ms);
let sample_offset = (((ms_into_frame as u64) * (file.header.sample_rate as u64)) / 1000) as u32;
let frame = &file.frames[frame_index as usize];
let sample_offset = sample_offset.min(frame.frame_samples);
let next_timestamp_ms = if frame_index + 1 < (file.toc.len() as u32) {
file.toc[(frame_index + 1) as usize].timestamp_ms
} else {
toc_entry.timestamp_ms + frame_duration_ms
};
Ok(SeekResult {
frame_index,
byte_offset: toc_entry.byte_offset,
timestamp_ms: toc_entry.timestamp_ms,
sample_offset,
next_timestamp_ms,
})
}
fn binary_search_frame(toc: &[TocEntry], target_ms: u32) -> u32 {
if toc.is_empty() {
return 0;
}
let mut left = 0;
let mut right = toc.len() - 1;
while left < right {
let mid = left + (right - left).div_ceil(2);
if toc[mid].timestamp_ms <= target_ms {
left = mid;
} else {
right = mid - 1;
}
}
left as u32
}
fn decode_frame_lossless(file: &FloFile, frame_index: usize) -> FloResult<Vec<f32>> {
let frame = &file.frames[frame_index];
let decoder = crate::Decoder::new();
let frames = vec![frame.clone()];
let temp_file = FloFile {
header: file.header.clone(),
toc: file.toc.clone(),
frames,
extra: vec![],
metadata: file.metadata.clone(),
};
decoder.decode_file(&temp_file)
}
fn decode_frame_lossy(file: &FloFile, frame_index: usize) -> FloResult<Vec<f32>> {
let frame = &file.frames[frame_index];
if frame.channels.is_empty() {
return Err("Transform frame has no channel data".into());
}
let frame_data = &frame.channels[0].residuals;
if let Some(transform_frame) = crate::lossy::deserialize_frame(frame_data) {
let mut decoder =
crate::lossy::TransformDecoder::new(file.header.sample_rate, file.header.channels);
if frame_index > 0 {
let prev = &file.frames[frame_index - 1];
if !prev.channels.is_empty() {
let ch_data = &prev.channels[0].residuals;
if let Some(tf) = crate::lossy::deserialize_frame(ch_data) {
let _ = decoder.decode_frame(&tf);
}
}
}
Ok(decoder.decode_frame(&transform_frame))
} else {
Err("Failed to deserialize transform frame".into())
}
}