mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-10 08:18:33 -04:00
* feat(vibevoice-cpp): true streaming TTSStream via vv_capi_tts_stream Replaces the synth-to-tempfile TTSStream hack with a real streaming path: binds the new vv_capi_tts_stream callback ABI via a single reusable purego callback (CGO_ENABLED=0-safe, no runtime/cgo), copies each int16 PCM window into the gRPC results channel after the streaming WAV header. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * test(vibevoice-cpp): real-model streaming integration test with TTFA measurement Gated behind VIBEVOICE_IT=1, this Ginkgo spec dlopens the engine .so and drives the exact Go->purego->C TTSStream/TTS path against the real vibevoice-realtime-0.5B model. It measures time-to-first-audio for the streaming path versus the batch path and asserts the streaming win: 44-byte WAV header first, >=2 PCM windows, non-silent audio, and TTFA < total_stream. Without the env var the spec skips so CI and normal go test are unaffected. Measured: TTFA 2.38s vs batch deliver-time 39.96s (first audio in 5.9% of the batch time, ~17x faster), 18 stream chunks, non-silent 24kHz PCM. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * chore(vibevoice-cpp): pin streaming-decoder engine build Bumps VIBEVOICE_CPP_VERSION to the streaming-decoder engine commit that adds vv_capi_tts_stream (localai-org/vibevoice.cpp#8). Re-pin to the merged master commit once that PR lands. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * chore(vibevoice-cpp): re-pin to merged streaming-decoder commit localai-org/vibevoice.cpp#8 merged to master as 000e372; move the pin off the PR branch commit onto the merged master commit. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * test(vibevoice-cpp): check writer errors in TTFA report (errcheck) golangci-lint errcheck flagged the unchecked fmt.Fprintf calls that print the streaming TTFA headline. Build the report once with fmt.Sprintf and write it per destination with an explicitly discarded error, matching the GinkgoWriter reporting idiom used by the other backend tests. Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-fable-5 [Claude Code] --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
47 lines
1.4 KiB
Go
47 lines
1.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/binary"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("TTSStream callback framing", func() {
|
|
// The real C callback copies a transient int16 PCM buffer out of the
|
|
// engine into a fresh []byte and pushes it onto the active stream's
|
|
// results channel as little-endian bytes. deliverPCMForTest runs that
|
|
// exact copy-and-push path against a []int16 so we can validate the
|
|
// framing without the C library (full audio e2e is a later task).
|
|
It("copies int16 PCM from the C callback into the results channel as LE bytes", func() {
|
|
samples := []int16{0, 1, -1, 32767, -32768, 1234, -4321}
|
|
|
|
prev := activeStream
|
|
DeferCleanup(func() { activeStream = prev })
|
|
|
|
s := &streamState{results: make(chan []byte, 1)}
|
|
activeStream = s
|
|
|
|
deliverPCMForTest(samples)
|
|
|
|
var got []byte
|
|
Eventually(s.results).Should(Receive(&got))
|
|
Expect(got).To(HaveLen(len(samples) * 2))
|
|
|
|
want := make([]byte, len(samples)*2)
|
|
for i, v := range samples {
|
|
binary.LittleEndian.PutUint16(want[i*2:], uint16(v))
|
|
}
|
|
Expect(got).To(Equal(want))
|
|
})
|
|
|
|
It("is a no-op when there is no active stream", func() {
|
|
prev := activeStream
|
|
DeferCleanup(func() { activeStream = prev })
|
|
activeStream = nil
|
|
|
|
// Must not panic when no stream is installed.
|
|
Expect(func() { deliverPCMForTest([]int16{1, 2, 3}) }).ToNot(Panic())
|
|
})
|
|
})
|