#audiobook #comments #encoding

toniefile

托尼文件crate提供了将音频数据写入可以被托尼盒播放的格式的函数。

1个不稳定版本

0.1.1 2024年2月2日

#550 in 音频

自定义许可证

55KB
607

#+title: 读取说明

  • 托尼文件

托尼文件crate提供了将音频数据写入可以被托尼盒播放的格式的函数。托尼文件格式是一个带有protobuf头部的修改过的Ogg Opus文件格式。然而,Ogg页面必须是4096个字节长,并且必须从文件中的4096个字节标记开始和结束。 (第一个页面除外,它从0x1200开始,到0x1FFF结束)protobuf头部本身也必须填充到4096个字节,并且Ogg头部和Ogg注释页面也必须填充到200个字节。这会得到如下文件: #+begin_src

地址数据 ascii 00000000 00 00 0f fc 0a 14 04 ef db a5 67 5d a9 a7 96 1e |..........g]....| ^ The protobuf header (starts at 0x04) until 0x0FFF.

0x00 - 0x03 are the length of the header in bytes 00001000 4f 67 67 53 00 02 00 00 00 00 00 00 00 00 78 56 |OggS..........xV| ^ Opus Header and Opus Comment until 0x11FF 00001200 4f 67 67 53 00 00 40 38 00 00 00 00 00 00 78 56 |[email protected]| ^ First payload audio data until 0x1FFF 00002000 4f 67 67 53 00 00 40 38 00 00 00 00 00 00 78 56 |[email protected]| ^ Second payload audio data until 0x2FFF 00003000 4f 67 67 53 00 00 40 38 00 00 00 00 00 00 78 56 |[email protected]| ^ And so on #+end_src

  • 用法 Toniefile结构体在其创建时接受任何实现了Write和Seek特征的任何事物作为其writer。当调用encode()或finalize()方法之一时,它将写入writer。在其创建过程中,它以唯一的音频ID和一个可选的用户注释Vector作为参数。音频ID可以是一个任意的uint32,用于在托尼盒上识别音频文件。(这里可以使用任何您想要的)用户注释是一个Vector &str,它被写入Ogg注释页面,它可以是空的。 #+begin_src Rust fn fill_single_buffer_toniefile() { // create a file let file = File::create(~/my_little_toniefile").unwrap(); // create a toniefile struct let mut toniefile = Toniefile::new ( file, 0x12345678, Some(vec!["my comment", "another comment"]) ) .unwrap();

    读取样本,这取决于您的用例。例如,使用 wav crate 将整个 wav 文件读入内存。let mut f = File::open("~/my_little_samplesfile").expect("文件未找到"); let (_, samples) = wav::read(&mut f).unwrap(); samples.try_into_sixteen().unwrap() let samples: Vec = samples.to_str().unwrap();

    将样本写入 toniefile。let res = toniefile.encode(&samples); // 确定toniefile。toniefile.finalize().unwrap(); } #+end_src

还可以多次调用 encode() 并使用较小的缓冲区。如果您不希望一次性将整个样本文件加载到内存中,这可能会很有用。如果我们不关心它们,可以使用 new_simple() 创建一个具有随机音频 ID 且没有用户注释的 Toniefile。这里我们使用 hound crate (https://crates.io/crates/hound) 中的 WavReader 读取样本的块。

#+begin_src Rust fn read_and_fill_chunks_toniefile() { let file = File::create("~/my_little_toniefile").unwrap(); let mut toniefile = Toniefile::new_simple(file) .unwrap();

let mut f = File::open("~/my_little_samplesfile").unwrap();
let mut wav_reader = hound::WavReader::new(&mut f).unwrap();
let total_samples = wav_reader.duration();
let mut wav_iter = wav_reader.samples::<i16>();
let mut samples_read = 0;
while samples_read < total_samples {
    let mut window = vec![];
    for _ in 0..TONIEFILE_FRAME_SIZE * OPUS_CHANNELS {
        window.push(wav_iter.next().unwrap().unwrap());
    }
    let res = toniefile.encode(&window);
    assert!(res.is_ok());
    samples_read += window.len() as u32;
}

toniefile.finalize().unwrap();

} #+end_src

最后,如果向量实现了 Write 和 Seek 特性,则也可以让 Toniefile 结构写入到向量中,而不是磁盘上的文件。我们可以使用 std 库中的 Cursor 结构来实现这一点。

#+begin_src Rust use std::io::{Cursor, Seek, SeekFrom, Write}; use toniefile::Toniefile;

fn fill_vector_toniefile() { let myvec: Vec = vec![]; let cursor = Cursor::new(myvec); let mut toniefile = Toniefile::new(cursor, 0x12345678, None).unwrap(); let samples: Vec = vec![0; 48000 * 2 * 60]; // 60 秒的最细静音 let res = toniefile.encode(&samples); assert!(res.is_ok()); toniefile.finalize_no_consume().unwrap(); // 对向量进行操作,例如将其写入磁盘或通过网络发送 } #+end_src

依赖关系

~5.5–7.5MB
~139K SLoC