Enable return value in callback for TTS in Go API. - #3150
Conversation
📝 WalkthroughWalkthroughAdds real-time audio playback to the offline TTS example (Oto + io.Pipe), updates Go versions to 1.24.0, changes TTS callback signatures to return bool for continuation, exposes OfflineTts.NumSpeakers() and SampleRate(), and redirects several test script paths to offline-tts-play. Changes
Sequence DiagramsequenceDiagram
participant App as Main App
participant Gen as TTS Generator (goroutine)
participant Pipe as io.Pipe
participant Player as Oto Player
participant Audio as Audio Output
App->>App: create Oto context & player
App->>Pipe: create reader/writer
App->>Player: wire pipe reader & start playback
App->>Gen: launch generator goroutine
par generation & playback
Gen->>Gen: GenerateWithCallback -> produce samples
Gen->>Gen: clamp & encode to 16-bit LE PCM
Gen->>Pipe: write PCM chunks
and
Player->>Pipe: read PCM chunks
Player->>Audio: stream to audio device
end
Gen->>App: close writer & signal done
App->>App: wait for stop or completion, drain, shutdown
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @csukuangfj, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly improves the Go API for Text-to-Speech by introducing a mechanism for callbacks to control the audio generation flow, enabling more dynamic and responsive applications. Additionally, it provides a practical, ready-to-use example of how to stream and play generated TTS audio in real-time, enhancing the overall utility and demonstration of the TTS capabilities. Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a valuable enhancement to the Go TTS API by allowing the callback to return a value, which provides better control over the audio generation stream. The new offline-tts-play example is a great addition, clearly demonstrating how to stream and play TTS audio as it's generated. The code is well-structured and uses Go's concurrency patterns effectively. I have one suggestion to improve the shutdown logic in the new example by removing a redundant sleep.
| // let remaining audio drain | ||
| time.Sleep(500 * time.Millisecond) |
There was a problem hiding this comment.
This time.Sleep is not necessary to ensure the audio drains completely. The player.Close() call, which is deferred on line 97, is a blocking call that waits for the audio stream to be fully played, as per the oto library's documentation. Removing this sleep will make the shutdown logic cleaner and more robust.
There was a problem hiding this comment.
Pull request overview
This PR enhances the Go API for TTS functionality by enabling return value handling in callbacks and adding a new demo for real-time TTS audio playback.
Changes:
- Modified TTS callback signatures to return bool values, allowing callers to control generation continuation
- Added accessor methods (NumSpeakers, SampleRate) to the OfflineTts type
- Created a new demo that plays generated TTS audio in real-time using the oto audio library with signal handling support
- Updated shell script references from non-streaming-tts to offline-tts-play for consistency
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/go/sherpa_onnx.go | Updated callback type signatures to return bool and implemented proper return value mapping to C API (1=continue, 0=stop); added NumSpeakers() and SampleRate() accessor methods |
| go-api-examples/offline-tts-play/main.go | Implemented real-time audio playback demo with streaming TTS output, audio format conversion, signal handling, and goroutine-based concurrent processing |
| go-api-examples/offline-tts-play/go.mod | Updated Go version to 1.24.0 |
| scripts/go/_internal/offline-tts-play/go.mod | Updated Go version to 1.24.0 |
| go-api-examples/offline-tts-play/run-matcha-zh.sh | Changed debug flag from 1 to 0 |
| scripts/go/_internal/offline-tts-play/run-*.sh | Updated script paths to reference offline-tts-play instead of non-streaming-tts |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <-ready | ||
|
|
||
| // Pipe: TTS writes → Oto reads | ||
| pr, pw := io.Pipe() |
There was a problem hiding this comment.
The pipe reader (pr) is never explicitly closed. When the main function exits, the player.Close() will close the player, but the pipe reader itself should be closed to prevent potential resource leaks. Consider adding a defer statement like "defer pr.Close()" after creating the pipe, or closing pr explicitly before the function returns.
| pr, pw := io.Pipe() | |
| pr, pw := io.Pipe() | |
| defer pr.Close() |
|
|
||
| select { | ||
| case <-stop: | ||
| log.Println("Interrupted, stopping...") |
There was a problem hiding this comment.
When the program is interrupted via Ctrl+C (SIGINT/SIGTERM), there's no mechanism to stop the ongoing TTS generation in the goroutine. The goroutine will continue writing to the pipe writer (pw) even after interruption, which could lead to a panic if pw.Write is called after the pipe is closed or the player is closed. Consider using a context or closing the pipe writer to signal the callback to stop generating audio.
| log.Println("Interrupted, stopping...") | |
| log.Println("Interrupted, stopping...") | |
| // Close the pipe writer to signal the TTS callback to stop generating audio. | |
| _ = pw.Close() | |
| // Wait for the TTS goroutine to finish cleanup. | |
| <-done |
Also, add demo for tts playback
Summary by CodeRabbit
New Features
Refactor
Chores