GitHub Copilot
ChatGPT
Claude
Codex CLI
Cursor
opencode
Skill
Text
azure-ai-voicelive-java
Azure AI VoiceLive SDK for Java. Real-time bidirectional voice conversations with AI assistants using WebSocket. Triggers: "VoiceLiveClient java", "voice assistant java", "real-time voice java", "audio streaming java", "voice activity detection java".
Virus-scanned
Reviewed automatically before listing.
Download
microsoft-skills-.github_plugins_azure-sdk-java_skills_azure-ai-voicelive-java-e58528d.zip · 8 KB
Install
skills CLI
npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-java/skills/azure-ai-voicelive-java
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git
git clone https://github.com/microsoft/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Azure AI VoiceLive SDK for Java
Real-time, bidirectional voice conversations with AI assistants using WebSocket technology.
Installation
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-ai-voicelive</artifactId>
<version>1.0.0-beta.2</version>
</dependency>
Environment Variables
AZURE_VOICELIVE_ENDPOINT=https://<resource>.openai.azure.com/ # Required for all auth methods
AZURE_VOICELIVE_API_KEY=<your-api-key> # Only required for AzureKeyCredential auth
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
Authentication
API Key
import com.azure.ai.voicelive.VoiceLiveAsyncClient;
import com.azure.ai.voicelive.VoiceLiveClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
VoiceLiveAsyncClient client = new VoiceLiveClientBuilder()
.endpoint(System.getenv("AZURE_VOICELIVE_ENDPOINT"))
.credential(new AzureKeyCredential(System.getenv("AZURE_VOICELIVE_API_KEY")))
.buildAsyncClient();
DefaultAzureCredential (Recommended)
import com.azure.core.credential.TokenCredential;
import com.azure.identity.AzureIdentityEnvVars;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.ManagedIdentityCredentialBuilder;
TokenCredential credential = new DefaultAzureCredentialBuilder()
.requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS)
.build();
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes
// TokenCredential credential = new ManagedIdentityCredentialBuilder().build();
VoiceLiveAsyncClient client = new VoiceLiveClientBuilder()
.endpoint(System.getenv("AZURE_VOICELIVE_ENDPOINT"))
.credential(credential)
.buildAsyncClient();
Key Concepts
| Concept | Description |
|---|---|
VoiceLiveAsyncClient |
Main entry point for voice sessions |
VoiceLiveSessionAsyncClient |
Active WebSocket connection for streaming |
VoiceLiveSessionOptions |
Configuration for session behavior |
Audio Requirements
- Sample Rate: 24kHz (24000 Hz)
- Bit Depth: 16-bit PCM
- Channels: Mono (1 channel)
- Format: Signed PCM, little-endian
Core Workflow
1. Start Session
import reactor.core.publisher.Mono;
client.startSession("gpt-4o-realtime-preview")
.flatMap(session -> {
System.out.println("Session started");
// Subscribe to events
session.receiveEvents()
.subscribe(
event -> System.out.println("Event: " + event.getType()),
error -> System.err.println("Error: " + error.getMessage())
);
return Mono.just(session);
})
.block();
2. Configure Session Options
import com.azure.ai.voicelive.models.*;
import java.util.Arrays;
ServerVadTurnDetection turnDetection = new ServerVadTurnDetection()
.setThreshold(0.5) // Sensitivity (0.0-1.0)
.setPrefixPaddingMs(300) // Audio before speech
.setSilenceDurationMs(500) // Silence to end turn
.setInterruptResponse(true) // Allow interruptions
.setAutoTruncate(true)
.setCreateResponse(true);
AudioInputTranscriptionOptions transcription = new AudioInputTranscriptionOptions(
AudioInputTranscriptionOptionsModel.WHISPER_1);
VoiceLiveSessionOptions options = new VoiceLiveSessionOptions()
.setInstructions("You are a helpful AI voice assistant.")
.setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY)))
.setModalities(Arrays.asList(InteractionModality.TEXT, InteractionModality.AUDIO))
.setInputAudioFormat(InputAudioFormat.PCM16)
.setOutputAudioFormat(OutputAudioFormat.PCM16)
.setInputAudioSamplingRate(24000)
.setInputAudioNoiseReduction(new AudioNoiseReduction(AudioNoiseReductionType.NEAR_FIELD))
.setInputAudioEchoCancellation(new AudioEchoCancellation())
.setInputAudioTranscription(transcription)
.setTurnDetection(turnDetection);
// Send configuration
ClientEventSessionUpdate updateEvent = new ClientEventSessionUpdate(options);
session.sendEvent(updateEvent).subscribe();
3. Send Audio Input
byte[] audioData = readAudioChunk(); // Your PCM16 audio data
session.sendInputAudio(BinaryData.fromBytes(audioData)).subscribe();
4. Handle Events
session.receiveEvents().subscribe(event -> {
ServerEventType eventType = event.getType();
if (ServerEventType.SESSION_CREATED.equals(eventType)) {
System.out.println("Session created");
} else if (ServerEventType.INPUT_AUDIO_BUFFER_SPEECH_STARTED.equals(eventType)) {
System.out.println("User started speaking");
} else if (ServerEventType.INPUT_AUDIO_BUFFER_SPEECH_STOPPED.equals(eventType)) {
System.out.println("User stopped speaking");
} else if (ServerEventType.RESPONSE_AUDIO_DELTA.equals(eventType)) {
if (event instanceof SessionUpdateResponseAudioDelta) {
SessionUpdateResponseAudioDelta audioEvent = (SessionUpdateResponseAudioDelta) event;
playAudioChunk(audioEvent.getDelta());
}
} else if (ServerEventType.RESPONSE_DONE.equals(eventType)) {
System.out.println("Response complete");
} else if (ServerEventType.ERROR.equals(eventType)) {
if (event instanceof SessionUpdateError) {
SessionUpdateError errorEvent = (SessionUpdateError) event;
System.err.println("Error: " + errorEvent.getError().getMessage());
}
}
});
Voice Configuration
OpenAI Voices
// Available: ALLOY, ASH, BALLAD, CORAL, ECHO, SAGE, SHIMMER, VERSE
VoiceLiveSessionOptions options = new VoiceLiveSessionOptions()
.setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY)));
Azure Voices
// Azure Standard Voice
options.setVoice(BinaryData.fromObject(new AzureStandardVoice("en-US-JennyNeural")));
// Azure Custom Voice
options.setVoice(BinaryData.fromObject(new AzureCustomVoice("myVoice", "endpointId")));
// Azure Personal Voice
options.setVoice(BinaryData.fromObject(
new AzurePersonalVoice("speakerProfileId", PersonalVoiceModels.PHOENIX_LATEST_NEURAL)));
Function Calling
VoiceLiveFunctionDefinition weatherFunction = new VoiceLiveFunctionDefinition("get_weather")
.setDescription("Get current weather for a location")
.setParameters(BinaryData.fromObject(parametersSchema));
VoiceLiveSessionOptions options = new VoiceLiveSessionOptions()
.setTools(Arrays.asList(weatherFunction))
.setInstructions("You have access to weather information.");
Best Practices
- Use async client — VoiceLive requires reactive patterns
- Configure turn detection for natural conversation flow
- Enable noise reduction for better speech recognition
- Handle interruptions gracefully with
setInterruptResponse(true) - Use Whisper transcription for input audio transcription
- Close sessions properly when conversation ends
Error Handling
session.receiveEvents()
.doOnError(error -> System.err.println("Connection error: " + error.getMessage()))
.onErrorResume(error -> {
// Attempt reconnection or cleanup
return Flux.empty();
})
.subscribe();
Reference Links
Files (skills)
-
references
-
examples.md 24.3 KB
# Azure AI VoiceLive Java SDK - Examples Comprehensive code examples for the Azure AI VoiceLive SDK for Java. ## Table of Contents - [Maven Dependency](#maven-dependency) - [Client Creation](#client-creation) - [Session Management](#session-management) - [Session Configuration](#session-configuration) - [Audio Streaming](#audio-streaming) - [Event Handling](#event-handling) - [Voice Configuration](#voice-configuration) - [Function Calling](#function-calling) - [Error Handling](#error-handling) - [Complete Application Example](#complete-application-example) ## Maven Dependency ```xml <dependency> <groupId>com.azure</groupId> <artifactId>azure-ai-voicelive</artifactId> <version>1.0.0-beta.2</version> </dependency> <!-- For DefaultAzureCredential --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-identity</artifactId> <version>1.14.2</version> </dependency> <!-- Reactor for reactive streams --> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.6.0</version> </dependency> ``` ## Client Creation ### With API Key ```java import com.azure.ai.voicelive.VoiceLiveAsyncClient; import com.azure.ai.voicelive.VoiceLiveClientBuilder; import com.azure.core.credential.AzureKeyCredential; String endpoint = System.getenv("AZURE_VOICELIVE_ENDPOINT"); String key = System.getenv("AZURE_VOICELIVE_API_KEY"); VoiceLiveAsyncClient client = new VoiceLiveClientBuilder() .endpoint(endpoint) .credential(new AzureKeyCredential(key)) .buildAsyncClient(); ``` ### With DefaultAzureCredential (Recommended) ```java import com.azure.identity.DefaultAzureCredentialBuilder; VoiceLiveAsyncClient client = new VoiceLiveClientBuilder() .endpoint(System.getenv("AZURE_VOICELIVE_ENDPOINT")) .credential(new DefaultAzureCredentialBuilder().build()) .buildAsyncClient(); ``` ## Session Management ### Start Session ```java import com.azure.ai.voicelive.VoiceLiveSessionAsyncClient; import reactor.core.publisher.Mono; String modelDeployment = "gpt-4o-realtime-preview"; client.startSession(modelDeployment) .flatMap(session -> { System.out.println("Session started successfully"); // Subscribe to events session.receiveEvents() .subscribe( event -> handleEvent(event), error -> System.err.println("Error: " + error.getMessage()), () -> System.out.println("Session ended") ); return Mono.just(session); }) .subscribe(); ``` ### Basic Session Flow ```java client.startSession("gpt-4o-realtime-preview") .flatMap(session -> { // 1. Configure session return configureSession(session) .then(Mono.just(session)); }) .flatMap(session -> { // 2. Start receiving events session.receiveEvents() .subscribe(event -> processEvent(event)); // 3. Send audio return sendAudioStream(session); }) .subscribe( result -> System.out.println("Audio sent"), error -> System.err.println("Error: " + error.getMessage()) ); ``` ## Session Configuration ### Configure with Turn Detection ```java import com.azure.ai.voicelive.models.*; import com.azure.core.util.BinaryData; import java.util.Arrays; // Server-side Voice Activity Detection (VAD) ServerVadTurnDetection turnDetection = new ServerVadTurnDetection() .setThreshold(0.5) // Sensitivity (0.0-1.0) .setPrefixPaddingMs(300) // Audio to keep before speech .setSilenceDurationMs(500) // Silence to end turn .setInterruptResponse(true) // Allow user interruptions .setAutoTruncate(true) .setCreateResponse(true); // Input transcription AudioInputTranscriptionOptions transcription = new AudioInputTranscriptionOptions( AudioInputTranscriptionOptionsModel.WHISPER_1); // Session options VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setInstructions("You are a helpful AI voice assistant. Be concise and friendly.") .setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY))) .setModalities(Arrays.asList(InteractionModality.TEXT, InteractionModality.AUDIO)) .setInputAudioFormat(InputAudioFormat.PCM16) .setOutputAudioFormat(OutputAudioFormat.PCM16) .setInputAudioSamplingRate(24000) .setInputAudioNoiseReduction(new AudioNoiseReduction(AudioNoiseReductionType.NEAR_FIELD)) .setInputAudioEchoCancellation(new AudioEchoCancellation()) .setInputAudioTranscription(transcription) .setTurnDetection(turnDetection); // Send configuration ClientEventSessionUpdate updateEvent = new ClientEventSessionUpdate(options); session.sendEvent(updateEvent).subscribe(); ``` ### Configure for Text-Only Response ```java VoiceLiveSessionOptions textOnlyOptions = new VoiceLiveSessionOptions() .setInstructions("You are a helpful assistant.") .setModalities(Arrays.asList(InteractionModality.TEXT)) // Text only .setInputAudioFormat(InputAudioFormat.PCM16) .setInputAudioSamplingRate(24000) .setTurnDetection(new ServerVadTurnDetection() .setCreateResponse(true)); session.sendEvent(new ClientEventSessionUpdate(textOnlyOptions)).subscribe(); ``` ## Audio Streaming ### Audio Requirements - **Sample Rate**: 24kHz (24000 Hz) - **Bit Depth**: 16-bit PCM - **Channels**: Mono (1 channel) - **Format**: Signed PCM, little-endian ### Send Audio Input ```java import com.azure.core.util.BinaryData; // Read audio from microphone or file (PCM16 format) byte[] audioData = readAudioChunk(); // Your audio data // Send audio to session session.sendInputAudio(BinaryData.fromBytes(audioData)) .subscribe( () -> System.out.println("Audio sent"), error -> System.err.println("Error sending audio: " + error.getMessage()) ); ``` ### Stream Audio from Microphone ```java import javax.sound.sampled.*; import java.util.concurrent.atomic.AtomicBoolean; public void streamFromMicrophone(VoiceLiveSessionAsyncClient session) { AtomicBoolean recording = new AtomicBoolean(true); // Audio format: 24kHz, 16-bit, mono, signed, little-endian AudioFormat format = new AudioFormat(24000, 16, 1, true, false); try { DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); TargetDataLine microphone = (TargetDataLine) AudioSystem.getLine(info); microphone.open(format); microphone.start(); // Stream in chunks byte[] buffer = new byte[4800]; // 100ms of audio at 24kHz Thread audioThread = new Thread(() -> { while (recording.get()) { int bytesRead = microphone.read(buffer, 0, buffer.length); if (bytesRead > 0) { byte[] audioChunk = new byte[bytesRead]; System.arraycopy(buffer, 0, audioChunk, 0, bytesRead); session.sendInputAudio(BinaryData.fromBytes(audioChunk)) .subscribe(); } } microphone.close(); }); audioThread.start(); // Stop after some time or user action Thread.sleep(30000); recording.set(false); } catch (Exception e) { System.err.println("Error streaming audio: " + e.getMessage()); } } ``` ### Commit Audio Buffer Signal end of user input: ```java session.sendEvent(new ClientEventInputAudioBufferCommit()) .subscribe(); ``` ### Clear Audio Buffer Cancel current input: ```java session.sendEvent(new ClientEventInputAudioBufferClear()) .subscribe(); ``` ## Event Handling ### Handle All Event Types ```java import com.azure.ai.voicelive.models.*; private void handleEvent(ServerEvent event) { ServerEventType eventType = event.getType(); switch (eventType) { case SESSION_CREATED: handleSessionCreated((SessionCreatedEvent) event); break; case SESSION_UPDATED: System.out.println("Session configuration updated"); break; case INPUT_AUDIO_BUFFER_SPEECH_STARTED: System.out.println("User started speaking"); break; case INPUT_AUDIO_BUFFER_SPEECH_STOPPED: System.out.println("User stopped speaking"); break; case INPUT_AUDIO_BUFFER_COMMITTED: System.out.println("Audio buffer committed"); break; case CONVERSATION_ITEM_CREATED: handleConversationItemCreated(event); break; case RESPONSE_CREATED: System.out.println("Response generation started"); break; case RESPONSE_AUDIO_DELTA: handleAudioDelta(event); break; case RESPONSE_AUDIO_TRANSCRIPT_DELTA: handleTranscriptDelta(event); break; case RESPONSE_TEXT_DELTA: handleTextDelta(event); break; case RESPONSE_DONE: System.out.println("Response complete"); break; case ERROR: handleError(event); break; default: System.out.println("Event: " + eventType); } } private void handleSessionCreated(SessionCreatedEvent event) { System.out.println("Session created: " + event.getSession().getId()); } private void handleAudioDelta(ServerEvent event) { if (event instanceof ResponseAudioDeltaEvent) { ResponseAudioDeltaEvent audioEvent = (ResponseAudioDeltaEvent) event; byte[] audioData = audioEvent.getDelta(); // Play audio through speakers playAudio(audioData); } } private void handleTranscriptDelta(ServerEvent event) { if (event instanceof ResponseAudioTranscriptDeltaEvent) { ResponseAudioTranscriptDeltaEvent transcriptEvent = (ResponseAudioTranscriptDeltaEvent) event; System.out.print(transcriptEvent.getDelta()); // Print as it arrives } } private void handleTextDelta(ServerEvent event) { if (event instanceof ResponseTextDeltaEvent) { ResponseTextDeltaEvent textEvent = (ResponseTextDeltaEvent) event; System.out.print(textEvent.getDelta()); } } private void handleError(ServerEvent event) { if (event instanceof ErrorEvent) { ErrorEvent errorEvent = (ErrorEvent) event; System.err.println("Error: " + errorEvent.getError().getMessage()); } } ``` ### Play Audio Response ```java import javax.sound.sampled.*; private SourceDataLine speakerLine; private void initializeSpeaker() throws LineUnavailableException { AudioFormat format = new AudioFormat(24000, 16, 1, true, false); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); speakerLine = (SourceDataLine) AudioSystem.getLine(info); speakerLine.open(format); speakerLine.start(); } private void playAudio(byte[] audioData) { if (speakerLine != null) { speakerLine.write(audioData, 0, audioData.length); } } private void closeSpeaker() { if (speakerLine != null) { speakerLine.drain(); speakerLine.close(); } } ``` ## Voice Configuration ### OpenAI Voices ```java // Available voices: ALLOY, ASH, BALLAD, CORAL, ECHO, SAGE, SHIMMER, VERSE VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY))); // Other voices // OpenAIVoiceName.ASH - Warm, friendly // OpenAIVoiceName.BALLAD - Expressive, dramatic // OpenAIVoiceName.CORAL - Clear, professional // OpenAIVoiceName.ECHO - Soft, calming // OpenAIVoiceName.SAGE - Wise, measured // OpenAIVoiceName.SHIMMER - Bright, energetic // OpenAIVoiceName.VERSE - Neutral, versatile ``` ### Azure Neural Voices ```java // Azure Standard Voice AzureStandardVoice standardVoice = new AzureStandardVoice("en-US-JennyNeural"); options.setVoice(BinaryData.fromObject(standardVoice)); // Azure Custom Voice AzureCustomVoice customVoice = new AzureCustomVoice("myCustomVoice", "endpointId"); options.setVoice(BinaryData.fromObject(customVoice)); // Azure Personal Voice AzurePersonalVoice personalVoice = new AzurePersonalVoice( "speakerProfileId", PersonalVoiceModels.PHOENIX_LATEST_NEURAL ); options.setVoice(BinaryData.fromObject(personalVoice)); ``` ## Function Calling ### Define Functions ```java import java.util.Map; // Define function schema Map<String, Object> weatherParams = Map.of( "type", "object", "properties", Map.of( "location", Map.of( "type", "string", "description", "City name, e.g., San Francisco" ), "unit", Map.of( "type", "string", "enum", new String[]{"celsius", "fahrenheit"}, "description", "Temperature unit" ) ), "required", new String[]{"location"} ); VoiceLiveFunctionDefinition weatherFunction = new VoiceLiveFunctionDefinition("get_weather") .setDescription("Get current weather for a location") .setParameters(BinaryData.fromObject(weatherParams)); // Add to session options VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setInstructions("You are a helpful assistant with access to weather information.") .setTools(Arrays.asList(weatherFunction)); ``` ### Handle Function Calls ```java private void handleFunctionCall(ServerEvent event) { if (event instanceof ResponseFunctionCallArgumentsDoneEvent) { ResponseFunctionCallArgumentsDoneEvent funcEvent = (ResponseFunctionCallArgumentsDoneEvent) event; String functionName = funcEvent.getName(); String arguments = funcEvent.getArguments(); String callId = funcEvent.getCallId(); System.out.println("Function call: " + functionName); System.out.println("Arguments: " + arguments); // Execute function and get result String result = executeFunction(functionName, arguments); // Send result back ConversationItemFunctionCallOutput output = new ConversationItemFunctionCallOutput( callId, result ); session.sendEvent(new ClientEventConversationItemCreate(output)) .then(session.sendEvent(new ClientEventResponseCreate())) .subscribe(); } } private String executeFunction(String name, String arguments) { if ("get_weather".equals(name)) { // Parse arguments and call weather API return "{\"temperature\": 72, \"condition\": \"sunny\"}"; } return "{\"error\": \"Unknown function\"}"; } ``` ## Error Handling ### Handle Connection Errors ```java session.receiveEvents() .doOnError(error -> { System.err.println("Connection error: " + error.getMessage()); if (error instanceof java.net.SocketException) { System.err.println("Network disconnected - attempting reconnect"); reconnect(); } }) .onErrorResume(error -> { // Log and continue or terminate return Flux.empty(); }) .subscribe(event -> handleEvent(event)); ``` ### Handle API Errors ```java private void handleError(ServerEvent event) { if (event instanceof ErrorEvent) { ErrorEvent errorEvent = (ErrorEvent) event; ErrorDetails error = errorEvent.getError(); System.err.println("Error type: " + error.getType()); System.err.println("Error message: " + error.getMessage()); System.err.println("Error code: " + error.getCode()); // Handle specific errors String errorType = error.getType(); if ("invalid_request_error".equals(errorType)) { System.err.println("Invalid request - check parameters"); } else if ("rate_limit_error".equals(errorType)) { System.err.println("Rate limited - slow down requests"); } else if ("server_error".equals(errorType)) { System.err.println("Server error - retry later"); } } } ``` ## Complete Application Example ```java import com.azure.ai.voicelive.VoiceLiveAsyncClient; import com.azure.ai.voicelive.VoiceLiveClientBuilder; import com.azure.ai.voicelive.VoiceLiveSessionAsyncClient; import com.azure.ai.voicelive.models.*; import com.azure.core.util.BinaryData; import com.azure.identity.DefaultAzureCredentialBuilder; import reactor.core.Disposable; import javax.sound.sampled.*; import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; public class VoiceAssistant { private final VoiceLiveAsyncClient client; private VoiceLiveSessionAsyncClient session; private SourceDataLine speakerLine; private TargetDataLine microphoneLine; private AtomicBoolean running = new AtomicBoolean(false); private CountDownLatch sessionLatch; public VoiceAssistant() { this.client = new VoiceLiveClientBuilder() .endpoint(System.getenv("AZURE_VOICELIVE_ENDPOINT")) .credential(new DefaultAzureCredentialBuilder().build()) .buildAsyncClient(); } public void start() throws Exception { sessionLatch = new CountDownLatch(1); running.set(true); // Initialize audio initializeAudio(); // Start session client.startSession("gpt-4o-realtime-preview") .doOnNext(s -> { this.session = s; System.out.println("Session started"); }) .flatMap(this::configureSession) .subscribe( session -> { // Start receiving events session.receiveEvents() .subscribe( this::handleEvent, error -> { System.err.println("Error: " + error.getMessage()); stop(); }, () -> { System.out.println("Session ended"); sessionLatch.countDown(); } ); // Start microphone streaming startMicrophoneStream(); }, error -> { System.err.println("Failed to start session: " + error.getMessage()); sessionLatch.countDown(); } ); System.out.println("Voice assistant started. Speak into the microphone..."); System.out.println("Press Enter to stop."); // Wait for user to press Enter System.in.read(); stop(); sessionLatch.await(); } private reactor.core.publisher.Mono<VoiceLiveSessionAsyncClient> configureSession( VoiceLiveSessionAsyncClient session) { ServerVadTurnDetection turnDetection = new ServerVadTurnDetection() .setThreshold(0.5) .setPrefixPaddingMs(300) .setSilenceDurationMs(500) .setInterruptResponse(true) .setAutoTruncate(true) .setCreateResponse(true); VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setInstructions("You are a helpful voice assistant. Keep responses concise.") .setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY))) .setModalities(Arrays.asList(InteractionModality.TEXT, InteractionModality.AUDIO)) .setInputAudioFormat(InputAudioFormat.PCM16) .setOutputAudioFormat(OutputAudioFormat.PCM16) .setInputAudioSamplingRate(24000) .setInputAudioTranscription(new AudioInputTranscriptionOptions( AudioInputTranscriptionOptionsModel.WHISPER_1)) .setTurnDetection(turnDetection); return session.sendEvent(new ClientEventSessionUpdate(options)) .thenReturn(session); } private void initializeAudio() throws LineUnavailableException { AudioFormat format = new AudioFormat(24000, 16, 1, true, false); // Speaker DataLine.Info speakerInfo = new DataLine.Info(SourceDataLine.class, format); speakerLine = (SourceDataLine) AudioSystem.getLine(speakerInfo); speakerLine.open(format); speakerLine.start(); // Microphone DataLine.Info micInfo = new DataLine.Info(TargetDataLine.class, format); microphoneLine = (TargetDataLine) AudioSystem.getLine(micInfo); microphoneLine.open(format); microphoneLine.start(); } private void startMicrophoneStream() { Thread micThread = new Thread(() -> { byte[] buffer = new byte[4800]; // 100ms chunks while (running.get()) { int bytesRead = microphoneLine.read(buffer, 0, buffer.length); if (bytesRead > 0 && session != null) { byte[] chunk = new byte[bytesRead]; System.arraycopy(buffer, 0, chunk, 0, bytesRead); session.sendInputAudio(BinaryData.fromBytes(chunk)).subscribe(); } } }); micThread.setDaemon(true); micThread.start(); } private void handleEvent(ServerEvent event) { switch (event.getType()) { case INPUT_AUDIO_BUFFER_SPEECH_STARTED: System.out.println("\n[You are speaking...]"); break; case INPUT_AUDIO_BUFFER_SPEECH_STOPPED: System.out.println("[Processing...]"); break; case RESPONSE_AUDIO_DELTA: if (event instanceof ResponseAudioDeltaEvent) { byte[] audio = ((ResponseAudioDeltaEvent) event).getDelta(); speakerLine.write(audio, 0, audio.length); } break; case RESPONSE_AUDIO_TRANSCRIPT_DELTA: if (event instanceof ResponseAudioTranscriptDeltaEvent) { System.out.print(((ResponseAudioTranscriptDeltaEvent) event).getDelta()); } break; case RESPONSE_DONE: System.out.println("\n"); break; case ERROR: if (event instanceof ErrorEvent) { System.err.println("Error: " + ((ErrorEvent) event).getError().getMessage()); } break; default: // Ignore other events break; } } public void stop() { running.set(false); if (microphoneLine != null) { microphoneLine.stop(); microphoneLine.close(); } if (speakerLine != null) { speakerLine.drain(); speakerLine.stop(); speakerLine.close(); } System.out.println("Voice assistant stopped"); } public static void main(String[] args) { try { VoiceAssistant assistant = new VoiceAssistant(); assistant.start(); } catch (Exception e) { System.err.println("Error: " + e.getMessage()); e.printStackTrace(); } } } ``` ## Environment Variables ```bash AZURE_VOICELIVE_ENDPOINT=https://<resource>.openai.azure.com/ AZURE_VOICELIVE_API_KEY=<your-api-key> # For DefaultAzureCredential AZURE_CLIENT_ID=<service-principal-client-id> AZURE_CLIENT_SECRET=<service-principal-secret> AZURE_TENANT_ID=<tenant-id> ``` ## Best Practices 1. **Use async client** — VoiceLive requires reactive patterns for real-time streaming 2. **Configure turn detection** — Tune VAD settings for natural conversation flow 3. **Enable noise reduction** — Use `AudioNoiseReduction` for better speech recognition 4. **Handle interruptions** — Set `setInterruptResponse(true)` for natural conversations 5. **Use Whisper transcription** — Enable for input audio transcription 6. **Close sessions properly** — Clean up resources when conversation ends 7. **Buffer audio appropriately** — Send audio in ~100ms chunks for optimal latency 8. **Implement error recovery** — Handle connection drops with reconnection logic
-
-
SKILL.md 7.9 KB
--- name: azure-ai-voicelive-java description: | Azure AI VoiceLive SDK for Java. Real-time bidirectional voice conversations with AI assistants using WebSocket. Triggers: "VoiceLiveClient java", "voice assistant java", "real-time voice java", "audio streaming java", "voice activity detection java". license: MIT metadata: author: Microsoft version: "1.0.0" package: com.azure:azure-ai-voicelive --- # Azure AI VoiceLive SDK for Java Real-time, bidirectional voice conversations with AI assistants using WebSocket technology. ## Installation ```xml <dependency> <groupId>com.azure</groupId> <artifactId>azure-ai-voicelive</artifactId> <version>1.0.0-beta.2</version> </dependency> ``` ## Environment Variables ```bash AZURE_VOICELIVE_ENDPOINT=https://<resource>.openai.azure.com/ # Required for all auth methods AZURE_VOICELIVE_API_KEY=<your-api-key> # Only required for AzureKeyCredential auth AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production ``` ## Authentication ### API Key ```java import com.azure.ai.voicelive.VoiceLiveAsyncClient; import com.azure.ai.voicelive.VoiceLiveClientBuilder; import com.azure.core.credential.AzureKeyCredential; VoiceLiveAsyncClient client = new VoiceLiveClientBuilder() .endpoint(System.getenv("AZURE_VOICELIVE_ENDPOINT")) .credential(new AzureKeyCredential(System.getenv("AZURE_VOICELIVE_API_KEY"))) .buildAsyncClient(); ``` ### DefaultAzureCredential (Recommended) ```java import com.azure.core.credential.TokenCredential; import com.azure.identity.AzureIdentityEnvVars; import com.azure.identity.DefaultAzureCredentialBuilder; import com.azure.identity.ManagedIdentityCredentialBuilder; TokenCredential credential = new DefaultAzureCredentialBuilder() .requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS) .build(); // Or use a specific credential directly in production: // See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes // TokenCredential credential = new ManagedIdentityCredentialBuilder().build(); VoiceLiveAsyncClient client = new VoiceLiveClientBuilder() .endpoint(System.getenv("AZURE_VOICELIVE_ENDPOINT")) .credential(credential) .buildAsyncClient(); ``` ## Key Concepts | Concept | Description | |---------|-------------| | `VoiceLiveAsyncClient` | Main entry point for voice sessions | | `VoiceLiveSessionAsyncClient` | Active WebSocket connection for streaming | | `VoiceLiveSessionOptions` | Configuration for session behavior | ### Audio Requirements - **Sample Rate**: 24kHz (24000 Hz) - **Bit Depth**: 16-bit PCM - **Channels**: Mono (1 channel) - **Format**: Signed PCM, little-endian ## Core Workflow ### 1. Start Session ```java import reactor.core.publisher.Mono; client.startSession("gpt-4o-realtime-preview") .flatMap(session -> { System.out.println("Session started"); // Subscribe to events session.receiveEvents() .subscribe( event -> System.out.println("Event: " + event.getType()), error -> System.err.println("Error: " + error.getMessage()) ); return Mono.just(session); }) .block(); ``` ### 2. Configure Session Options ```java import com.azure.ai.voicelive.models.*; import java.util.Arrays; ServerVadTurnDetection turnDetection = new ServerVadTurnDetection() .setThreshold(0.5) // Sensitivity (0.0-1.0) .setPrefixPaddingMs(300) // Audio before speech .setSilenceDurationMs(500) // Silence to end turn .setInterruptResponse(true) // Allow interruptions .setAutoTruncate(true) .setCreateResponse(true); AudioInputTranscriptionOptions transcription = new AudioInputTranscriptionOptions( AudioInputTranscriptionOptionsModel.WHISPER_1); VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setInstructions("You are a helpful AI voice assistant.") .setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY))) .setModalities(Arrays.asList(InteractionModality.TEXT, InteractionModality.AUDIO)) .setInputAudioFormat(InputAudioFormat.PCM16) .setOutputAudioFormat(OutputAudioFormat.PCM16) .setInputAudioSamplingRate(24000) .setInputAudioNoiseReduction(new AudioNoiseReduction(AudioNoiseReductionType.NEAR_FIELD)) .setInputAudioEchoCancellation(new AudioEchoCancellation()) .setInputAudioTranscription(transcription) .setTurnDetection(turnDetection); // Send configuration ClientEventSessionUpdate updateEvent = new ClientEventSessionUpdate(options); session.sendEvent(updateEvent).subscribe(); ``` ### 3. Send Audio Input ```java byte[] audioData = readAudioChunk(); // Your PCM16 audio data session.sendInputAudio(BinaryData.fromBytes(audioData)).subscribe(); ``` ### 4. Handle Events ```java session.receiveEvents().subscribe(event -> { ServerEventType eventType = event.getType(); if (ServerEventType.SESSION_CREATED.equals(eventType)) { System.out.println("Session created"); } else if (ServerEventType.INPUT_AUDIO_BUFFER_SPEECH_STARTED.equals(eventType)) { System.out.println("User started speaking"); } else if (ServerEventType.INPUT_AUDIO_BUFFER_SPEECH_STOPPED.equals(eventType)) { System.out.println("User stopped speaking"); } else if (ServerEventType.RESPONSE_AUDIO_DELTA.equals(eventType)) { if (event instanceof SessionUpdateResponseAudioDelta) { SessionUpdateResponseAudioDelta audioEvent = (SessionUpdateResponseAudioDelta) event; playAudioChunk(audioEvent.getDelta()); } } else if (ServerEventType.RESPONSE_DONE.equals(eventType)) { System.out.println("Response complete"); } else if (ServerEventType.ERROR.equals(eventType)) { if (event instanceof SessionUpdateError) { SessionUpdateError errorEvent = (SessionUpdateError) event; System.err.println("Error: " + errorEvent.getError().getMessage()); } } }); ``` ## Voice Configuration ### OpenAI Voices ```java // Available: ALLOY, ASH, BALLAD, CORAL, ECHO, SAGE, SHIMMER, VERSE VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setVoice(BinaryData.fromObject(new OpenAIVoice(OpenAIVoiceName.ALLOY))); ``` ### Azure Voices ```java // Azure Standard Voice options.setVoice(BinaryData.fromObject(new AzureStandardVoice("en-US-JennyNeural"))); // Azure Custom Voice options.setVoice(BinaryData.fromObject(new AzureCustomVoice("myVoice", "endpointId"))); // Azure Personal Voice options.setVoice(BinaryData.fromObject( new AzurePersonalVoice("speakerProfileId", PersonalVoiceModels.PHOENIX_LATEST_NEURAL))); ``` ## Function Calling ```java VoiceLiveFunctionDefinition weatherFunction = new VoiceLiveFunctionDefinition("get_weather") .setDescription("Get current weather for a location") .setParameters(BinaryData.fromObject(parametersSchema)); VoiceLiveSessionOptions options = new VoiceLiveSessionOptions() .setTools(Arrays.asList(weatherFunction)) .setInstructions("You have access to weather information."); ``` ## Best Practices 1. **Use async client** — VoiceLive requires reactive patterns 2. **Configure turn detection** for natural conversation flow 3. **Enable noise reduction** for better speech recognition 4. **Handle interruptions** gracefully with `setInterruptResponse(true)` 5. **Use Whisper transcription** for input audio transcription 6. **Close sessions** properly when conversation ends ## Error Handling ```java session.receiveEvents() .doOnError(error -> System.err.println("Connection error: " + error.getMessage())) .onErrorResume(error -> { // Attempt reconnection or cleanup return Flux.empty(); }) .subscribe(); ``` ## Reference Links | Resource | URL | |----------|-----| | GitHub Source | https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/ai/azure-ai-voicelive | | Samples | https://github.com/Azure/azure-sdk-for-java/tree/main/sdk/ai/azure-ai-voicelive/src/samples |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.