package transcode import ( "bytes" "strings" ) // Format 是小写、无点的音频格式标识("amr" / "wav" / …)。 // // 口径与 mediafetch.AudioFormatOf(internal/channel/mediafetch/inline_voice.go:62) // 归一出的格式名、以及 voice.Audio{Format}(internal/runtime/multimodal/voice) // 的格式名**同一份**:全仓的「字符串 ↔ Format」转换只有本文件的 FormatOf / // FormatName 两处,调用方不得各写一份映射(design/25 §6.2)。 type Format string const ( // FormatAMR 是 AMR-NB(窄带),魔数 `#!AMR\n`。 FormatAMR Format = "amr" // FormatAMRWB 是 AMR-WB(宽带),魔数 `#!AMR-WB\n`。**本期不实现** // (design/25 §3.3 后置):登记它是为了让嗅探与报错诚实——「认得出但没这条路」 // 比「认不出」更能说清问题。 FormatAMRWB Format = "amr-wb" // FormatWAV 是 RIFF/WAVE 容器(本期只发 PCM16 一种载荷)。 FormatWAV Format = "wav" ) // 魔数与容器标识。AMR 文件的头来自 RFC 4867 §5(存储格式);RIFF/WAVE 的四块 // 结构见 design/25 §0.5 登记的 WAV/RIFF 规范。 const ( amrNBMagic = "#!AMR\n" amrWBMagic = "#!AMR-WB\n" riffMagic = "RIFF" waveMagic = "WAVE" // riffChunkSizeOffset 是 RIFF 头里 "WAVE" 标识的起始偏移("RIFF" + 4 字节长度)。 riffChunkSizeOffset = 8 ) // FormatOf 把外部格式名归一成 Format:去空白、转小写、去掉开头的点 // (`.amr` → `amr`)。它不判断格式是否支持——「认不认得」由 Sniff / // FormatOf 决定,「支不支持」由 Registry.Lookup 决定,两件事分开。 func FormatOf(name string) Format { return Format(strings.ToLower(strings.TrimPrefix(strings.TrimSpace(name), "."))) } // FormatName 返回 Format 的规范字符串形式(= 归一化后的格式名)。 func FormatName(f Format) string { return string(f) } // Sniff 按魔数嗅探 data 的音频格式。嗅不出即返回 false(deny-by-default: // 类型不明的字节绝不进解码器)。 func Sniff(data []byte) (Format, bool) { switch { case bytes.HasPrefix(data, []byte(amrWBMagic)): return FormatAMRWB, true case bytes.HasPrefix(data, []byte(amrNBMagic)): return FormatAMR, true case len(data) >= riffChunkSizeOffset+len(waveMagic) && bytes.HasPrefix(data, []byte(riffMagic)) && bytes.HasPrefix(data[riffChunkSizeOffset:], []byte(waveMagic)): return FormatWAV, true default: return "", false } }