纯 MoonBit 实现的 OGG/Vorbis 音频解码器,不依赖任何 C 库或系统编解码器
Dependencies
moon add LL728/moonvorbismoon add moonbitlang/xmoon check --target wasm-gc
moon test --target wasm-gc// moon.pkg
import {
"LL728/moonvorbis",
"moonbitlang/x/fs",
}
// main.mbt —— 把 in.ogg 解成 16-bit PCM WAV,并打印元数据
fn main {
try {
let data = @fs.read_file_to_bytes("in.ogg")
match @moonvorbis.decode_ogg(data) {
Ok(stream) => {
let wav = @moonvorbis.wav_encode_pcm16(
stream.pcm(),
stream.sample_rate(),
)
@fs.write_bytes_to_file("out.wav", wav)
println(
"采样率 \{stream.sample_rate()} Hz,声道 \{stream.channels()},样本 \{stream.pcm()[0].length()}",
)
match stream.metadata() {
Some(meta) =>
match meta.get("TITLE") {
Some(title) => println("标题: \{title}")
None => ()
}
None => ()
}
}
Err(e) => println("解码失败: \{e}")
}
} catch {
@fs.IOError::IOError(msg) => println("I/O 错误: \{msg}")
}
}moon run cmd/main --target wasm-gc -- demo/sample.ogg out.wav
python -m http.server 8000 # 先在仓库根目录起服务
python tools/make_demo_gif.py# 重新生成 demo/moonvorbis.wasm(仓库内已附带一份)
moon build --target wasm-gc --release
cp _build/wasm-gc/release/build/moonvorbis.wasm demo/
python -m http.server
# 打开 http://localhost:8000/demo/| 文件 | 职责 |
|---|---|
| bitreader.mbt | LSB-first 位读取器,带越界检查 |
| ogg_crc.mbt | OGG 页校验用的 CRC-32 |
| ogg_page.mbt | 页头解析与校验 |
| ogg_packet.mbt | 跨页 packet 组装 |
| vorbis_info.mbt | identification 头 |
| vorbis_comment.mbt | comment 头解析与元数据查询(get / get_all) |
| vorbis_setup.mbt | setup 头,聚合下列子结构 |
| codebook.mbt | codebook 与 Huffman/VQ 解码 |
| huffman.mbt | Huffman 表构建 |
| floor0.mbt | floor0 解码与合成 |
| floor1.mbt | floor1 解码与合成 |
| residue.mbt | residue 配置与解码 |
| mapping.mbt | channel mapping 与耦合 |
| mode.mbt | block flag / mapping 选择 |
| mdct.mbt | IMDCT |
| window.mbt | 窗函数 |
| decoder.mbt | 解码主循环 |
| vorbis_stream.mbt | 高层编排:字节流 → PCM |
| wav.mbt | WAV 序列化 |
| wasm_api.mbt | WASM 导出接口 |
| cmd/main/ | 命令行入口 |
| CHANGELOG.md | 各版本的改动记录 |
| index.html | 项目主页(GitHub Pages 的根路径) |
| tools/verify.py | 对 libvorbis 的交叉验证脚本 |
| tools/vorbisgen.py | 按规范直接拼出 OGG/Vorbis 测试流 |
| tools/granule_sweep.py | 扫 granule position 裁剪行为 |
| tools/residue_sweep.py | 扫 residue 各条通路 |
| tools/floor0_sweep.py | 扫 floor 0 各条通路 |
| tools/wasm_contract.py | 静态校验 wasm 的导入/导出契约 |
| tools/wasm_smoke.mjs | 在 Node 里跑通 wasm 解码并与命令行产物比对 |
| tools/make_demo_gif.py | 逐帧截图并合成演示 GIF |
| demo/headless-test.html | 无头浏览器冒烟测试 |
| demo/decoder.js | 浏览器侧解码胶水,两个演示页共用 |
| demo/record.html | 演示录制页,按 ?t= 渲染流程中的某一刻 |
moon test --target wasm-gc # 77 个用例
moon test --target js # 同一套用例,js 后端python tools/verify.py --stereo # 相关系数 0.9999
python tools/verify.py --stereo --noise # 相关系数 0.9970python tools/verify.py song.oggpython tools/granule_sweep.py # 12 组 packet 数 × 尾部裁剪量
python tools/residue_sweep.py # residue type 0/1 × 单/多声道 × sequence_p
python tools/floor0_sweep.py # floor 0 的奇/偶阶 × 单/多声道 × 采样率与 Bark 频带数python tools/wasm_contract.py
node tools/wasm_smoke.mjs out.wav
moon run cmd/main --target wasm-gc -- demo/sample.ogg cli.wav
cmp out.wav cli.wavpython -m http.server 8000
# 打开 http://localhost:8000/demo/headless-test.html| 项目 | 链接 | 许可证 | 参考范围 |
|---|---|---|---|
| stb_vorbis | https://github.com/nothings/stb/blob/master/stb_vorbis.c | public domain | 作为同一份规范的确定性读法,用于定位 7 处规范理解偏差 |
| libvorbis | https://github.com/xiph/vorbis | BSD-3-Clause | floor 0 合成公式的期望值推算;sequence_p 语义歧义时以其为准;工具脚本的输出对照参考实现 |
| 依赖 | 链接 | 许可证 | 用途 |
|---|---|---|---|
| moonbitlang/core | https://github.com/moonbitlang/core | Apache-2.0 | 缓冲区、base64、UTF-8 解码与数学函数;随 MoonBit 工具链提供 |
| moonbitlang/x | https://github.com/moonbitlang/x | Apache-2.0 | 只有命令行入口用到(moonbitlang/x/fs);解码库本身不依赖 |
| 文件 | 来源 |
|---|---|
| demo/sample.ogg | 按 tools/verify.py 的信号规格生成:左声道 440 Hz、右声道 554 Hz 的立体声正弦波,44.1 kHz、1 秒,无第三方内容 |
| demo/moonvorbis.wasm | 由本仓库源码构建(moon build --target wasm-gc --release) |
| demo/demo.gif | 由 tools/make_demo_gif.py 对 demo/record.html 逐帧截图合成,波形取自真实解码结果 |
type BitReaderpub struct Codebook {
dimensions : Int
entries : Int
lengths : Array[Int]
lookup_type : Int
min_value : Float
delta_value : Float
value_bits : Int
sequence_flag : Int
multiplicands : Array[Int]
multiplicands_f : Array[Float]
huffman : HuffmanTable
}pub struct Decoder {
info : VorbisInfo
setup : VorbisSetup
window0 : Array[Float]
window1 : Array[Float]
previous_window : Array[Array[Float]]
previous_length : Array[Int]
}pub struct Floor0 {
order : Int
rate : Int
bark_map_size : Int
amplitude_bits : Int
amplitude_offset : Int
books : Array[Int]
}pub struct Floor1 {
partitions : Int
partition_class_list : Array[Int]
class_dimensions : Array[Int]
class_subclasses : Array[Int]
class_masterbooks : Array[Int]
subclass_books : Array[Array[Int]]
multiplier : Int
rangebits : Int
x_list : Array[Int]
sorted_order : Array[Int]
neighbors : Array[Array[Int]]
}pub struct Mode {
blockflag : Int
windowtype : Int
transformtype : Int
mapping : Int
}pub struct OggPage {
data : Bytes
offset : Int
header_type : Int
granule_position : Int64
serial_number : UInt
page_sequence : UInt
checksum : UInt
n_segments : Int
body_len : Int
}type PacketAssemblerpub struct VorbisInfo {
channels : Int
sample_rate : Int
blocksize0 : Int
blocksize1 : Int
}pub struct VorbisStream {
// private fields
}fn decode_ogg_base64(ogg_b64 : String) -> Stringfn ogg_crc32(data : Bytes) -> UIntfn ogg_crc32_update(crc : UInt, data : Bytes, start : Int, len : Int) -> UIntInstall
Download zip纯 MoonBit 实现的 OGG/Vorbis 音频解码器,不依赖任何 C 库或系统编解码器
Dependencies