C++ client for the Fish Audio API — text-to-speech and speech-to-text, over REST and WebSocket.
| Area | REST | Streaming |
|---|---|---|
| Text-to-Speech | POST /v1/tts |
wss:///v1/tts/live |
| Speech-to-Text (ASR) | POST /v1/asr |
— (Fish Audio has no ASR WebSocket) |
s1/s2-pro/s2.1-pro/s2.1-pro-freeTTS models,wav/pcm/mp3/opusoutput formats- Prosody control (speed/volume/loudness normalization), inline zero-shot voice cloning via reference audio samples, multi-speaker synthesis (S2-Pro)
- Live TTS over a MessagePack-framed WebSocket for voice agents and low-latency playback
- Per-segment ASR transcription with timestamps
- See docs/api_coverage.md for the exact endpoint/field checklist
| Dependency | Fetched automatically? |
|---|---|
| spdlog, nlohmann-json | Yes (CMake FetchContent, or reused if already on the system) |
| libcurl | Yes — built from source with mbedTLS (non-Windows) or Schannel (Windows) |
| libwebsockets | Yes, non-Windows only — Windows uses the native WinHTTP WebSocket API instead |
| mbedTLS | Yes, non-Windows only — shared TLS backend for curl and libwebsockets |
| CLI11 | Yes, sample applications only |
No vcpkg, no Boost, no manually-installed system packages, no separate MessagePack library —
MessagePack encoding for the live TTS protocol uses nlohmann::json::to_msgpack/from_msgpack,
which ships with the JSON dependency this library already needs. Everything is resolved via
find_package first (so a system-installed copy is reused if present) and falls back to building
from source otherwise.
git clone https://github.com/fatehmtd/fishaudiopp.git
cd fishaudiopp
cmake -B build
cmake --build buildLibrary-only build (skip the sample applications):
cmake -B build -DFISHAUDIOPP_BUILD_SAMPLES=OFFShared library build (for consumption as a prebuilt .so/.dylib/.dll — every public class is
annotated with FISHAUDIOPP_EXPORT, generated by CMake's GenerateExportHeader module):
cmake -B build -DFISHAUDIOPP_BUILD_SHARED=ONinclude(FetchContent)
FetchContent_Declare(
fishaudiopp
GIT_REPOSITORY https://github.com/fatehmtd/fishaudiopp.git
GIT_TAG main # or a specific tag/commit
)
FetchContent_MakeAvailable(fishaudiopp)
target_link_libraries(your_target PRIVATE fishaudiopp)git submodule add https://github.com/fatehmtd/fishaudiopp.git deps/fishaudiopp
git submodule update --init --recursiveadd_subdirectory(deps/fishaudiopp)
target_link_libraries(your_target PRIVATE fishaudiopp)Set your API key:
export FISH_API_KEY=...Each client below is constructed independently — there's no monolithic "do everything" facade. All
accept an optional injected transport (shared_ptr<transport::HttpTransportInterface> /
shared_ptr<transport::WebSocketTransportInterface>), so you can substitute a fake for testing.
#include <fishaudiopp/fishaudiopp.hpp>
fishaudiopp::tts::TtsRestClient tts(apiKey);
fishaudiopp::tts::TtsRequest request;
request.text = "Hello world, this is a test message.";
request.model = fishaudiopp::tts::models::RECOMMENDED;
request.format = fishaudiopp::tts::formats::WAV;
auto audio = tts.bytes(request); // std::vector<uint8_t>See samples/tts-bytes/main.cpp.
fishaudiopp::tts::TtsLiveClient client(apiKey, fishaudiopp::tts::models::S1); // live: s1/s2-pro only
client.setOnAudio([](const fishaudiopp::tts::TtsLiveAudioEvent& event) {
writeAudio(event.audio);
});
client.setOnFinish([](const fishaudiopp::tts::TtsLiveFinishEvent& event) {
// event.reason == "stop" | "error"
});
client.connect();
fishaudiopp::tts::TtsRequest request;
request.format = fishaudiopp::tts::formats::MP3;
client.start(request);
client.sendText("Streamed over a live WebSocket session.");
client.stop();See samples/tts-live/main.cpp.
fishaudiopp::asr::AsrClient asr(apiKey);
auto result = asr.transcribeFileTyped("audio.wav");
std::cout << result.text << " (" << result.duration << "s)\n";
for (const auto& segment : result.segments) {
std::cout << segment.text << " [" << segment.start << "-" << segment.end << "s]\n";
}See samples/asr/main.cpp.
REST responses with HTTP status >= 400 throw fishaudiopp::FishAudioApiException, carrying a
structured ApiErrorDetail (status_code, message) — Fish Audio's error payload is a simple
{"message", "status"} shape. Live-WebSocket protocol failures are delivered as a finish event
with reason == "error" via TtsLiveClient::setOnFinish; transport-level failures (DNS, TLS,
connection loss) go through setOnTransportError instead, since both fire asynchronously off the
transport's receive thread rather than being throwable.
TTS/ASR clients talk to two small interfaces — transport::HttpTransportInterface and
transport::WebSocketTransportInterface — backed by CurlHttpTransport and, depending on
platform, LwsWebSocketTransport (libwebsockets, POSIX) or WinHttpWebSocketTransport (WinHTTP,
Windows). Every client constructor accepts one of these as an optional argument, so you can inject
a fake for testing. See docs/transport.md.
lib/fishaudiopp/
include/fishaudiopp/
types.hpp, api_constants.hpp shared types, constants, FishAudioApiException
tts_client.hpp TtsRequest, TtsRestClient, TtsLiveClient (namespace tts)
asr_client.hpp AsrRequest, AsrResult, AsrClient (namespace asr)
transport/ HttpTransportInterface, WebSocketTransportInterface + backends
fishaudiopp.hpp aggregator header
fishaudiopp_export.hpp generated at configure time (FISHAUDIOPP_EXPORT macro)
src/ client + transport implementations
samples/ one example per client, each its own CMake target
export FISH_API_KEY=...
cd build/samples
./tts-bytes/FishAudioPP_Sample_TTS_Bytes
./tts-live/FishAudioPP_Sample_TTS_Live
./asr/FishAudioPP_Sample_ASR(.exe on Windows, under build\samples\<name>\<Config>\.)
- Voice Design (
/v1/voice-design) and Model management (/modelCRUD — persistent voice cloning) are out of scope for this library (it targets TTS/ASR specifically); the only voice-cloning surface exposed is inline zero-shotTtsRequest::references. asr::AsrClientis REST-only — Fish Audio's public API has no ASR WebSocket to wrap.- The array-of-arrays
referencesvariant for per-speaker zero-shot samples in multi-speaker requests isn't supported — only flat, single-speakerreferences. - No automated test suite yet; the transport interfaces exist partly to make one possible.
- No CMake package config (
find_package(fishaudiopp)) yet — consume viaadd_subdirectory,FetchContent, or the installed headers/library directly.
See LICENSE file for details.