package tts // 本文件是**音频字节嗅探**:判断上游回给我们的到底是不是音频,以及它到底是什么 // 格式。 // // 两条口径(与本仓「字节嗅探是唯一裁决权」同源,design/24 §10.1/§10.11): // - **不信声明**:上游的 `Content-Type` 与我自己发出去的 `response_format` 都不 // 是判据 —— 服务端产物恒为 WAV,但会按 `response_format` 自报 media_type // (`tts_router.py` 的 `FileResponse`),照抄它就会把 WAV 标成 mp3。 // - **不是音频就报错**:上游 200 也可能是一段 JSON/HTML(配置错、反代错误页), // 把它当音频落盘/下发等于给客户端一个打不开的文件。判不出来就 **ErrNotAudio**, // 由调用方回落既有链 —— 绝不"拿到字节就算成功"。 // sniffAudio 按魔数判断 b 是不是可下发的音频,返回格式名与是否认出。 // // 认得的容器覆盖本仓音频白名单里最常见的那几种(wav / mp3 / ogg / flac / // m4a(mp4) / webm / amr);认不出返回 ("", false)。 func sniffAudio(b []byte) (string, bool) { switch { case hasPrefix(b, "RIFF") && len(b) >= 12 && string(b[8:12]) == "WAVE": return "wav", true case hasPrefix(b, "ID3"): return "mp3", true case isMP3Frame(b): return "mp3", true case hasPrefix(b, "OggS"): return "ogg", true case hasPrefix(b, "fLaC"): return "flac", true case len(b) >= 12 && string(b[4:8]) == "ftyp": return "m4a", true case hasPrefix(b, "\x1a\x45\xdf\xa3"): return "webm", true case hasPrefix(b, "#!AMR"): return "amr", true default: return "", false } } // isMP3Frame 识别无 ID3 头的裸 MPEG 音频帧同步字(11 位全 1 + 合法版本/层)。 // 只做**粗判**:音频帧的头两字节 + 一个合法比特率/采样率索引即可,目的是把 // "看起来像二进制"与"真的是 MP3"区分开,不追求完整解析。 func isMP3Frame(b []byte) bool { if len(b) < 3 { return false } if b[0] != 0xFF || b[1]&0xE0 != 0xE0 { return false } // 版本位(b[1] 的 bit 3-4)不能是 01(保留值);层位(bit 1-2)不能是 00。 if b[1]&0x18 == 0x08 || b[1]&0x06 == 0x00 { return false } // 比特率索引(b[2] 的 bit 4-7)不能是 1111(非法)。 return b[2]>>4 != 0x0F } // hasPrefix 报告 b 是否以 s 开头(s 为 ASCII 字符串字面量)。 func hasPrefix(b []byte, s string) bool { if len(b) < len(s) { return false } return string(b[:len(s)]) == s }