feat: audio speech streaming (#56)

Co-authored-by: yuhongxiao <hongxiao.yu@aminer.cn>
This commit is contained in:
iceAndFireisFailed 2025-09-29 17:08:18 +08:00 committed by GitHub
parent 0ec7aac35f
commit 3120753df3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 79 additions and 0 deletions

View file

@ -39,6 +39,20 @@ public interface AudioApi {
@POST("audio/speech")
Single<ResponseBody> audioSpeech(@Body AudioSpeechRequest request);
/**
* Text-to-Speech (TTS) conversion using GLM-4-Voice Converts text input into
* natural-sounding speech audio with emotion and tone control Supports multiple
* voices, languages, speed adjustment, and various audio formats Features advanced
* voice synthesis with customizable emotional expressions and dialects
* @param request TTS parameters including text, voice selection, emotion, speed,
* tone, and output format
* @return Generated high-quality audio streaming in specified format with natural
* prosody
*/
@Streaming
@POST("audio/speech")
Call<ResponseBody> audioSpeechStreaming(@Body AudioSpeechRequest request);
/**
* Voice cloning and customization using advanced neural models Creates custom voice
* models from provided audio samples with high fidelity Enables personalized speech

View file

@ -12,6 +12,13 @@ public interface AudioService {
*/
AudioSpeechResponse createSpeech(AudioSpeechRequest request);
/**
* Creates speech from text using text-to-speech.
* @param request the speech generation request
* @return AudioSpeechStreamingResponse containing the generated speech streaming
*/
AudioSpeechStreamingResponse createStreamingSpeechStreaming(AudioSpeechRequest request);
/**
* Creates customized speech with specific voice characteristics.
* @param request the speech customization request

View file

@ -6,6 +6,7 @@ import ai.z.openapi.service.deserialize.MessageDeserializeFactory;
import ai.z.openapi.utils.FlowableRequestSupplier;
import ai.z.openapi.utils.RequestSupplier;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.reactivex.rxjava3.core.Single;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
@ -57,6 +58,13 @@ public class AudioServiceImpl implements AudioService {
return this.zAiClient.executeRequest(request, supplier, AudioSpeechResponse.class);
}
@Override
public AudioSpeechStreamingResponse createStreamingSpeechStreaming(AudioSpeechRequest request) {
validateSpeechParams(request);
FlowableRequestSupplier<AudioSpeechRequest, retrofit2.Call<ResponseBody>> supplier = audioApi::audioSpeechStreaming;
return this.zAiClient.streamRequest(request, supplier, AudioSpeechStreamingResponse.class, ObjectNode.class);
}
@Override
public AudioCustomizationResponse createCustomSpeech(AudioCustomizationRequest request) {
validateCustomSpeechParams(request);

View file

@ -0,0 +1,27 @@
package ai.z.openapi.service.audio;
import ai.z.openapi.core.model.ClientResponse;
import ai.z.openapi.core.model.FlowableClientResponse;
import ai.z.openapi.service.model.ChatError;
import java.io.File;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.reactivex.rxjava3.core.Flowable;
import lombok.Data;
@Data
public class AudioSpeechStreamingResponse implements FlowableClientResponse<ObjectNode> {
private int code;
private String msg;
private boolean success;
private ObjectNode data;
private ChatError error;
private Flowable<ObjectNode> flowable;
}

View file

@ -82,6 +82,29 @@ public class AudioServiceTest {
logger.info("Text-to-speech response: {}", mapper.writeValueAsString(response));
}
@Test
@DisplayName("Should generate speech streaming from text successfully")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")
void testAudioSpeechStreaming() {
String requestId = String.format(REQUEST_ID_TEMPLATE, System.currentTimeMillis());
AudioSpeechRequest audioSpeechRequest = AudioSpeechRequest.builder()
.model(Constants.ModelTTS)
.encodeFormat("base64")
.input("Hello, this is a test for text-to-speech functionality.")
.voice("female")
.speed(1.0f)
.volume(1.0f)
.stream(Boolean.TRUE)
.responseFormat("wav")
.requestId(requestId)
.build();
AudioSpeechStreamingResponse audioSpeechStreamingApiResponse = audioService
.createStreamingSpeechStreaming(audioSpeechRequest);
audioSpeechStreamingApiResponse.getFlowable()
.doOnNext(speechPro -> logger.info("speechPro: {}", speechPro.toString()))
.blockingSubscribe();
}
@Test
@DisplayName("Should generate custom speech with voice cloning successfully")
@EnabledIfEnvironmentVariable(named = "ZAI_API_KEY", matches = "^[^.]+\\.[^.]+$")