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

70 lines
2.0 KiB
Go

package app
import (
"context"
"errors"
"strings"
"gitea.nik4nao.com/nik/home-services/tts-gateway/internal/core/domain"
"gitea.nik4nao.com/nik/home-services/tts-gateway/internal/core/ports/driven"
)
// ErrSpeakerNotFound is returned when the requested speaker name has no
// matching entry in domain.Speakers.
var ErrSpeakerNotFound = errors.New("speaker not found")
type TTSApp struct {
normalizer driven.TextNormalizer
engine driven.TTSEngine
encoder driven.AudioEncoder
nameToID map[string]int
}
// NewTTSApp constructs the synthesis application service.
func NewTTSApp(normalizer driven.TextNormalizer, engine driven.TTSEngine, encoder driven.AudioEncoder) *TTSApp {
nameToID := make(map[string]int, len(domain.Speakers))
for _, s := range domain.Speakers {
nameToID[s.Name] = s.ID
}
return &TTSApp{normalizer: normalizer, engine: engine, encoder: encoder, nameToID: nameToID}
}
// Synthesize normalizes text, runs the TTS engine, and encodes the result.
func (a *TTSApp) Synthesize(ctx context.Context, speakerName, text string, params domain.SynthesisParams) (*domain.AudioClip, error) {
speakerID, ok := a.nameToID[speakerName]
if !ok {
return nil, ErrSpeakerNotFound
}
symbolIDs, err := a.normalizer.Normalize(ctx, text)
if err != nil {
return nil, err
}
pcm, sampleRate, err := a.engine.Synthesize(ctx, speakerID, symbolIDs, params)
if err != nil {
return nil, err
}
data, mimeType, err := a.encoder.Encode(ctx, pcm, sampleRate)
if err != nil {
return nil, err
}
return &domain.AudioClip{Data: data, MimeType: mimeType}, nil
}
// ListSpeakers returns speaker names filtered by a case-insensitive substring
// match, or the full roster when search is empty - matching uma-tts-api's
// /speakers endpoint.
func (a *TTSApp) ListSpeakers(_ context.Context, search string) []string {
search = strings.ToLower(search)
names := make([]string, 0, len(domain.Speakers))
for _, s := range domain.Speakers {
if search == "" || strings.Contains(strings.ToLower(s.Name), search) {
names = append(names, s.Name)
}
}
return names
}