- 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.
72 lines
2.1 KiB
Go
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")
|
|
}
|
|
})
|
|
}
|