diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index cb8983895cfe..953efb46402d 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -898,6 +898,12 @@
# ServiceLabel: %VideoAnalyzer
# ServiceOwners: @giakas
+# PRLabel: %VoiceLive
+/sdk/ai/Azure.AI.VoiceLive/ @rhurey
+
+# ServiceLabel: %VoiceLive
+# ServiceOwners: @rhurey
+
# ServiceLabel: %Web Apps
# ServiceOwners: @AzureAppServiceCLI @antcp
diff --git a/sdk/ai/Azure.AI.VoiceLive/Azure.AI.VoiceLive.sln b/sdk/ai/Azure.AI.VoiceLive/Azure.AI.VoiceLive.sln
new file mode 100644
index 000000000000..852040ccbe22
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/Azure.AI.VoiceLive.sln
@@ -0,0 +1,54 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36301.6
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Core.TestFramework", "..\..\core\Azure.Core.TestFramework\src\Azure.Core.TestFramework.csproj", "{ECC730C1-4AEA-420C-916A-66B19B79E4DC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.AI.VoiceLive", "src\Azure.AI.VoiceLive.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.AI.VoiceLive.Tests", "tests\Azure.AI.VoiceLive.Tests.csproj", "{1F1CD1D4-9932-4B73-99D8-C252A67D4B46}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicVoiceAssistant", "samples\BasicVoiceAssistant\BasicVoiceAssistant.csproj", "{4F423188-2AE3-CB57-5BE2-808B33B8B5AB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomerServiceBot", "samples\CustomerServiceBot\CustomerServiceBot.csproj", "{0821FA24-C459-5CC2-DB1B-2F755A9B3148}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Azure.AI.VoiceLive.Snippets", "samples\snippets\Azure.AI.VoiceLive.Snippets.csproj", "{1694BF5F-7AE7-9D41-44E7-A50680B5CA40}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {ECC730C1-4AEA-420C-916A-66B19B79E4DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ECC730C1-4AEA-420C-916A-66B19B79E4DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ECC730C1-4AEA-420C-916A-66B19B79E4DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ECC730C1-4AEA-420C-916A-66B19B79E4DC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {28FF4005-4467-4E36-92E7-DEA27DEB1519}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F1CD1D4-9932-4B73-99D8-C252A67D4B46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4F423188-2AE3-CB57-5BE2-808B33B8B5AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4F423188-2AE3-CB57-5BE2-808B33B8B5AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4F423188-2AE3-CB57-5BE2-808B33B8B5AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4F423188-2AE3-CB57-5BE2-808B33B8B5AB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0821FA24-C459-5CC2-DB1B-2F755A9B3148}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0821FA24-C459-5CC2-DB1B-2F755A9B3148}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0821FA24-C459-5CC2-DB1B-2F755A9B3148}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0821FA24-C459-5CC2-DB1B-2F755A9B3148}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1694BF5F-7AE7-9D41-44E7-A50680B5CA40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1694BF5F-7AE7-9D41-44E7-A50680B5CA40}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1694BF5F-7AE7-9D41-44E7-A50680B5CA40}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1694BF5F-7AE7-9D41-44E7-A50680B5CA40}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {A97F4B90-2591-4689-B1F8-5F21FE6D6CAE}
+ EndGlobalSection
+EndGlobal
diff --git a/sdk/ai/Azure.AI.VoiceLive/CHANGELOG.md b/sdk/ai/Azure.AI.VoiceLive/CHANGELOG.md
new file mode 100644
index 000000000000..d309e9bab1b7
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Release History
+
+## 1.0.0-beta.1 (Unreleased)
+
+### Features Added
+Initial Addition of VoiceLiveClient and associated classes.
+
+### Breaking Changes
+
+### Bugs Fixed
+
+### Other Changes
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/Directory.Build.props b/sdk/ai/Azure.AI.VoiceLive/Directory.Build.props
new file mode 100644
index 000000000000..a4e7b88bc67a
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/Directory.Build.props
@@ -0,0 +1,11 @@
+
+
+
+ true
+ true
+
+
+
+
diff --git a/sdk/ai/Azure.AI.VoiceLive/README.md b/sdk/ai/Azure.AI.VoiceLive/README.md
new file mode 100644
index 000000000000..dd64655a984c
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/README.md
@@ -0,0 +1,318 @@
+# Azure VoiceLive client library for .NET
+
+Azure VoiceLive is a managed service that enables low-latency, high-quality speech-to-speech interactions for voice agents. The API consolidates speech recognition, generative AI, and text-to-speech functionalities into a single, unified interface, providing an end-to-end solution for creating seamless voice-driven experiences.
+
+Use the client library to:
+
+* Create real-time voice assistants and conversational agents
+* Build speech-to-speech applications with minimal latency
+* Integrate advanced conversational features like noise suppression and echo cancellation
+* Leverage multiple AI models (GPT-4o, GPT-4o-mini, Phi) for different use cases
+* Implement function calling and tool integration for dynamic responses
+* Create avatar-enabled voice interactions with visual components
+
+[Source code][source_root] | [Package (NuGet)][package] | [API reference documentation][reference_docs] | [Product documentation][voicelive_docs] | [Samples][source_samples]
+
+## Getting started
+
+This section includes everything a developer needs to install the package and create their first VoiceLive client connection.
+
+### Install the package
+
+Install the client library for .NET with [NuGet](https://www.nuget.org/):
+
+```dotnetcli
+dotnet add package Azure.AI.VoiceLive --prerelease
+```
+
+### Prerequisites
+
+You must have an [Azure subscription](https://azure.microsoft.com/free/dotnet/) and an [Azure AI Foundry resource](https://docs.microsoft.com/azure/ai-services/openai/how-to/create-resource) to use this service.
+
+The client library targets .NET Standard 2.0 and .NET 8.0, providing compatibility with a wide range of .NET implementations. To use the async streaming features demonstrated in the examples, you'll need .NET 6.0 or later.
+
+### Authenticate the client
+
+The Azure.AI.VoiceLive client supports two authentication methods:
+
+1. **Microsoft Entra ID (recommended)**: Use token-based authentication
+2. **API Key**: Use your resource's API key
+
+#### Authentication with Microsoft Entra ID
+
+```C# Snippet:CreateVoiceLiveClientWithTokenCredential
+Uri endpoint = new Uri("https://your-resource.cognitiveservices.azure.com");
+DefaultAzureCredential credential = new DefaultAzureCredential();
+VoiceLiveClient client = new VoiceLiveClient(endpoint, credential);
+```
+
+#### Authentication with API Key
+
+```C# Snippet:CreateVoiceLiveClientWithApiKey
+Uri endpoint = new Uri("https://your-resource.cognitiveservices.azure.com");
+AzureKeyCredential credential = new AzureKeyCredential("your-api-key");
+VoiceLiveClient client = new VoiceLiveClient(endpoint, credential);
+```
+
+For the recommended keyless authentication with Microsoft Entra ID, you need to:
+
+1. Assign the `Cognitive Services User` role to your user account or managed identity in the Azure portal under Access control (IAM) > Add role assignment
+2. Use a `TokenCredential` implementation - the SDK automatically handles token acquisition and refresh with the appropriate scope
+
+### Service API versions
+
+The client library targets the latest service API version by default. You can optionally specify the API version when creating a client instance.
+
+#### Select a service API version
+
+You have the flexibility to explicitly select a supported service API version when instantiating a client by configuring its associated options:
+
+```C# Snippet:CreateVoiceLiveClientForSpecificApiVersion
+Uri endpoint = new Uri("https://your-resource.cognitiveservices.azure.com");
+DefaultAzureCredential credential = new DefaultAzureCredential();
+VoiceLiveClientOptions options = new VoiceLiveClientOptions(VoiceLiveClientOptions.ServiceVersion.V2025_05_01_Preview);
+VoiceLiveClient client = new VoiceLiveClient(endpoint, credential, options);
+```
+
+## Key concepts
+
+The Azure.AI.VoiceLive client library provides several key classes for real-time voice interactions:
+
+### VoiceLiveClient
+
+The primary entry point for the Azure.AI.VoiceLive service. Use this client to establish sessions and configure authentication.
+
+### VoiceLiveSession
+
+Represents an active WebSocket connection to the VoiceLive service. This class handles bidirectional communication, allowing you to send audio input and receive audio output, text transcriptions, and other events in real-time.
+
+### Session Configuration
+
+The service uses session configuration to control various aspects of the voice interaction:
+
+- **Turn Detection**: Configure how the service detects when users start and stop speaking
+- **Audio Processing**: Enable noise suppression and echo cancellation
+- **Voice Selection**: Choose from standard Azure voices, high-definition voices, or custom voices
+- **Model Selection**: Select the AI model (GPT-4o, GPT-4o-mini, Phi variants) that best fits your needs
+
+### Models and Capabilities
+
+The VoiceLive API supports multiple AI models with different capabilities:
+
+| Model | Description | Use Case |
+|-------|-------------|----------|
+| `gpt-4o-realtime-preview` | GPT-4o with real-time audio processing | High-quality conversational AI |
+| `gpt-4o-mini-realtime-preview` | Lightweight GPT-4o variant | Fast, efficient interactions |
+| `phi4-mm-realtime` | Phi model with multimodal support | Cost-effective voice applications |
+
+### Conversational Enhancements
+
+The VoiceLive API provides Azure-specific enhancements:
+
+- **Azure Semantic VAD**: Advanced voice activity detection that removes filler words
+- **Noise Suppression**: Reduces environmental background noise
+- **Echo Cancellation**: Removes echo from the model's own voice
+- **End-of-Turn Detection**: Allows natural pauses without premature interruption
+
+### Thread safety
+
+We guarantee that all client instance methods are thread-safe and independent of each other ([guideline](https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-thread-safety)). This ensures that the recommendation of reusing client instances is always safe, even across threads.
+
+### Additional concepts
+
+[Client options](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#configuring-service-clients-using-clientoptions) |
+[Accessing the response](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#accessing-http-response-details-using-responset) |
+[Long-running operations](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#consuming-long-running-operations-using-operationt) |
+[Handling failures](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#reporting-errors-requestfailedexception) |
+[Diagnostics](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/samples/Diagnostics.md) |
+[Mocking](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/Azure.Core/README.md#mocking) |
+[Client lifetime](https://devblogs.microsoft.com/azure-sdk/lifetime-management-and-thread-safety-guarantees-of-azure-sdk-net-clients/)
+
+
+## Examples
+
+You can familiarize yourself with different APIs using [Samples](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.VoiceLive/samples).
+
+### Basic voice assistant
+
+```C# Snippet:BasicVoiceAssistantExample
+// Create the VoiceLive client
+Uri endpoint = new Uri("https://your-resource.cognitiveservices.azure.com");
+DefaultAzureCredential credential = new DefaultAzureCredential();
+VoiceLiveClient client = new VoiceLiveClient(endpoint, credential);
+
+// Start a new session
+VoiceLiveSession session = await client.StartSessionAsync().ConfigureAwait(false);
+
+// Configure session for voice conversation
+SessionOptions sessionOptions = new SessionOptions()
+{
+ Model = "gpt-4o-mini-realtime-preview",
+ Instructions = "You are a helpful AI assistant. Respond naturally and conversationally.",
+ Voice = new AzureStandardVoice("en-US-AvaNeural", AzureStandardVoiceType.AzureStandard),
+ TurnDetection = new ServerVad()
+ {
+ Threshold = 0.5f,
+ PrefixPaddingMs = 300,
+ SilenceDurationMs = 500
+ },
+ InputAudioFormat = AudioFormat.Pcm16,
+ OutputAudioFormat = AudioFormat.Pcm16
+};
+
+// Ensure modalities include audio
+sessionOptions.Modalities.Clear();
+sessionOptions.Modalities.Add(InputModality.Text);
+sessionOptions.Modalities.Add(InputModality.Audio);
+
+await session.ConfigureConversationSessionAsync(sessionOptions).ConfigureAwait(false);
+
+// Process events from the session
+await foreach (ServerEvent serverEvent in session.GetUpdatesAsync().ConfigureAwait(false))
+{
+ if (serverEvent is ServerEventResponseAudioDelta audioDelta)
+ {
+ // Play audio response
+ byte[] audioData = audioDelta.Delta.ToArray();
+ // ... audio playback logic
+ }
+ else if (serverEvent is ServerEventResponseTextDelta textDelta)
+ {
+ // Display text response
+ Console.Write(textDelta.Delta);
+ }
+}
+```
+
+### Configuring custom voice and advanced features
+
+```C# Snippet:AdvancedVoiceConfiguration
+SessionOptions sessionOptions = new SessionOptions()
+{
+ Model = "gpt-4o-realtime-preview",
+ Instructions = "You are a customer service representative. Be helpful and professional.",
+ Voice = new AzureCustomVoice("your-custom-voice-name", "your-custom-voice-endpoint-id", AzureCustomVoiceType.AzureCustom)
+ {
+ Temperature = 0.8f
+ },
+ TurnDetection = new AzureSemanticVad()
+ {
+ NegThreshold = 0.3f,
+ WindowSize = 300,
+ RemoveFillerWords = true
+ },
+ InputAudioFormat = AudioFormat.Pcm16,
+ OutputAudioFormat = AudioFormat.Pcm16
+};
+
+// Ensure modalities include audio
+sessionOptions.Modalities.Clear();
+sessionOptions.Modalities.Add(InputModality.Text);
+sessionOptions.Modalities.Add(InputModality.Audio);
+
+await session.ConfigureConversationSessionAsync(sessionOptions).ConfigureAwait(false);
+```
+
+### Function calling example
+
+```C# Snippet:FunctionCallingExample
+// Define a function for the assistant to call
+var getCurrentWeatherFunction = new FunctionTool("get_current_weather")
+{
+ Description = "Get the current weather for a given location",
+ Parameters = BinaryData.FromString("""
+ {
+ "type": "object",
+ "properties": {
+ "location": {
+ "type": "string",
+ "description": "The city and state or country"
+ }
+ },
+ "required": ["location"]
+ }
+ """)
+};
+
+SessionOptions sessionOptions = new SessionOptions()
+{
+ Model = "gpt-4o-mini-realtime-preview",
+ Instructions = "You are a weather assistant. Use the get_current_weather function to help users with weather information.",
+ Voice = new AzureStandardVoice("en-US-AvaNeural", AzureStandardVoiceType.AzureStandard),
+ InputAudioFormat = AudioFormat.Pcm16,
+ OutputAudioFormat = AudioFormat.Pcm16
+};
+
+// Add the function tool
+sessionOptions.Tools.Add(getCurrentWeatherFunction);
+
+// Ensure modalities include audio
+sessionOptions.Modalities.Clear();
+sessionOptions.Modalities.Add(InputModality.Text);
+sessionOptions.Modalities.Add(InputModality.Audio);
+
+await session.ConfigureConversationSessionAsync(sessionOptions).ConfigureAwait(false);
+```
+
+## Troubleshooting
+
+### Common errors and exceptions
+
+**Authentication Errors**: If you receive authentication errors, verify that:
+- Your Azure AI Foundry resource is correctly configured
+- Your API key or credential has the necessary permissions
+- The endpoint URL is correct and accessible
+
+**WebSocket Connection Issues**: VoiceLive uses WebSocket connections. Ensure that:
+- Your network allows WebSocket connections
+- Firewall rules permit connections to `*.cognitiveservices.azure.com`
+- The service is available in your selected region
+
+**Audio Processing Errors**: For audio-related issues:
+- Verify audio input format is supported (16kHz or 24kHz PCM)
+- Check that audio devices are accessible and functioning
+- Ensure proper audio codec configuration
+
+### Logging and diagnostics
+
+Enable logging to help diagnose issues:
+
+```csharp
+using Azure.Core.Diagnostics;
+
+// Enable logging for Azure SDK
+using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger();
+```
+
+### Rate limiting and throttling
+
+The VoiceLive service implements rate limiting based on:
+- Concurrent connections per resource
+- Token consumption rates
+- Model-specific limits
+
+Implement appropriate retry logic and connection management to handle throttling gracefully.
+
+## Next steps
+
+* Explore the comprehensive [samples](https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.VoiceLive/samples) including basic voice assistants and customer service bots
+* Learn about [voice customization](https://learn.microsoft.com/azure/ai-services/speech-service/custom-neural-voice) to create unique brand voices
+* Understand [avatar integration](https://learn.microsoft.com/azure/ai-services/speech-service/text-to-speech-avatar/what-is-text-to-speech-avatar) for visual voice experiences
+* Review the [VoiceLive API documentation](https://docs.microsoft.com/azure/ai-services/) for advanced configuration options
+
+## Contributing
+
+This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
+
+When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
+
+
+[source_root]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.VoiceLive/src
+[source_samples]: https://github.com/Azure/azure-sdk-for-net/tree/main/sdk/ai/Azure.AI.VoiceLive/samples
+[package]: https://www.nuget.org/
+[reference_docs]: https://azure.github.io/azure-sdk-for-net/
+[voicelive_docs]: https://docs.microsoft.com/azure/ai-services/
+[style-guide-msft]: https://docs.microsoft.com/style-guide/capitalization
+[style-guide-cloud]: https://aka.ms/azsdk/cloud-style-guide
diff --git a/sdk/ai/Azure.AI.VoiceLive/cspell.json b/sdk/ai/Azure.AI.VoiceLive/cspell.json
new file mode 100644
index 000000000000..61c03bda4518
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/cspell.json
@@ -0,0 +1,6 @@
+{
+ // Version of the setting file. Always 0.2
+ "version": "0.2",
+ // words - list of words to be always considered correct
+ "words": ["Viseme", "logprobs", "Alaw", "Ulaw"]
+}
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/.gitignore b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/.gitignore
new file mode 100644
index 000000000000..09ee35a7a9c2
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/.gitignore
@@ -0,0 +1,323 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+*.userprefs
+launchsettings.json
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Visual Studio 2015/2017/2019/2022 cache/options directory
+.vs/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# Tye
+.tye/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these files may be visible.
+*.azurePubxml
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment the next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+CrystalReportsBackup*/
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
+
+# Fody - auto-generated XML schema
+FodyWeavers.xsd
+
+# Sensitive configuration
+appsettings.Development.json
+appsettings.Production.json
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/AudioProcessor.cs b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/AudioProcessor.cs
new file mode 100644
index 000000000000..5d6713b1e484
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/AudioProcessor.cs
@@ -0,0 +1,386 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Threading.Channels;
+using NAudio.Wave;
+using Azure.Core.Pipeline;
+
+namespace Azure.AI.VoiceLive.Samples;
+
+///
+/// Handles real-time audio capture and playback for the voice assistant.
+///
+/// This processor demonstrates some of the new VoiceLive SDK convenience methods:
+/// - Uses existing SendInputAudioAsync() method for audio streaming
+/// - Shows how convenience methods simplify audio operations
+///
+/// Additional convenience methods available in the SDK:
+/// - StartAudioTurnAsync() / AppendAudioToTurnAsync() / EndAudioTurnAsync() - Audio turn management
+/// - ClearStreamingAudioAsync() - Clear all streaming audio
+/// - ConnectAvatarAsync() - Avatar connection with SDP
+///
+/// Threading Architecture:
+/// - Main thread: Event loop and UI
+/// - Capture thread: NAudio input stream reading
+/// - Send thread: Async audio data transmission to VoiceLive
+/// - Playback thread: NAudio output stream writing
+///
+public class AudioProcessor : IDisposable
+{
+ private readonly VoiceLiveSession _session;
+ private readonly ILogger _logger;
+
+ // Audio configuration - PCM16, 24kHz, mono as specified
+ private const int SampleRate = 24000;
+ private const int Channels = 1;
+ private const int BitsPerSample = 16;
+
+ // NAudio components
+ private WaveInEvent? _waveIn;
+ private WaveOutEvent? _waveOut;
+ private BufferedWaveProvider? _playbackBuffer;
+
+ // Audio capture and playback state
+ private bool _isCapturing;
+ private bool _isPlaying;
+
+ // Audio streaming channels
+ private readonly Channel _audioSendChannel;
+ private readonly Channel _audioPlaybackChannel;
+ private readonly ChannelWriter _audioSendWriter;
+ private readonly ChannelReader _audioSendReader;
+ private readonly ChannelWriter _audioPlaybackWriter;
+ private readonly ChannelReader _audioPlaybackReader;
+
+ // Background tasks
+ private Task? _audioSendTask;
+ private Task? _audioPlaybackTask;
+ private readonly CancellationTokenSource _cancellationTokenSource;
+ private CancellationTokenSource _playbackCancellationTokenSource;
+
+ ///
+ /// Initializes a new instance of the AudioProcessor class.
+ ///
+ /// The VoiceLive session for audio communication.
+ /// Logger for diagnostic information.
+ public AudioProcessor(VoiceLiveSession session, ILogger logger)
+ {
+ _session = session ?? throw new ArgumentNullException(nameof(session));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ // Create unbounded channels for audio data
+ _audioSendChannel = Channel.CreateUnbounded();
+ _audioSendWriter = _audioSendChannel.Writer;
+ _audioSendReader = _audioSendChannel.Reader;
+
+ _audioPlaybackChannel = Channel.CreateUnbounded();
+ _audioPlaybackWriter = _audioPlaybackChannel.Writer;
+ _audioPlaybackReader = _audioPlaybackChannel.Reader;
+
+ _cancellationTokenSource = new CancellationTokenSource();
+ _playbackCancellationTokenSource = new CancellationTokenSource();
+
+ _logger.LogInformation("AudioProcessor initialized with {SampleRate}Hz PCM16 mono audio", SampleRate);
+ }
+
+ ///
+ /// Start capturing audio from microphone.
+ ///
+ public Task StartCaptureAsync()
+ {
+ if (_isCapturing)
+ return Task.CompletedTask;
+
+ _isCapturing = true;
+
+ try
+ {
+ _waveIn = new WaveInEvent
+ {
+ WaveFormat = new WaveFormat(SampleRate, BitsPerSample, Channels),
+ BufferMilliseconds = 50 // 50ms buffer for low latency
+ };
+
+ _waveIn.DataAvailable += OnAudioDataAvailable;
+ _waveIn.RecordingStopped += OnRecordingStopped;
+
+ _waveIn.DeviceNumber = 0;
+
+ _waveIn.StartRecording();
+
+ // Start audio send task
+ _audioSendTask = ProcessAudioSendAsync(_cancellationTokenSource.Token);
+
+ _logger.LogInformation("Started audio capture");
+ return Task.CompletedTask;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to start audio capture");
+ _isCapturing = false;
+ throw;
+ }
+ }
+
+ ///
+ /// Stop capturing audio.
+ ///
+ public async Task StopCaptureAsync()
+ {
+ if (!_isCapturing)
+ return;
+
+ _isCapturing = false;
+
+ if (_waveIn != null)
+ {
+ _waveIn.StopRecording();
+ _waveIn.DataAvailable -= OnAudioDataAvailable;
+ _waveIn.RecordingStopped -= OnRecordingStopped;
+ _waveIn.Dispose();
+ _waveIn = null;
+ }
+
+ // Complete the send channel and wait for the send task
+ _audioSendWriter.TryComplete();
+ if (_audioSendTask != null)
+ {
+ await _audioSendTask.ConfigureAwait(false);
+ _audioSendTask = null;
+ }
+
+ _logger.LogInformation("Stopped audio capture");
+ }
+
+ ///
+ /// Initialize audio playback system.
+ ///
+ public Task StartPlaybackAsync()
+ {
+ if (_isPlaying)
+ return Task.CompletedTask;
+
+ _isPlaying = true;
+
+ try
+ {
+ _waveOut = new WaveOutEvent
+ {
+ DesiredLatency = 100 // 100ms latency
+ };
+
+ _playbackBuffer = new BufferedWaveProvider(new WaveFormat(SampleRate, BitsPerSample, Channels))
+ {
+ BufferDuration = TimeSpan.FromMinutes(5), // 5 second buffer
+ DiscardOnBufferOverflow = true
+ };
+
+ _waveOut.Init(_playbackBuffer);
+ _waveOut.Play();
+
+ _playbackCancellationTokenSource = new CancellationTokenSource();
+
+ // Start audio playback task
+ _audioPlaybackTask = ProcessAudioPlaybackAsync();
+
+ _logger.LogInformation("Audio playback system ready");
+ return Task.CompletedTask;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to initialize audio playback");
+ _isPlaying = false;
+ throw;
+ }
+ }
+
+ ///
+ /// Stop audio playback and clear buffer.
+ ///
+ public async Task StopPlaybackAsync()
+ {
+ if (!_isPlaying)
+ return;
+
+ _isPlaying = false;
+
+ // Clear the playback channel
+ while (_audioPlaybackReader.TryRead(out _))
+ { }
+
+ if (_playbackBuffer != null)
+ {
+ _playbackBuffer.ClearBuffer();
+ }
+
+ if (_waveOut != null)
+ {
+ _waveOut.Stop();
+ _waveOut.Dispose();
+ _waveOut = null;
+ }
+
+ _playbackBuffer = null;
+
+ // Complete the playback channel and wait for the playback task
+ _playbackCancellationTokenSource.Cancel();
+
+ if (_audioPlaybackTask != null)
+ {
+ await _audioPlaybackTask.ConfigureAwait(false);
+ _audioPlaybackTask = null;
+ }
+
+ _logger.LogInformation("Stopped audio playback");
+ }
+
+ ///
+ /// Queue audio data for playback.
+ ///
+ /// The audio data to queue.
+ public async Task QueueAudioAsync(byte[] audioData)
+ {
+ if (_isPlaying && audioData.Length > 0)
+ {
+ await _audioPlaybackWriter.WriteAsync(audioData).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Event handler for audio data available from microphone.
+ ///
+ private void OnAudioDataAvailable(object? sender, WaveInEventArgs e)
+ {
+ if (_isCapturing && e.BytesRecorded > 0)
+ {
+ byte[] audioData = new byte[e.BytesRecorded];
+ Array.Copy(e.Buffer, 0, audioData, 0, e.BytesRecorded);
+
+ // Queue audio data for sending (non-blocking)
+ if (!_audioSendWriter.TryWrite(audioData))
+ {
+ _logger.LogWarning("Failed to queue audio data for sending - channel may be full");
+ }
+ }
+ }
+
+ ///
+ /// Event handler for recording stopped.
+ ///
+ private void OnRecordingStopped(object? sender, StoppedEventArgs e)
+ {
+ if (e.Exception != null)
+ {
+ _logger.LogError(e.Exception, "Audio recording stopped due to error");
+ }
+ }
+
+ ///
+ /// Background task to process audio data and send to VoiceLive service.
+ ///
+ private async Task ProcessAudioSendAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ await foreach (byte[] audioData in _audioSendReader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ break;
+
+ try
+ {
+ // Send audio data directly to the session using the convenience method
+ // This demonstrates the existing SendInputAudioAsync convenience method
+ // Other available methods: StartAudioTurnAsync, AppendAudioToTurnAsync, EndAudioTurnAsync
+ await _session.SendInputAudioAsync(audioData, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error sending audio data to VoiceLive");
+ // Continue processing other audio data
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Expected when cancellation is requested
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in audio send processing");
+ }
+ }
+
+ ///
+ /// Background task to process audio playback.
+ ///
+ private async Task ProcessAudioPlaybackAsync()
+ {
+ try
+ {
+ CancellationTokenSource combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_playbackCancellationTokenSource.Token, _cancellationTokenSource.Token);
+ var cancellationToken = combinedTokenSource.Token;
+
+ await foreach (byte[] audioData in _audioPlaybackReader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ break;
+
+ try
+ {
+ if (_playbackBuffer != null && _isPlaying)
+ {
+ _playbackBuffer.AddSamples(audioData, 0, audioData.Length);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in audio playback");
+ // Continue processing other audio data
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Expected when cancellation is requested
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in audio playback processing");
+ }
+ }
+
+ ///
+ /// Clean up audio resources.
+ ///
+ public async Task CleanupAsync()
+ {
+ await StopCaptureAsync().ConfigureAwait(false);
+ await StopPlaybackAsync().ConfigureAwait(false);
+
+ _cancellationTokenSource.Cancel();
+
+ // Wait for background tasks to complete
+ var tasks = new List();
+ if (_audioSendTask != null)
+ tasks.Add(_audioSendTask);
+ if (_audioPlaybackTask != null)
+ tasks.Add(_audioPlaybackTask);
+
+ if (tasks.Count > 0)
+ {
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ }
+
+ _logger.LogInformation("Audio processor cleaned up");
+ }
+
+ ///
+ /// Dispose of resources.
+ ///
+ public void Dispose()
+ {
+ CleanupAsync().Wait();
+ _cancellationTokenSource.Dispose();
+ }
+}
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.cs b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.cs
new file mode 100644
index 000000000000..4b55460f2061
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.cs
@@ -0,0 +1,305 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Azure.AI.VoiceLive;
+
+namespace Azure.AI.VoiceLive.Samples;
+
+///
+/// Basic voice assistant implementing the VoiceLive SDK patterns.
+///
+///
+/// This sample now demonstrates some of the new convenience methods added to the VoiceLive SDK:
+/// - ClearStreamingAudioAsync() - Clears all input audio currently being streamed
+/// - CancelResponseAsync() - Cancels the current response generation (existing method)
+/// - ConfigureConversationSessionAsync() - Configures session options (existing method)
+///
+/// Additional convenience methods available but not shown in this sample:
+/// - StartAudioTurnAsync() / EndAudioTurnAsync() / CancelAudioTurnAsync() - Audio turn management
+/// - AppendAudioToTurnAsync() - Append audio data to an ongoing turn
+/// - ConnectAvatarAsync() - Connect avatar with SDP for media negotiation
+///
+/// These methods provide a more developer-friendly API similar to the OpenAI SDK,
+/// eliminating the need to manually construct and populate ClientEvent classes.
+///
+public class BasicVoiceAssistant : IDisposable
+{
+ private readonly VoiceLiveClient _client;
+ private readonly string _model;
+ private readonly string _voice;
+ private readonly string _instructions;
+ private readonly ILogger _logger;
+ private readonly ILoggerFactory _loggerFactory;
+
+ private VoiceLiveSession? _session;
+ private AudioProcessor? _audioProcessor;
+ private bool _disposed;
+
+ ///
+ /// Initializes a new instance of the BasicVoiceAssistant class.
+ ///
+ /// The VoiceLive client.
+ /// The model to use.
+ /// The voice to use.
+ /// The system instructions.
+ /// Logger factory for creating loggers.
+ public BasicVoiceAssistant(
+ VoiceLiveClient client,
+ string model,
+ string voice,
+ string instructions,
+ ILoggerFactory loggerFactory)
+ {
+ _client = client ?? throw new ArgumentNullException(nameof(client));
+ _model = model ?? throw new ArgumentNullException(nameof(model));
+ _voice = voice ?? throw new ArgumentNullException(nameof(voice));
+ _instructions = instructions ?? throw new ArgumentNullException(nameof(instructions));
+ _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ ///
+ /// Start the voice assistant session.
+ ///
+ /// Cancellation token for stopping the session.
+ public async Task StartAsync(CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ _logger.LogInformation("Connecting to VoiceLive API with model {Model}", _model);
+
+ // Start VoiceLive session
+ _session = await _client.StartSessionAsync(_model, cancellationToken).ConfigureAwait(false);
+
+ // Initialize audio processor
+ _audioProcessor = new AudioProcessor(_session, _loggerFactory.CreateLogger());
+
+ // Configure session for voice conversation
+ await SetupSessionAsync(cancellationToken).ConfigureAwait(false);
+
+ // Start audio systems
+ await _audioProcessor.StartPlaybackAsync().ConfigureAwait(false);
+ await _audioProcessor.StartCaptureAsync().ConfigureAwait(false);
+
+ _logger.LogInformation("Voice assistant ready! Start speaking...");
+ Console.WriteLine();
+ Console.WriteLine("=" + new string('=', 59));
+ Console.WriteLine("🎤 VOICE ASSISTANT READY");
+ Console.WriteLine("Start speaking to begin conversation");
+ Console.WriteLine("Press Ctrl+C to exit");
+ Console.WriteLine("=" + new string('=', 59));
+ Console.WriteLine();
+
+ // Process events
+ await ProcessEventsAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogInformation("Received cancellation signal, shutting down...");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Connection error");
+ throw;
+ }
+ finally
+ {
+ // Cleanup
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.CleanupAsync().ConfigureAwait(false);
+ }
+ }
+ }
+
+ ///
+ /// Configure the VoiceLive session for audio conversation.
+ ///
+ private async Task SetupSessionAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Setting up voice conversation session...");
+
+ // Azure voice
+ var azureVoice = new AzureStandardVoice(_voice, AzureStandardVoiceType.AzureStandard);
+
+ // Create strongly typed turn detection configuration
+ var turnDetectionConfig = new ServerVad
+ {
+ Threshold = 0.5f,
+ PrefixPaddingMs = 300,
+ SilenceDurationMs = 500
+ };
+
+ // Create conversation session options
+ var sessionOptions = new SessionOptions
+ {
+ EchoCancellation = new AudioEchoCancellation(),
+ Model = _model,
+ Instructions = _instructions,
+ Voice = azureVoice,
+ InputAudioFormat = AudioFormat.Pcm16,
+ OutputAudioFormat = AudioFormat.Pcm16,
+ TurnDetection = turnDetectionConfig
+ };
+
+ // Ensure modalities include audio
+ sessionOptions.Modalities.Clear();
+ sessionOptions.Modalities.Add(InputModality.Text);
+ sessionOptions.Modalities.Add(InputModality.Audio);
+
+ await _session!.ConfigureConversationSessionAsync(sessionOptions, cancellationToken).ConfigureAwait(false);
+
+ _logger.LogInformation("Session configuration sent");
+ }
+
+ ///
+ /// Process events from the VoiceLive session.
+ ///
+ private async Task ProcessEventsAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ await foreach (ServerEvent serverEvent in _session!.GetUpdatesAsync(cancellationToken).ConfigureAwait(false))
+ {
+ await HandleServerEventAsync(serverEvent, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogInformation("Event processing cancelled");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error processing events");
+ throw;
+ }
+ }
+
+ ///
+ /// Handle different types of server events from VoiceLive.
+ ///
+ private async Task HandleServerEventAsync(ServerEvent serverEvent, CancellationToken cancellationToken)
+ {
+ _logger.LogDebug("Received event: {EventType}", serverEvent.GetType().Name);
+
+ switch (serverEvent)
+ {
+ case ServerEventSessionCreated sessionCreated:
+ await HandleSessionCreatedAsync(sessionCreated, cancellationToken).ConfigureAwait(false);
+ break;
+
+ case ServerEventSessionUpdated sessionUpdated:
+ _logger.LogInformation("Session updated successfully");
+
+ // Start audio capture once session is ready
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StartCaptureAsync().ConfigureAwait(false);
+ }
+ break;
+
+ case ServerEventInputAudioBufferSpeechStarted speechStarted:
+ _logger.LogInformation("🎤 User started speaking - stopping playback");
+ Console.WriteLine("🎤 Listening...");
+
+ // Stop current assistant audio playback (interruption handling)
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StopPlaybackAsync().ConfigureAwait(false);
+ }
+
+ // Cancel any ongoing response
+ try
+ {
+ await _session!.CancelResponseAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "No response to cancel");
+ }
+
+ // Demonstrate the new ClearStreamingAudio convenience method
+ try
+ {
+ await _session!.ClearStreamingAudioAsync(cancellationToken).ConfigureAwait(false);
+ _logger.LogInformation("✨ Used ClearStreamingAudioAsync convenience method");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "ClearStreamingAudio call failed (may not be supported in all scenarios)");
+ }
+ break;
+
+ case ServerEventInputAudioBufferSpeechStopped speechStopped:
+ _logger.LogInformation("🎤 User stopped speaking");
+ Console.WriteLine("🤔 Processing...");
+
+ // Restart playback system for response
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StartPlaybackAsync().ConfigureAwait(false);
+ }
+ break;
+
+ case ServerEventResponseCreated responseCreated:
+ _logger.LogInformation("🤖 Assistant response created");
+ break;
+
+ case ServerEventResponseAudioDelta audioDelta:
+ // Stream audio response to speakers
+ _logger.LogDebug("Received audio delta");
+
+ if (audioDelta.Delta != null && _audioProcessor != null)
+ {
+ byte[] audioData = audioDelta.Delta.ToArray();
+ await _audioProcessor.QueueAudioAsync(audioData).ConfigureAwait(false);
+ }
+ break;
+
+ case ServerEventResponseAudioDone audioDone:
+ _logger.LogInformation("🤖 Assistant finished speaking");
+ Console.WriteLine("🎤 Ready for next input...");
+ break;
+
+ case ServerEventResponseDone responseDone:
+ _logger.LogInformation("✅ Response complete");
+ break;
+
+ case ServerEventError errorEvent:
+ _logger.LogError("❌ VoiceLive error: {ErrorMessage}", errorEvent.Error?.Message);
+ Console.WriteLine($"Error: {errorEvent.Error?.Message}");
+ break;
+
+ default:
+ _logger.LogDebug("Unhandled event type: {EventType}", serverEvent.GetType().Name);
+ break;
+ }
+ }
+
+ ///
+ /// Handle session created event.
+ ///
+ private async Task HandleSessionCreatedAsync(ServerEventSessionCreated sessionCreated, CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Session ready: {SessionId}", sessionCreated.Session?.Id);
+
+ // Start audio capture once session is ready
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StartCaptureAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Dispose of resources.
+ ///
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ _audioProcessor?.Dispose();
+ _session?.Dispose();
+ _disposed = true;
+ }
+}
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.csproj b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.csproj
new file mode 100644
index 000000000000..b4fcec9b4aec
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.csproj
@@ -0,0 +1,43 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ BasicVoiceAssistant
+ Azure.AI.VoiceLive.Samples
+ true
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.sln b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.sln
new file mode 100644
index 000000000000..4ceacdfde460
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/BasicVoiceAssistant.sln
@@ -0,0 +1,18 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BasicVoiceAssistant", "BasicVoiceAssistant.csproj", "{A1B2C3D4-E5F6-7890-1234-567890ABCDEF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A1B2C3D4-E5F6-7890-1234-567890ABCDEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-1234-567890ABCDEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1B2C3D4-E5F6-7890-1234-567890ABCDEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1B2C3D4-E5F6-7890-1234-567890ABCDEF}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/GlobalUsings.cs b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/GlobalUsings.cs
new file mode 100644
index 000000000000..2487c6297d6c
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/GlobalUsings.cs
@@ -0,0 +1,8 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+global using System;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using Azure.AI.VoiceLive;
+global using Microsoft.Extensions.Logging;
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/README.md b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/README.md
new file mode 100644
index 000000000000..dcdef5f6a866
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/README.md
@@ -0,0 +1,248 @@
+# Basic Voice Assistant Sample
+
+This sample demonstrates the fundamental capabilities of the Azure VoiceLive SDK by creating a basic voice assistant that can engage in natural conversation with proper interruption handling. This serves as the foundational example that showcases the core value proposition of unified speech-to-speech interaction.
+
+## New VoiceLive SDK Convenience Methods
+
+This sample now demonstrates some of the new convenience methods added to the VoiceLive SDK for better developer experience:
+
+**Used in this sample:**
+- `ClearStreamingAudioAsync()` - Clears all input audio currently being streamed
+- `ConfigureConversationSessionAsync()` - Configures conversation session options
+- `CancelResponseAsync()` - Cancels the current response generation
+- `SendInputAudioAsync()` - Sends audio data to the service
+
+**Additional convenience methods available:**
+- `StartAudioTurnAsync()` / `EndAudioTurnAsync()` / `CancelAudioTurnAsync()` - Audio turn management
+- `AppendAudioToTurnAsync()` - Append audio data to an ongoing turn
+- `ConnectAvatarAsync()` - Connect avatar with SDP for media negotiation
+- `CommitInputAudioAsync()` / `ClearInputAudioAsync()` - Audio buffer operations
+
+These methods eliminate the need to manually construct and populate `ClientEvent` classes, providing a more developer-friendly API similar to the OpenAI SDK.
+
+## Features
+
+- **Real-time voice conversation**: Seamless bidirectional audio streaming
+- **Interruption handling**: Graceful handling of user interruptions during assistant responses
+- **Multiple voice options**: Support for both OpenAI and Azure voices
+- **Cross-platform audio**: Uses NAudio for reliable audio capture and playback
+- **Robust error handling**: Automatic reconnection and error recovery
+- **Configurable settings**: Command line options and configuration file support
+
+## Prerequisites
+
+- .NET 8.0 or later
+- Azure VoiceLive API key or Azure credential
+- Microphone and speakers/headphones
+- Audio drivers properly installed
+
+## Setup
+
+1. **Install dependencies**:
+ ```bash
+ dotnet restore
+ ```
+
+2. **Configure credentials**:
+
+ Option 1: Environment variables
+ ```bash
+ export AZURE_VOICELIVE_API_KEY="your-api-key"
+ export AZURE_VOICELIVE_ENDPOINT="wss://api.voicelive.com/v1"
+ ```
+
+ Option 2: Update `appsettings.json`
+ ```json
+ {
+ "VoiceLive": {
+ "ApiKey": "your-api-key",
+ "Endpoint": "wss://api.voicelive.com/v1",
+ "Model": "gpt-4o-realtime-preview",
+ "Voice": "en-US-AvaNeural",
+ "Instructions": "You are a helpful AI assistant. Respond naturally and conversationally."
+ }
+ }
+ ```
+
+3. **Test audio system**:
+ ```bash
+ dotnet run -- --verbose
+ ```
+
+## Usage
+
+### Basic Usage
+```bash
+dotnet run
+```
+
+### Command Line Options
+```bash
+dotnet run -- --help
+```
+
+Available options:
+- `--api-key `: Azure VoiceLive API key
+- `--endpoint `: VoiceLive service endpoint
+- `--model `: Model to use (default: gpt-4o-realtime-preview)
+- `--voice `: Voice for the assistant (default: en-US-AvaNeural)
+- `--instructions `: System instructions for the AI
+- `--use-token-credential`: Use Azure authentication instead of API key
+- `--verbose`: Enable detailed logging
+
+### Example Commands
+
+**Using a different voice**:
+```bash
+dotnet run -- --voice "en-US-JennyNeural"
+```
+
+**Using OpenAI voice**:
+```bash
+dotnet run -- --voice "alloy"
+```
+
+**Custom instructions**:
+```bash
+dotnet run -- --instructions "You are a helpful coding assistant. Focus on programming questions and provide code examples."
+```
+
+**Azure authentication**:
+```bash
+dotnet run -- --use-token-credential
+```
+
+## Supported Voices
+
+### OpenAI Voices
+- `alloy`
+- `echo`
+- `fable`
+- `onyx`
+- `nova`
+- `shimmer`
+
+### Azure Voices
+- `en-US-AvaNeural`
+- `en-US-JennyNeural`
+- `en-US-GuyNeural`
+
+## How It Works
+
+### Architecture
+The sample uses a multi-threaded architecture for optimal performance:
+
+1. **Main Thread**: UI and event processing
+2. **Audio Capture**: NAudio input stream processing
+3. **Audio Send**: Async transmission to VoiceLive service
+4. **Audio Playback**: NAudio output stream processing
+
+### Key Components
+
+#### VoiceLiveClient
+- Manages authentication and connection to the service
+- Provides WebSocket-based real-time communication
+
+#### VoiceLiveSession
+- Handles bidirectional message streaming
+- Manages session lifecycle and configuration
+
+#### AudioProcessor
+- Captures audio from microphone (24kHz PCM16 mono)
+- Streams audio to VoiceLive service in real-time
+- Plays back assistant responses through speakers
+- Handles interruption scenarios
+
+#### BasicVoiceAssistant
+- Orchestrates the entire conversation flow
+- Handles VoiceLive events and updates
+- Manages session configuration and voice settings
+
+### Event Flow
+
+1. **Session Setup**: Configure audio format, voice, and turn detection
+2. **Audio Capture**: Start capturing microphone input
+3. **Speech Detection**: Service detects when user starts/stops speaking
+4. **Response Generation**: AI generates audio response
+5. **Audio Playback**: Stream response audio to speakers
+6. **Interruption Handling**: Stop playback when user interrupts
+
+## Troubleshooting
+
+### Audio Issues
+
+**No microphone detected**:
+- Ensure microphone is connected and recognized by Windows
+- Check audio permissions for the application
+- Try running with `--verbose` to see detailed audio device information
+
+**No sound output**:
+- Check speaker/headphone connections
+- Verify volume levels
+- Ensure no other applications are exclusively using audio devices
+
+**Poor audio quality**:
+- Check microphone positioning and levels
+- Reduce background noise
+- Ensure stable internet connection for real-time streaming
+
+### Connection Issues
+
+**Authentication failed**:
+- Verify API key is correct and active
+- Check endpoint URL format
+- Try using `--use-token-credential` for Azure authentication
+
+**Connection timeouts**:
+- Check internet connectivity
+- Verify firewall allows WebSocket connections
+- Try different endpoint if available
+
+### Performance Issues
+
+**High latency**:
+- Close other bandwidth-intensive applications
+- Use wired internet connection instead of WiFi
+- Reduce audio buffer sizes (requires code modification)
+
+**Audio dropouts**:
+- Check system resources (CPU, memory)
+- Close unnecessary applications
+- Update audio drivers
+
+## Advanced Configuration
+
+### Custom Audio Settings
+Modify `AudioProcessor.cs` to adjust:
+- Buffer sizes for latency vs. stability trade-offs
+- Sample rates (requires service support)
+- Audio formats and compression
+
+### Session Configuration
+Modify `BasicVoiceAssistant.cs` to customize:
+- Turn detection sensitivity
+- Response modalities (text + audio vs. audio only)
+- Conversation context and memory
+
+### Error Handling
+The sample includes robust error handling for:
+- Network connectivity issues
+- Audio device problems
+- Service-side errors
+- Graceful shutdown scenarios
+
+## Next Steps
+
+This basic sample can be extended with:
+
+1. **Voice Selection UI**: Dynamic voice switching during conversation
+2. **Conversation History**: Save and replay conversations
+3. **Custom Instructions**: Runtime instruction updates
+4. **Multi-Language Support**: Language detection and switching
+5. **Audio Effects**: Voice modulation and audio processing
+6. **Analytics**: Conversation metrics and usage tracking
+
+## References
+
+- [NAudio Documentation](https://github.com/naudio/NAudio)
+- [System.CommandLine Documentation](https://docs.microsoft.com/dotnet/standard/commandline/)
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/SampleProgram.cs b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/SampleProgram.cs
new file mode 100644
index 000000000000..fc1fc8c3380c
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/SampleProgram.cs
@@ -0,0 +1,263 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.CommandLine;
+using Azure.AI.VoiceLive.Samples;
+using Azure.Core;
+using Azure.Core.Pipeline;
+using Azure.Identity;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+using NAudio.Wave;
+
+namespace Azure.AI.VoiceLive.Samples
+{
+ ///
+ /// FILE: Program.cs
+ ///
+ ///
+ /// DESCRIPTION:
+ /// This sample demonstrates the fundamental capabilities of the VoiceLive SDK by creating
+ /// a basic voice assistant that can engage in natural conversation with proper interruption
+ /// handling. This serves as the foundational example that showcases the core value
+ /// proposition of unified speech-to-speech interaction.
+ ///
+ /// USAGE:
+ /// dotnet run
+ ///
+ /// Set the environment variables with your own values before running the sample:
+ /// 1) AZURE_VOICELIVE_API_KEY - The Azure VoiceLive API key
+ /// 2) AZURE_VOICELIVE_ENDPOINT - The Azure VoiceLive endpoint
+ ///
+ /// Or update appsettings.json with your values.
+ ///
+ /// REQUIREMENTS:
+ /// - Azure.AI.VoiceLive
+ /// - Azure.Identity
+ /// - NAudio (for audio capture and playback)
+ /// - Microsoft.Extensions.Configuration
+ /// - System.CommandLine
+ ///
+ public class SampleProgram
+ {
+ ///
+ /// Main entry point for the Voice Assistant sample.
+ ///
+ ///
+ ///
+ public static async Task Main(string[] args)
+ {
+ // Create command line interface
+ var rootCommand = CreateRootCommand();
+ return await rootCommand.InvokeAsync(args).ConfigureAwait(false);
+ }
+
+ private static RootCommand CreateRootCommand()
+ {
+ var rootCommand = new RootCommand("Basic Voice Assistant using Azure VoiceLive SDK");
+
+ var apiKeyOption = new Option(
+ "--api-key",
+ "Azure VoiceLive API key. If not provided, will use AZURE_VOICELIVE_API_KEY environment variable.");
+
+ var endpointOption = new Option(
+ "--endpoint",
+ () => "wss://api.voicelive.com/v1",
+ "Azure VoiceLive endpoint");
+
+ var modelOption = new Option(
+ "--model",
+ () => "gpt-4o",
+ "VoiceLive model to use");
+
+ var voiceOption = new Option(
+ "--voice",
+ () => "en-US-AvaNeural",
+ "Voice to use for the assistant");
+
+ var instructionsOption = new Option(
+ "--instructions",
+ () => "You are a helpful AI assistant. Respond naturally and conversationally. Keep your responses concise but engaging.",
+ "System instructions for the AI assistant");
+
+ var useTokenCredentialOption = new Option(
+ "--use-token-credential",
+ "Use Azure token credential instead of API key");
+
+ var verboseOption = new Option(
+ "--verbose",
+ "Enable verbose logging");
+
+ rootCommand.AddOption(apiKeyOption);
+ rootCommand.AddOption(endpointOption);
+ rootCommand.AddOption(modelOption);
+ rootCommand.AddOption(voiceOption);
+ rootCommand.AddOption(instructionsOption);
+ rootCommand.AddOption(useTokenCredentialOption);
+ rootCommand.AddOption(verboseOption);
+
+ rootCommand.SetHandler(async (
+ string? apiKey,
+ string endpoint,
+ string model,
+ string voice,
+ string instructions,
+ bool useTokenCredential,
+ bool verbose) =>
+ {
+ await RunVoiceAssistantAsync(apiKey, endpoint, model, voice, instructions, useTokenCredential, verbose).ConfigureAwait(false);
+ },
+ apiKeyOption,
+ endpointOption,
+ modelOption,
+ voiceOption,
+ instructionsOption,
+ useTokenCredentialOption,
+ verboseOption);
+
+ return rootCommand;
+ }
+
+ private static async Task RunVoiceAssistantAsync(
+ string? apiKey,
+ string endpoint,
+ string model,
+ string voice,
+ string instructions,
+ bool useTokenCredential,
+ bool verbose)
+ {
+ // Setup configuration
+ var configuration = new ConfigurationBuilder()
+ .AddJsonFile("appsettings.json", optional: true)
+ .AddEnvironmentVariables()
+ .Build();
+
+ // Override with command line values if provided
+ apiKey ??= configuration["VoiceLive:ApiKey"] ?? Environment.GetEnvironmentVariable("AZURE_VOICELIVE_API_KEY");
+ endpoint = configuration["VoiceLive:Endpoint"] ?? endpoint;
+ model = configuration["VoiceLive:Model"] ?? model;
+ voice = configuration["VoiceLive:Voice"] ?? voice;
+ instructions = configuration["VoiceLive:Instructions"] ?? instructions;
+
+ // Setup logging
+ using var loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.AddConsole();
+ if (verbose)
+ {
+ builder.SetMinimumLevel(LogLevel.Debug);
+ }
+ else
+ {
+ builder.SetMinimumLevel(LogLevel.Information);
+ }
+ });
+
+ var logger = loggerFactory.CreateLogger();
+
+ // Validate credentials
+ if (string.IsNullOrEmpty(apiKey) && !useTokenCredential)
+ {
+ Console.WriteLine("❌ Error: No authentication provided");
+ Console.WriteLine("Please provide an API key using --api-key or set AZURE_VOICELIVE_API_KEY environment variable,");
+ Console.WriteLine("or use --use-token-credential for Azure authentication.");
+ return;
+ }
+
+ // Check audio system before starting
+ if (!CheckAudioSystem(logger))
+ {
+ return;
+ }
+
+ try
+ {
+ // Create client with appropriate credential
+ VoiceLiveClient client;
+ if (useTokenCredential)
+ {
+ var tokenCredential = new DefaultAzureCredential();
+ client = new VoiceLiveClient(new Uri(endpoint), tokenCredential, new VoiceLiveClientOptions());
+ logger.LogInformation("Using Azure token credential");
+ }
+ else
+ {
+ var keyCredential = new Azure.AzureKeyCredential(apiKey!);
+ client = new VoiceLiveClient(new Uri(endpoint), keyCredential, new VoiceLiveClientOptions());
+ logger.LogInformation("Using API key credential");
+ }
+
+ // Create and start voice assistant
+ using var assistant = new BasicVoiceAssistant(
+ client,
+ model,
+ voice,
+ instructions,
+ loggerFactory);
+
+ // Setup cancellation token for graceful shutdown
+ using var cancellationTokenSource = new CancellationTokenSource();
+ Console.CancelKeyPress += (sender, e) =>
+ {
+ e.Cancel = true;
+ logger.LogInformation("Received shutdown signal");
+ cancellationTokenSource.Cancel();
+ };
+
+ // Start the assistant
+ await assistant.StartAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ Console.WriteLine("\n👋 Voice assistant shut down. Goodbye!");
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Fatal error");
+ Console.WriteLine($"❌ Error: {ex.Message}");
+ }
+ }
+
+ private static bool CheckAudioSystem(ILogger logger)
+ {
+ try
+ {
+ // Try input (default device)
+ using (var waveIn = new WaveInEvent
+ {
+ WaveFormat = new WaveFormat(24000, 16, 1),
+ BufferMilliseconds = 50
+ })
+ {
+ // Start/Stop to force initialization and surface any device errors
+ waveIn.DataAvailable += (_, __) => { };
+ waveIn.StartRecording();
+ waveIn.StopRecording();
+ }
+
+ // Try output (default device)
+ var buffer = new BufferedWaveProvider(new WaveFormat(24000, 16, 1))
+ {
+ BufferDuration = TimeSpan.FromMilliseconds(200)
+ };
+
+ using (var waveOut = new WaveOutEvent { DesiredLatency = 100 })
+ {
+ waveOut.Init(buffer);
+ // Playing isn’t strictly required to validate a device, but it’s safe
+ waveOut.Play();
+ waveOut.Stop();
+ }
+
+ logger.LogInformation("Audio system check passed (default input/output initialized).");
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"❌ Audio system check failed: {ex.Message}");
+ return false;
+ }
+ }
+ }
+}
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/appsettings.json b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/appsettings.json
new file mode 100644
index 000000000000..f193a20b915a
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/appsettings.json
@@ -0,0 +1,6 @@
+{
+ "VoiceLive": {
+ "Voice": "en-US-AvaNeural",
+ "Instructions": "You are a helpful AI assistant. Respond naturally and conversationally. Keep your responses concise but engaging."
+ }
+}
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/appsettings.template.json b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/appsettings.template.json
new file mode 100644
index 000000000000..778f63db265d
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/BasicVoiceAssistant/appsettings.template.json
@@ -0,0 +1,15 @@
+{
+ "VoiceLive": {
+ "ApiKey": "your-api-key-here",
+ "Endpoint": "wss://api.voicelive.com/v1",
+ "Model": "gpt-4o-realtime-preview",
+ "Voice": "en-US-AvaNeural",
+ "Instructions": "You are a helpful AI assistant. Respond naturally and conversationally. Keep your responses concise but engaging."
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Azure.AI.VoiceLive": "Debug"
+ }
+ }
+}
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/.gitignore b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/.gitignore
new file mode 100644
index 000000000000..2945f93d5676
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/.gitignore
@@ -0,0 +1,66 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+launchsettings.json
+appsettings.Development.json
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Configuration files with secrets
+appsettings.json
+!appsettings.template.json
+
+# IDE files
+.vscode/
+.idea/
+
+# OS generated files
+.DS_Store
+.DS_Store?
+._*
+.Spotlight-V100
+.Trashes
+ehthumbs.db
+Thumbs.db
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/AudioProcessor.cs b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/AudioProcessor.cs
new file mode 100644
index 000000000000..ba16ce05fbe4
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/AudioProcessor.cs
@@ -0,0 +1,384 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Threading.Channels;
+using NAudio.Wave;
+
+namespace Azure.AI.VoiceLive.Samples;
+
+///
+/// Handles real-time audio capture and playback for the voice assistant.
+///
+///
+/// Threading Architecture:
+/// - Main thread: Event loop and UI
+/// - Capture thread: NAudio input stream reading
+/// - Send thread: Async audio data transmission to VoiceLive
+/// - Playback thread: NAudio output stream writing
+///
+public class AudioProcessor : IDisposable
+{
+ private readonly VoiceLiveSession _session;
+ private readonly ILogger _logger;
+
+ // Audio configuration - PCM16, 24kHz, mono as specified
+ private const int SampleRate = 24000;
+ private const int Channels = 1;
+ private const int BitsPerSample = 16;
+
+ // NAudio components
+ private WaveInEvent? _waveIn;
+ private WaveOutEvent? _waveOut;
+ private BufferedWaveProvider? _playbackBuffer;
+
+ // Audio capture and playback state
+ private bool _isCapturing;
+ private bool _isPlaying;
+
+ // Audio streaming channels
+ private readonly Channel _audioSendChannel;
+ private readonly Channel _audioPlaybackChannel;
+ private readonly ChannelWriter _audioSendWriter;
+ private readonly ChannelReader _audioSendReader;
+ private readonly ChannelWriter _audioPlaybackWriter;
+ private readonly ChannelReader _audioPlaybackReader;
+
+ // Background tasks
+ private Task? _audioSendTask;
+ private Task? _audioPlaybackTask;
+ private readonly CancellationTokenSource _cancellationTokenSource;
+ private CancellationTokenSource _playbackCancellationTokenSource;
+
+ ///
+ /// Initializes a new instance of the AudioProcessor class.
+ ///
+ /// The VoiceLive session for audio communication.
+ /// Logger for diagnostic information.
+ public AudioProcessor(VoiceLiveSession session, ILogger logger)
+ {
+ _session = session ?? throw new ArgumentNullException(nameof(session));
+ _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ // Create unbounded channels for audio data
+ _audioSendChannel = Channel.CreateUnbounded();
+ _audioSendWriter = _audioSendChannel.Writer;
+ _audioSendReader = _audioSendChannel.Reader;
+
+ _audioPlaybackChannel = Channel.CreateUnbounded();
+ _audioPlaybackWriter = _audioPlaybackChannel.Writer;
+ _audioPlaybackReader = _audioPlaybackChannel.Reader;
+
+ _cancellationTokenSource = new CancellationTokenSource();
+ _playbackCancellationTokenSource = new CancellationTokenSource();
+
+ _logger.LogInformation("AudioProcessor initialized with {SampleRate}Hz PCM16 mono audio", SampleRate);
+ }
+
+ ///
+ /// Start capturing audio from microphone.
+ ///
+ public Task StartCaptureAsync()
+ {
+ if (_isCapturing)
+ return Task.CompletedTask;
+
+ _isCapturing = true;
+
+ try
+ {
+ _waveIn = new WaveInEvent
+ {
+ WaveFormat = new WaveFormat(SampleRate, BitsPerSample, Channels),
+ BufferMilliseconds = 50 // 50ms buffer for low latency
+ };
+
+ _waveIn.DataAvailable += OnAudioDataAvailable;
+ _waveIn.RecordingStopped += OnRecordingStopped;
+
+ /*
+ _logger.LogInformation($"There are {WaveIn.DeviceCount} devices available.");
+ for (int i = 0; i < WaveIn.DeviceCount; i++)
+ {
+ var deviceInfo = WaveIn.GetCapabilities(i);
+
+ _logger.LogInformation($"{i}: {deviceInfo.ProductName}");
+ }
+ */
+ _waveIn.DeviceNumber = 0; // Default to first device
+
+ _waveIn.StartRecording();
+
+ // Start audio send task
+ _audioSendTask = ProcessAudioSendAsync(_cancellationTokenSource.Token);
+
+ _logger.LogInformation("Started audio capture");
+ return Task.CompletedTask;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to start audio capture");
+ _isCapturing = false;
+ throw;
+ }
+ }
+
+ ///
+ /// Stop capturing audio.
+ ///
+ public async Task StopCaptureAsync()
+ {
+ if (!_isCapturing)
+ return;
+
+ _isCapturing = false;
+
+ if (_waveIn != null)
+ {
+ _waveIn.StopRecording();
+ _waveIn.DataAvailable -= OnAudioDataAvailable;
+ _waveIn.RecordingStopped -= OnRecordingStopped;
+ _waveIn.Dispose();
+ _waveIn = null;
+ }
+
+ // Complete the send channel and wait for the send task
+ _audioSendWriter.TryComplete();
+ if (_audioSendTask != null)
+ {
+ await _audioSendTask.ConfigureAwait(false);
+ _audioSendTask = null;
+ }
+
+ _logger.LogInformation("Stopped audio capture");
+ }
+
+ ///
+ /// Initialize audio playback system.
+ ///
+ public Task StartPlaybackAsync()
+ {
+ if (_isPlaying)
+ return Task.CompletedTask;
+
+ _isPlaying = true;
+
+ try
+ {
+ _waveOut = new WaveOutEvent
+ {
+ DesiredLatency = 100 // 100ms latency
+ };
+
+ _playbackBuffer = new BufferedWaveProvider(new WaveFormat(SampleRate, BitsPerSample, Channels))
+ {
+ BufferDuration = TimeSpan.FromMinutes(5), // 5 second buffer
+ DiscardOnBufferOverflow = true
+ };
+
+ _waveOut.Init(_playbackBuffer);
+ _waveOut.Play();
+
+ _playbackCancellationTokenSource = new CancellationTokenSource();
+
+ // Start audio playback task
+ _audioPlaybackTask = ProcessAudioPlaybackAsync();
+
+ _logger.LogInformation("Audio playback system ready");
+ return Task.CompletedTask;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Failed to initialize audio playback");
+ _isPlaying = false;
+ throw;
+ }
+ }
+
+ ///
+ /// Stop audio playback and clear buffer.
+ ///
+ public async Task StopPlaybackAsync()
+ {
+ if (!_isPlaying)
+ return;
+
+ _isPlaying = false;
+
+ // Clear the playback channel
+ while (_audioPlaybackReader.TryRead(out _))
+ { }
+
+ if (_playbackBuffer != null)
+ {
+ _playbackBuffer.ClearBuffer();
+ }
+
+ if (_waveOut != null)
+ {
+ _waveOut.Stop();
+ _waveOut.Dispose();
+ _waveOut = null;
+ }
+
+ _playbackBuffer = null;
+
+ // Complete the playback channel and wait for the playback task
+ _playbackCancellationTokenSource.Cancel();
+
+ if (_audioPlaybackTask != null)
+ {
+ await _audioPlaybackTask.ConfigureAwait(false);
+ _audioPlaybackTask = null;
+ }
+
+ _logger.LogInformation("Stopped audio playback");
+ }
+
+ ///
+ /// Queue audio data for playback.
+ ///
+ /// The audio data to queue.
+ public async Task QueueAudioAsync(byte[] audioData)
+ {
+ if (_isPlaying && audioData.Length > 0)
+ {
+ await _audioPlaybackWriter.WriteAsync(audioData).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Event handler for audio data available from microphone.
+ ///
+ private void OnAudioDataAvailable(object? sender, WaveInEventArgs e)
+ {
+ if (_isCapturing && e.BytesRecorded > 0)
+ {
+ byte[] audioData = new byte[e.BytesRecorded];
+ Array.Copy(e.Buffer, 0, audioData, 0, e.BytesRecorded);
+
+ // Queue audio data for sending (non-blocking)
+ if (!_audioSendWriter.TryWrite(audioData))
+ {
+ _logger.LogWarning("Failed to queue audio data for sending - channel may be full");
+ }
+ }
+ }
+
+ ///
+ /// Event handler for recording stopped.
+ ///
+ private void OnRecordingStopped(object? sender, StoppedEventArgs e)
+ {
+ if (e.Exception != null)
+ {
+ _logger.LogError(e.Exception, "Audio recording stopped due to error");
+ }
+ }
+
+ ///
+ /// Background task to process audio data and send to VoiceLive service.
+ ///
+ private async Task ProcessAudioSendAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ await foreach (byte[] audioData in _audioSendReader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ break;
+
+ try
+ {
+ // Send audio data directly to the session
+ await _session.SendInputAudioAsync(audioData, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error sending audio data to VoiceLive");
+ // Continue processing other audio data
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Expected when cancellation is requested
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in audio send processing");
+ }
+ }
+
+ ///
+ /// Background task to process audio playback.
+ ///
+ private async Task ProcessAudioPlaybackAsync()
+ {
+ try
+ {
+ CancellationTokenSource combinedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_playbackCancellationTokenSource.Token, _cancellationTokenSource.Token);
+ var cancellationToken = combinedTokenSource.Token;
+
+ await foreach (byte[] audioData in _audioPlaybackReader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
+ {
+ if (cancellationToken.IsCancellationRequested)
+ break;
+
+ try
+ {
+ if (_playbackBuffer != null && _isPlaying)
+ {
+ _playbackBuffer.AddSamples(audioData, 0, audioData.Length);
+ }
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in audio playback");
+ // Continue processing other audio data
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Expected when cancellation is requested
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in audio playback processing");
+ }
+ }
+
+ ///
+ /// Clean up audio resources.
+ ///
+ public async Task CleanupAsync()
+ {
+ await StopCaptureAsync().ConfigureAwait(false);
+ await StopPlaybackAsync().ConfigureAwait(false);
+
+ _cancellationTokenSource.Cancel();
+
+ // Wait for background tasks to complete
+ var tasks = new List();
+ if (_audioSendTask != null)
+ tasks.Add(_audioSendTask);
+ if (_audioPlaybackTask != null)
+ tasks.Add(_audioPlaybackTask);
+
+ if (tasks.Count > 0)
+ {
+ await Task.WhenAll(tasks).ConfigureAwait(false);
+ }
+
+ _logger.LogInformation("Audio processor cleaned up");
+ }
+
+ ///
+ /// Dispose of resources.
+ ///
+ public void Dispose()
+ {
+ CleanupAsync().Wait();
+ _cancellationTokenSource.Dispose();
+ }
+}
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.cs b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.cs
new file mode 100644
index 000000000000..11938839c40d
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.cs
@@ -0,0 +1,602 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+namespace Azure.AI.VoiceLive.Samples;
+
+///
+/// Customer service voice bot implementing function calling with the VoiceLive SDK.
+///
+///
+/// This sample demonstrates how to build a sophisticated customer service bot that can:
+/// - Check order status and track shipments
+/// - Retrieve customer account information and history
+/// - Schedule technical support calls
+/// - Process returns and exchanges
+/// - Update shipping addresses for pending orders
+///
+/// The bot uses strongly-typed function definitions and provides real-time voice interaction
+/// with proper interruption handling and error recovery.
+///
+public class CustomerServiceBot : IDisposable
+{
+ private readonly VoiceLiveClient _client;
+ private readonly string _model;
+ private readonly string _voice;
+ private readonly string _instructions;
+ private readonly ICustomerServiceFunctions _functions;
+ private readonly ILogger _logger;
+ private readonly ILoggerFactory _loggerFactory;
+
+ private readonly HashSet _assistantMessageItems = new HashSet();
+ private readonly HashSet _assistantMessageResponses = new HashSet();
+
+ private VoiceLiveSession? _session;
+ private AudioProcessor? _audioProcessor;
+ private bool _disposed;
+
+ ///
+ /// Initializes a new instance of the CustomerServiceBot class.
+ ///
+ /// The VoiceLive client.
+ /// The model to use.
+ /// The voice to use.
+ /// The system instructions.
+ /// The customer service functions implementation.
+ /// Logger factory for creating loggers.
+ public CustomerServiceBot(
+ VoiceLiveClient client,
+ string model,
+ string voice,
+ string instructions,
+ ICustomerServiceFunctions functions,
+ ILoggerFactory loggerFactory)
+ {
+ _client = client ?? throw new ArgumentNullException(nameof(client));
+ _model = model ?? throw new ArgumentNullException(nameof(model));
+ _voice = voice ?? throw new ArgumentNullException(nameof(voice));
+ _instructions = instructions ?? throw new ArgumentNullException(nameof(instructions));
+ _functions = functions ?? throw new ArgumentNullException(nameof(functions));
+ _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
+ _logger = loggerFactory.CreateLogger();
+ }
+
+ ///
+ /// Start the customer service bot session.
+ ///
+ /// Cancellation token for stopping the session.
+ public async Task StartAsync(CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ _logger.LogInformation("Connecting to VoiceLive API with model {Model}", _model);
+
+ // Start VoiceLive session
+ _session = await _client.StartSessionAsync(_model, cancellationToken).ConfigureAwait(false);
+
+ // Initialize audio processor
+ _audioProcessor = new AudioProcessor(_session, _loggerFactory.CreateLogger());
+
+ // Configure session for voice conversation with function calling
+ await SetupSessionAsync(cancellationToken).ConfigureAwait(false);
+
+ // Start audio systems
+ await _audioProcessor.StartPlaybackAsync().ConfigureAwait(false);
+ await _audioProcessor.StartCaptureAsync().ConfigureAwait(false);
+
+ _logger.LogInformation("Customer service bot ready! Start speaking...");
+ Console.WriteLine();
+ Console.WriteLine("=" + new string('=', 69));
+ Console.WriteLine("🏢 CUSTOMER SERVICE BOT READY");
+ Console.WriteLine("I can help you with orders, returns, account info, and scheduling support calls");
+ Console.WriteLine("Start speaking to begin your customer service session");
+ Console.WriteLine("Press Ctrl+C to exit");
+ Console.WriteLine("=" + new string('=', 69));
+ Console.WriteLine();
+
+ // Process events
+ await ProcessEventsAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogInformation("Received cancellation signal, shutting down...");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Connection error");
+ throw;
+ }
+ finally
+ {
+ // Cleanup
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.CleanupAsync().ConfigureAwait(false);
+ }
+ }
+ }
+
+ ///
+ /// Configure the VoiceLive session for customer service with function calling.
+ ///
+ private async Task SetupSessionAsync(CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Setting up customer service session with function calling...");
+
+ // Azure voice configuration
+ var azureVoice = new AzureStandardVoice(_voice, AzureStandardVoiceType.AzureStandard);
+
+ // Create strongly typed turn detection configuration
+ var turnDetectionConfig = new ServerVad
+ {
+ Threshold = 0.5f,
+ PrefixPaddingMs = 300,
+ SilenceDurationMs = 500
+ };
+
+ // Create conversation session options with function tools
+ var sessionOptions = new SessionOptions
+ {
+ Model = _model,
+ Instructions = _instructions,
+ Voice = azureVoice,
+ InputAudioFormat = AudioFormat.Pcm16,
+ OutputAudioFormat = AudioFormat.Pcm16,
+ TurnDetection = turnDetectionConfig
+ };
+
+ // Ensure modalities include audio
+ sessionOptions.Modalities.Clear();
+ sessionOptions.Modalities.Add(InputModality.Text);
+ sessionOptions.Modalities.Add(InputModality.Audio);
+
+ // Add function tools for customer service operations
+ sessionOptions.Tools.Add(CreateCheckOrderStatusTool());
+ sessionOptions.Tools.Add(CreateGetCustomerInfoTool());
+ sessionOptions.Tools.Add(CreateScheduleSupportCallTool());
+ sessionOptions.Tools.Add(CreateInitiateReturnProcessTool());
+ sessionOptions.Tools.Add(CreateUpdateShippingAddressTool());
+
+
+ await _session!.ConfigureConversationSessionAsync(sessionOptions, cancellationToken).ConfigureAwait(false);
+
+ _logger.LogInformation("Session configuration sent with {ToolCount} customer service tools", sessionOptions.Tools.Count);
+ }
+
+ ///
+ /// Create the check order status function tool.
+ ///
+ private FunctionTool CreateCheckOrderStatusTool()
+ {
+ var parameters = new
+ {
+ type = "object",
+ properties = new
+ {
+ order_number = new
+ {
+ type = "string",
+ description = "The customer's order number (required)"
+ },
+ email = new
+ {
+ type = "string",
+ description = "Customer's email address if order number is not available"
+ }
+ },
+ required = new[] { "order_number" }
+ };
+
+ return new FunctionTool("check_order_status")
+ {
+ Description = "Check the status of a customer order by order number or email. Use this when customers ask about their order status, shipping, or delivery information.",
+ Parameters = BinaryData.FromObjectAsJson(parameters)
+ };
+ }
+
+ ///
+ /// Create the get customer info function tool.
+ ///
+ private FunctionTool CreateGetCustomerInfoTool()
+ {
+ var parameters = new
+ {
+ type = "object",
+ properties = new
+ {
+ customer_id = new
+ {
+ type = "string",
+ description = "Customer ID or email address to look up"
+ },
+ include_history = new
+ {
+ type = "boolean",
+ description = "Whether to include recent purchase history in the response",
+ @default = false
+ }
+ },
+ required = new[] { "customer_id" }
+ };
+
+ return new FunctionTool("get_customer_info")
+ {
+ Description = "Retrieve customer account information and optionally their purchase history. Use this when customers ask about their account details or past orders.",
+ Parameters = BinaryData.FromObjectAsJson(parameters)
+ };
+ }
+
+ ///
+ /// Create the schedule support call function tool.
+ ///
+ private FunctionTool CreateScheduleSupportCallTool()
+ {
+ var parameters = new
+ {
+ type = "object",
+ properties = new
+ {
+ customer_id = new
+ {
+ type = "string",
+ description = "Customer identifier (ID or email)"
+ },
+ preferred_time = new
+ {
+ type = "string",
+ description = "Preferred call time in ISO format (optional)"
+ },
+ issue_category = new
+ {
+ type = "string",
+ @enum = new[] { "technical", "billing", "warranty", "returns" },
+ description = "Category of the support issue"
+ },
+ urgency = new
+ {
+ type = "string",
+ @enum = new[] { "low", "medium", "high", "critical" },
+ description = "Urgency level of the issue",
+ @default = "medium"
+ },
+ description = new
+ {
+ type = "string",
+ description = "Brief description of the issue the customer needs help with"
+ }
+ },
+ required = new[] { "customer_id", "issue_category", "description" }
+ };
+
+ return new FunctionTool("schedule_support_call")
+ {
+ Description = "Schedule a technical support call with a specialist. Use this when customers need to speak with a technical expert about complex issues.",
+ Parameters = BinaryData.FromObjectAsJson(parameters)
+ };
+ }
+
+ ///
+ /// Create the initiate return process function tool.
+ ///
+ private FunctionTool CreateInitiateReturnProcessTool()
+ {
+ var parameters = new
+ {
+ type = "object",
+ properties = new
+ {
+ order_number = new
+ {
+ type = "string",
+ description = "Original order number for the return"
+ },
+ product_id = new
+ {
+ type = "string",
+ description = "Specific product ID to return from the order"
+ },
+ reason = new
+ {
+ type = "string",
+ @enum = new[] { "defective", "wrong_item", "not_satisfied", "damaged_shipping" },
+ description = "Reason for the return"
+ },
+ return_type = new
+ {
+ type = "string",
+ @enum = new[] { "refund", "exchange", "store_credit" },
+ description = "Type of return requested by the customer"
+ }
+ },
+ required = new[] { "order_number", "product_id", "reason", "return_type" }
+ };
+
+ return new FunctionTool("initiate_return_process")
+ {
+ Description = "Start the return/exchange process for a product. Use this when customers want to return or exchange items they've purchased.",
+ Parameters = BinaryData.FromObjectAsJson(parameters)
+ };
+ }
+
+ ///
+ /// Create the update shipping address function tool.
+ ///
+ private FunctionTool CreateUpdateShippingAddressTool()
+ {
+ var parameters = new
+ {
+ type = "object",
+ properties = new
+ {
+ order_number = new
+ {
+ type = "string",
+ description = "Order number to update the shipping address for"
+ },
+ new_address = new
+ {
+ type = "object",
+ properties = new
+ {
+ street = new { type = "string", description = "Street address" },
+ city = new { type = "string", description = "City name" },
+ state = new { type = "string", description = "State or province" },
+ zip_code = new { type = "string", description = "ZIP or postal code" },
+ country = new { type = "string", description = "Country code", @default = "US" }
+ },
+ required = new[] { "street", "city", "state", "zip_code" },
+ description = "New shipping address information"
+ }
+ },
+ required = new[] { "order_number", "new_address" }
+ };
+
+ return new FunctionTool("update_shipping_address")
+ {
+ Description = "Update shipping address for pending orders. Use this when customers need to change where their order will be delivered.",
+ Parameters = BinaryData.FromObjectAsJson(parameters)
+ };
+ }
+
+ ///
+ /// Process events from the VoiceLive session.
+ ///
+ private async Task ProcessEventsAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ await foreach (ServerEvent serverEvent in _session!.GetUpdatesAsync(cancellationToken).ConfigureAwait(false))
+ {
+ await HandleServerEventAsync(serverEvent, cancellationToken).ConfigureAwait(false);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ _logger.LogInformation("Event processing cancelled");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error processing events");
+ throw;
+ }
+ }
+
+ ///
+ /// Handle different types of server events from VoiceLive.
+ ///
+ private async Task HandleServerEventAsync(ServerEvent serverEvent, CancellationToken cancellationToken)
+ {
+ _logger.LogDebug("Received event: {EventType}", serverEvent.GetType().Name);
+
+ switch (serverEvent)
+ {
+ case ServerEventSessionCreated sessionCreated:
+ await HandleSessionCreatedAsync(sessionCreated, cancellationToken).ConfigureAwait(false);
+ break;
+
+ case ServerEventSessionUpdated sessionUpdated:
+ _logger.LogInformation("Session updated successfully with function tools");
+
+ // Start audio capture once session is ready
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StartCaptureAsync().ConfigureAwait(false);
+ }
+ break;
+
+ case ServerEventInputAudioBufferSpeechStarted speechStarted:
+ _logger.LogInformation("🎤 Customer started speaking - stopping playback");
+ Console.WriteLine("🎤 Listening...");
+
+ // Stop current assistant audio playback (interruption handling)
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StopPlaybackAsync().ConfigureAwait(false);
+ }
+
+ // Cancel any ongoing response
+ try
+ {
+ await _session!.CancelResponseAsync(cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "No response to cancel");
+ }
+ break;
+
+ case ServerEventInputAudioBufferSpeechStopped speechStopped:
+ _logger.LogInformation("🎤 Customer stopped speaking");
+ Console.WriteLine("🤔 Processing...");
+
+ // Restart playback system for response
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StartPlaybackAsync().ConfigureAwait(false);
+ }
+ break;
+
+ case ServerEventResponseCreated responseCreated:
+ _logger.LogInformation("🤖 Assistant response created");
+ break;
+
+ case ServerEventResponseOutputItemAdded outputItemAdded:
+ await HandleResponseOutputItemAddedAsync(outputItemAdded, cancellationToken).ConfigureAwait(false);
+ break;
+
+ case ServerEventResponseAudioDelta audioDelta:
+ // Stream audio response to speakers
+ _logger.LogDebug("Received audio delta");
+
+ if (audioDelta.Delta != null && _audioProcessor != null)
+ {
+ byte[] audioData = audioDelta.Delta.ToArray();
+ await _audioProcessor.QueueAudioAsync(audioData).ConfigureAwait(false);
+ }
+ break;
+
+ case ServerEventResponseAudioDone audioDone:
+ _logger.LogInformation("🤖 Assistant finished speaking");
+ Console.WriteLine("🎤 Ready for next customer inquiry...");
+ break;
+
+ case ServerEventResponseContentPartAdded partAdded:
+ if (_assistantMessageItems.Contains(partAdded.ItemId))
+ {
+ _assistantMessageResponses.Add(partAdded.ResponseId);
+ }
+
+ break;
+ case ServerEventResponseDone responseDone:
+ _logger.LogInformation("✅ Response complete");
+ break;
+ case ServerEventResponseFunctionCallArgumentsDone functionCallArgumentsDone:
+ _logger.LogInformation("🔧 Function call arguments done for call ID: {CallId}", functionCallArgumentsDone.CallId);
+ await HandleFunctionCallAsync(functionCallArgumentsDone.Name, functionCallArgumentsDone.CallId, functionCallArgumentsDone.Arguments, cancellationToken).ConfigureAwait(false);
+ break;
+ case ServerEventResponseAudioTranscriptDelta transcriptDelta:
+ // For now, only deal with the assistant responses.
+ if (_assistantMessageResponses.Contains(transcriptDelta.ResponseId))
+ {
+ Console.Write($"{transcriptDelta.Delta}");
+ }
+ break;
+
+ case ServerEventResponseAudioTranscriptDone transcriptDone:
+ // For now, only deal with the assistant responses.
+ if (_assistantMessageResponses.Contains(transcriptDone.ResponseId))
+ {
+ Console.WriteLine();
+ }
+ break;
+ case ServerEventError errorEvent:
+ _logger.LogError("❌ VoiceLive error: {ErrorMessage}", errorEvent.Error?.Message);
+ Console.WriteLine($"Error: {errorEvent.Error?.Message}");
+ break;
+
+ default:
+ _logger.LogDebug("Unhandled event type: {EventType}", serverEvent.GetType().Name);
+ break;
+ }
+ }
+
+ ///
+ /// Handle response output item added events, including function calls.
+ ///
+ private async Task HandleResponseOutputItemAddedAsync(ServerEventResponseOutputItemAdded outputItemAdded, CancellationToken cancellationToken)
+ {
+ if (outputItemAdded.Item is ConversationItemWithReference item &&
+ item.Type == ConversationItemWithReferenceType.FunctionCall)
+ {
+ // This is a function call item, extract the details
+ var functionName = item.Name;
+ var callId = item.CallId;
+ var arguments = item.Arguments;
+
+ if (!string.IsNullOrEmpty(functionName) && !string.IsNullOrEmpty(callId) && !string.IsNullOrEmpty(arguments))
+ {
+ await HandleFunctionCallAsync(functionName, callId, arguments, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ _logger.LogWarning("Function call item missing required properties: Name={Name}, CallId={CallId}, Arguments={Arguments}",
+ functionName, callId, arguments);
+ }
+ }
+ else if (outputItemAdded.Item is ConversationItemWithReference messageItem &&
+ messageItem.Type == ConversationItemWithReferenceType.Message &&
+ messageItem.Role == ConversationItemWithReferenceRole.Assistant)
+ {
+ // Keep track of the items that are from the assistant, so we know how to display the conversation.
+ _assistantMessageItems.Add(messageItem.Id);
+ }
+ }
+
+ ///
+ /// Handle function call execution and send results back to the session.
+ ///
+ private async Task HandleFunctionCallAsync(string functionName, string callId, string arguments, CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("🔧 Executing function: {FunctionName}", functionName);
+ Console.WriteLine($"🔧 Looking up {functionName.Replace("_", " ")}...");
+
+ try
+ {
+ // Execute the function through our business logic layer
+ var result = await _functions.ExecuteFunctionAsync(functionName, arguments, cancellationToken).ConfigureAwait(false);
+
+ // Create function call output item using the model factory
+ var outputItem = new ConversationItemWithReference()
+ {
+ Type = ConversationItemWithReferenceType.FunctionCallOutput,
+ CallId = callId,
+ Output = JsonSerializer.Serialize(result)
+ };
+
+ // Add the result to the conversation
+ await _session!.AddItemAsync(outputItem, cancellationToken).ConfigureAwait(false);
+ await _session!.StartResponseAsync(cancellationToken).ConfigureAwait(false);
+
+ _logger.LogInformation("✅ Function {FunctionName} completed successfully", functionName);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "❌ Function {FunctionName} execution failed", functionName);
+
+ // Send error response
+ var errorResult = new { success = false, error = "I'm sorry, I'm having trouble accessing that information right now. Please try again in a moment." };
+ var outputItem = new ConversationItemWithReference();
+ outputItem.Type = ConversationItemWithReferenceType.FunctionCallOutput;
+ outputItem.CallId = callId;
+ outputItem.Output = JsonSerializer.Serialize(errorResult);
+
+ await _session!.AddItemAsync(outputItem, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Handle session created event.
+ ///
+ private async Task HandleSessionCreatedAsync(ServerEventSessionCreated sessionCreated, CancellationToken cancellationToken)
+ {
+ _logger.LogInformation("Session ready: {SessionId}", sessionCreated.Session?.Id);
+
+ // Start audio capture once session is ready
+ if (_audioProcessor != null)
+ {
+ await _audioProcessor.StartCaptureAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Dispose of resources.
+ ///
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ _audioProcessor?.Dispose();
+ _session?.Dispose();
+ _disposed = true;
+ }
+}
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.csproj b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.csproj
new file mode 100644
index 000000000000..a2a542fb77f5
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.csproj
@@ -0,0 +1,42 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+ CustomerServiceBot
+ Azure.AI.VoiceLive.Samples
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.sln b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.sln
new file mode 100644
index 000000000000..817844e0fdfa
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceBot.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CustomerServiceBot", "CustomerServiceBot.csproj", "{12345678-1234-1234-1234-123456789012}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {12345678-1234-1234-1234-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {12345678-1234-1234-1234-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {12345678-1234-1234-1234-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {12345678-1234-1234-1234-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
\ No newline at end of file
diff --git a/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceFunctions.cs b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceFunctions.cs
new file mode 100644
index 000000000000..4536930efd61
--- /dev/null
+++ b/sdk/ai/Azure.AI.VoiceLive/samples/CustomerServiceBot/CustomerServiceFunctions.cs
@@ -0,0 +1,464 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using static Azure.AI.VoiceLive.Samples.FunctionModels;
+using static Azure.AI.VoiceLive.Samples.SampleData;
+
+namespace Azure.AI.VoiceLive.Samples;
+
+///
+/// Interface for customer service function execution.
+///
+public interface ICustomerServiceFunctions
+{
+ ///
+ /// Executes a function by name with JSON arguments.
+ ///
+ ///
+ ///
+ ///
+ ///
+ Task