111 lines
3.9 KiB
Go
111 lines
3.9 KiB
Go
package transcode
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"math"
|
|
"testing"
|
|
)
|
|
|
|
// TestWAVHeaderFields 逐字段断言 44 字节头(design/25 §3.1:不重采样、8 kHz、
|
|
// 单声道、PCM16)。
|
|
func TestWAVHeaderFields(t *testing.T) {
|
|
const samples = 160
|
|
dataBytes := samples * wavPCMBytesPerSample
|
|
hdr, err := wavHeader(dataBytes, amrSampleRate, amrChannels)
|
|
if err != nil {
|
|
t.Fatalf("wavHeader: %v", err)
|
|
}
|
|
if len(hdr) != wavHeaderBytes {
|
|
t.Fatalf("header length = %d, want %d", len(hdr), wavHeaderBytes)
|
|
}
|
|
if string(hdr[wavOffRiffID:wavOffChunkSize]) != wavRiffID ||
|
|
string(hdr[wavOffWaveID:wavOffFmtID]) != wavWaveID ||
|
|
string(hdr[wavOffFmtID:wavOffFmtSize]) != wavFmtID ||
|
|
string(hdr[wavOffDataID:wavOffDataSize]) != wavDataID {
|
|
t.Fatalf("chunk ids wrong: %q %q %q %q",
|
|
hdr[wavOffRiffID:wavOffChunkSize], hdr[wavOffWaveID:wavOffFmtID],
|
|
hdr[wavOffFmtID:wavOffFmtSize], hdr[wavOffDataID:wavOffDataSize])
|
|
}
|
|
checks := []struct {
|
|
name string
|
|
got uint32
|
|
want uint32
|
|
}{
|
|
{"RIFF size", binary.LittleEndian.Uint32(hdr[wavOffChunkSize:]), uint32(riffChunkSizeBase + dataBytes)},
|
|
{"fmt size", binary.LittleEndian.Uint32(hdr[wavOffFmtSize:]), wavFmtChunkBytes},
|
|
{"sample rate", binary.LittleEndian.Uint32(hdr[wavOffSampleRate:]), uint32(amrSampleRate)},
|
|
{"byte rate", binary.LittleEndian.Uint32(hdr[wavOffByteRate:]), uint32(amrSampleRate * wavPCMBytesPerSample)},
|
|
{"data size", binary.LittleEndian.Uint32(hdr[wavOffDataSize:]), uint32(dataBytes)},
|
|
}
|
|
for _, c := range checks {
|
|
if c.got != c.want {
|
|
t.Errorf("%s = %d, want %d", c.name, c.got, c.want)
|
|
}
|
|
}
|
|
if got := binary.LittleEndian.Uint16(hdr[wavOffAudioFormat:]); got != wavFormatPCM {
|
|
t.Errorf("audio format = %d, want %d (PCM)", got, wavFormatPCM)
|
|
}
|
|
if got := binary.LittleEndian.Uint16(hdr[wavOffChannels:]); got != amrChannels {
|
|
t.Errorf("channels = %d, want %d", got, amrChannels)
|
|
}
|
|
if got := binary.LittleEndian.Uint16(hdr[wavOffBlockAlign:]); got != wavPCMBytesPerSample {
|
|
t.Errorf("block align = %d, want %d", got, wavPCMBytesPerSample)
|
|
}
|
|
if got := binary.LittleEndian.Uint16(hdr[wavOffBitsPerSamp:]); got != wavBitsPerSample {
|
|
t.Errorf("bits per sample = %d, want %d", got, wavBitsPerSample)
|
|
}
|
|
}
|
|
|
|
// TestEncodePCM16WAVRoundTrip 断言样本 → WAV → 样本往返无损(小端正确)。
|
|
func TestEncodePCM16WAVRoundTrip(t *testing.T) {
|
|
samples := []int16{0, 1, -1, math.MaxInt16, math.MinInt16, 1234, -4321}
|
|
wav, err := encodePCM16WAV(samples, amrSampleRate, amrChannels)
|
|
if err != nil {
|
|
t.Fatalf("encodePCM16WAV: %v", err)
|
|
}
|
|
got := wavDataSamples(t, wav)
|
|
if len(got) != len(samples) {
|
|
t.Fatalf("round trip samples = %d, want %d", len(got), len(samples))
|
|
}
|
|
for i := range samples {
|
|
if got[i] != samples[i] {
|
|
t.Fatalf("sample %d = %d, want %d", i, got[i], samples[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestWAVHeaderRejectsImpossibleDeclarations 断言不可能的头参数被拒(不写出
|
|
// 一个长度撒谎的头)。
|
|
func TestWAVHeaderRejectsImpossibleDeclarations(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
dataBytes, rate, chans int
|
|
}{
|
|
{"zero rate", 2, 0, 1},
|
|
{"negative rate", 2, -1, 1},
|
|
{"zero channels", 2, amrSampleRate, 0},
|
|
{"negative data", -1, amrSampleRate, 1},
|
|
{"data beyond RIFF limit", int(wavMaxDataBytes) + 1, amrSampleRate, 1},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if _, err := wavHeader(tc.dataBytes, tc.rate, tc.chans); !errors.Is(err, ErrEncodeFailed) {
|
|
t.Fatalf("err = %v, want ErrEncodeFailed", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestWritePCM16LEIsLittleEndian 断言字节序:-2 = 0xFE 0xFF。
|
|
func TestWritePCM16LEIsLittleEndian(t *testing.T) {
|
|
dst := make([]byte, 2*wavPCMBytesPerSample)
|
|
writePCM16LE(dst, []int16{-2, 258})
|
|
if dst[0] != 0xFE || dst[1] != 0xFF {
|
|
t.Fatalf("-2 写成 %#x %#x, want 0xfe 0xff", dst[0], dst[1])
|
|
}
|
|
if dst[2] != 0x02 || dst[3] != 0x01 {
|
|
t.Fatalf("258 写成 %#x %#x, want 0x02 0x01", dst[2], dst[3])
|
|
}
|
|
}
|