mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-05-25 16:00:35 +00:00
0f395ce110
* refactor: update ASR and TTS implementations * fix lint * Integrating asr/tts models w/ new security config * update documents * add arbitrary whisper transcriptor support * update documents * fix lint * add mimo tts
147 lines
3.7 KiB
Go
147 lines
3.7 KiB
Go
package audio
|
|
|
|
import (
|
|
"bytes"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// buildOggPage helper creates an Ogg page for testing.
|
|
// lacingVals specifies the segment table, and data is the payload.
|
|
func buildOggPage(lacingVals []byte, data []byte) []byte {
|
|
var buf bytes.Buffer
|
|
// 27-byte Ogg header
|
|
header := make([]byte, 27)
|
|
copy(header[:4], "OggS")
|
|
header[5] = 0 // type flag
|
|
// For testing, we only care about OggS magic and page_segments (byte 26)
|
|
header[26] = byte(len(lacingVals))
|
|
buf.Write(header)
|
|
buf.Write(lacingVals)
|
|
buf.Write(data)
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func TestDecodeOggOpus_ValidParsing(t *testing.T) {
|
|
var b bytes.Buffer
|
|
|
|
// Packet 1: Single segment, length 50
|
|
pkt1 := bytes.Repeat([]byte{1}, 50)
|
|
// Packet 2: Multi-segment (255 + 10 = 265 bytes)
|
|
pkt2Part1 := bytes.Repeat([]byte{2}, 255)
|
|
pkt2Part2 := bytes.Repeat([]byte{2}, 10)
|
|
// Packet 3: Continued across pages. Page 1 gets 255, Page 2 gets 20. Total 275 bytes.
|
|
pkt3Part1 := bytes.Repeat([]byte{3}, 255)
|
|
pkt3Part2 := bytes.Repeat([]byte{3}, 20)
|
|
|
|
// Page 1: OpusHead (skip), OpusTags (skip), pkt1, pkt2, pkt3Part1
|
|
page1Lacing := []byte{8, 8, 50, 255, 10, 255}
|
|
page1Data := bytes.Join([][]byte{
|
|
[]byte("OpusHead"),
|
|
[]byte("OpusTags"),
|
|
pkt1,
|
|
pkt2Part1, pkt2Part2,
|
|
pkt3Part1,
|
|
}, nil)
|
|
|
|
// Page 2: pkt3Part2, pkt4 (length 10)
|
|
pkt4 := bytes.Repeat([]byte{4}, 10)
|
|
page2Lacing := []byte{20, 10}
|
|
page2Data := bytes.Join([][]byte{
|
|
pkt3Part2,
|
|
pkt4,
|
|
}, nil)
|
|
|
|
b.Write(buildOggPage(page1Lacing, page1Data))
|
|
b.Write(buildOggPage(page2Lacing, page2Data))
|
|
|
|
var frames [][]byte
|
|
err := DecodeOggOpus(&b, func(frame []byte) error {
|
|
// making a copy to store as DecodeOggOpus might reuse backing array
|
|
cpy := make([]byte, len(frame))
|
|
copy(cpy, frame)
|
|
frames = append(frames, cpy)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
expectedFrames := [][]byte{
|
|
pkt1,
|
|
append(pkt2Part1, pkt2Part2...),
|
|
append(pkt3Part1, pkt3Part2...),
|
|
pkt4,
|
|
}
|
|
|
|
if len(frames) != len(expectedFrames) {
|
|
t.Fatalf("expected %d frames, got %d", len(expectedFrames), len(frames))
|
|
}
|
|
|
|
for i, expected := range expectedFrames {
|
|
if !reflect.DeepEqual(frames[i], expected) {
|
|
t.Errorf("frame %d mismatch:\nexp: %v\ngot: %v", i, expected, frames[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeOggOpus_Errors(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
errContains string
|
|
}{
|
|
{
|
|
name: "invalid magic string",
|
|
data: []byte(
|
|
"OggX\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
|
),
|
|
errContains: "invalid ogg magic string",
|
|
},
|
|
{
|
|
name: "short header",
|
|
data: []byte("Ogg"),
|
|
errContains: "failed to read ogg header",
|
|
},
|
|
{
|
|
name: "eof in segment table",
|
|
data: func() []byte {
|
|
h := make([]byte, 27)
|
|
copy(h, "OggS")
|
|
h[26] = 5 // expects 5 bytes of segment table, but none provided
|
|
return h
|
|
}(),
|
|
errContains: "failed to read segment table",
|
|
},
|
|
{
|
|
name: "eof in segment data",
|
|
data: func() []byte {
|
|
h := make([]byte, 27, 28)
|
|
copy(h, "OggS")
|
|
h[26] = 1
|
|
return append(h, 100) // expects 100 bytes of data, but none provided
|
|
}(),
|
|
errContains: "failed to read segment data",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := DecodeOggOpus(bytes.NewReader(tt.data), func(b []byte) error { return nil })
|
|
if tt.name == "short header" {
|
|
if err != nil {
|
|
t.Errorf("expected no error (io.EOF/ErrUnexpectedEOF swallowed), got %v", err)
|
|
}
|
|
return
|
|
}
|
|
if err == nil {
|
|
t.Fatalf("expected error containing %q, got nil", tt.errContains)
|
|
}
|
|
if !strings.Contains(err.Error(), tt.errContains) {
|
|
t.Errorf("expected error to contain %q, got: %q", tt.errContains, err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|