Nik Afiq 5238298b55
Some checks failed
CI / test (push) Successful in 6s
CI / build-ai-gateway (push) Failing after 28s
CI / build-ha-gateway (push) Failing after 24s
CI / build-discord-bot (push) Failing after 29s
Add TTS model components and inference server
- Implemented core model components in `modules.py` including various convolutional layers and normalization techniques.
- Added transformation functions in `transforms.py` for piecewise rational quadratic transformations.
- Created utility functions in `utils.py` for checkpoint management, logging, and hyperparameter handling.
- Introduced monotonic alignment functionality with Cython optimization in `monotonic_align`.
- Developed a minimal inference server in `server.py` to handle synthesis requests.
- Updated requirements to include necessary dependencies for Cython and scipy.
2026-07-24 22:38:36 +09:00

72 lines
2.1 KiB
Go

package inferencesidecar
import (
"encoding/binary"
"encoding/json"
"math"
"net/http"
"net/http/httptest"
"testing"
"gitea.nik4nao.com/nik/home-services/tts-gateway/internal/core/domain"
)
func TestClientSynthesize(t *testing.T) {
t.Run("happy path decodes little-endian float32 PCM and sample rate header", func(t *testing.T) {
wantPCM := []float32{0.1, -0.2, 0.3}
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/synthesize" {
t.Fatalf("request path = %q, want /synthesize", r.URL.Path)
}
var body synthesizeRequest
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Fatalf("decode request body: %v", err)
}
if body.SpeakerID != 29 || len(body.SymbolIDs) != 2 {
t.Fatalf("request body = %+v, unexpected", body)
}
payload := make([]byte, len(wantPCM)*4)
for i, f := range wantPCM {
binary.LittleEndian.PutUint32(payload[i*4:], math.Float32bits(f))
}
w.Header().Set("X-Sample-Rate", "22050")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(payload)
}))
defer srv.Close()
c := NewClient(srv.Listener.Addr().String())
gotPCM, gotSampleRate, err := c.Synthesize(t.Context(), 29, []int64{1, 2}, domain.SynthesisParams{NoiseScale: 0.37})
if err != nil {
t.Fatalf("Synthesize() error = %v", err)
}
if gotSampleRate != 22050 {
t.Fatalf("Synthesize() sampleRate = %d, want 22050", gotSampleRate)
}
if len(gotPCM) != len(wantPCM) {
t.Fatalf("Synthesize() pcm = %v, want %v", gotPCM, wantPCM)
}
for i, want := range wantPCM {
if gotPCM[i] != want {
t.Errorf("pcm[%d] = %v, want %v", i, gotPCM[i], want)
}
}
})
t.Run("non-200 response is surfaced as an error with the body", func(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte("bad speaker id"))
}))
defer srv.Close()
c := NewClient(srv.Listener.Addr().String())
_, _, err := c.Synthesize(t.Context(), 999, []int64{1}, domain.SynthesisParams{})
if err == nil {
t.Fatal("Synthesize() error = nil, want non-nil")
}
})
}