2025-04-07 04:15:27 +00:00
|
|
|
using BotSharp.Abstraction.Conversations.Enums;
|
|
|
|
|
using BotSharp.Abstraction.Conversations.Models;
|
|
|
|
|
using BotSharp.Abstraction.Conversations;
|
|
|
|
|
using BotSharp.OpenAPI;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
|
|
|
|
|
var services = ServiceBuilder.CreateHostBuilder();
|
|
|
|
|
var channel = services.GetRequiredService<IStreamChannel>();
|
|
|
|
|
|
|
|
|
|
Console.WriteLine("PCM-16 Microphone Capture (24kHz Sample Rate)");
|
|
|
|
|
Console.WriteLine("-----------------------------------------------");
|
|
|
|
|
|
|
|
|
|
var convService = services.GetRequiredService<IConversationService>();
|
|
|
|
|
var conv = new Conversation
|
|
|
|
|
{
|
|
|
|
|
AgentId = "01e2fc5c-2c89-4ec7-8470-7688608b496c",
|
|
|
|
|
Channel = ConversationChannel.Phone,
|
|
|
|
|
Title = $"Test",
|
|
|
|
|
Tags = [],
|
|
|
|
|
};
|
|
|
|
|
conv = await convService.NewConversation(conv);
|
|
|
|
|
|
|
|
|
|
await channel.ConnectAsync(conv.Id);
|
|
|
|
|
|
2025-04-08 01:16:47 +00:00
|
|
|
var settings = services.GetRequiredService<RealtimeModelSettings>();
|
2025-04-07 04:15:27 +00:00
|
|
|
var hub = services.GetRequiredService<IRealtimeHub>();
|
|
|
|
|
var conn = hub.SetHubConnection(conv.Id);
|
|
|
|
|
|
2025-04-11 21:29:44 +00:00
|
|
|
conn.OnModelReady = () =>
|
|
|
|
|
JsonSerializer.Serialize(new
|
2025-04-07 04:15:27 +00:00
|
|
|
{
|
2025-04-11 21:29:44 +00:00
|
|
|
@event = "init"
|
|
|
|
|
});
|
2025-04-07 04:15:27 +00:00
|
|
|
|
|
|
|
|
conn.OnModelMessageReceived = message =>
|
|
|
|
|
JsonSerializer.Serialize(new
|
|
|
|
|
{
|
|
|
|
|
@event = "media",
|
|
|
|
|
media = message
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
conn.OnModelAudioResponseDone = () =>
|
|
|
|
|
JsonSerializer.Serialize(new
|
|
|
|
|
{
|
|
|
|
|
@event = "mark",
|
|
|
|
|
mark = new { name = "responsePart" }
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
conn.OnModelUserInterrupted = () =>
|
|
|
|
|
JsonSerializer.Serialize(new
|
|
|
|
|
{
|
|
|
|
|
@event = "clear"
|
|
|
|
|
});
|
|
|
|
|
|
2025-04-11 21:29:44 +00:00
|
|
|
await hub.ConnectToModel(async data =>
|
|
|
|
|
{
|
|
|
|
|
var response = JsonSerializer.Deserialize<ModelResponseEvent>(data);
|
|
|
|
|
if (response.Event == "clear")
|
|
|
|
|
{
|
|
|
|
|
channel.ClearBuffer();
|
|
|
|
|
}
|
|
|
|
|
else if (response.Event == "media")
|
|
|
|
|
{
|
|
|
|
|
var message = JsonSerializer.Deserialize<ModelResponseMediaEvent>(data);
|
|
|
|
|
await channel.SendAsync(Convert.FromBase64String(message.Media), CancellationToken.None);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
StreamReceiveResult result;
|
|
|
|
|
var buffer = new byte[1024 * 8];
|
|
|
|
|
|
2025-04-07 04:15:27 +00:00
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
var seg = new ArraySegment<byte>(buffer);
|
|
|
|
|
result = await channel.ReceiveAsync(seg, CancellationToken.None);
|
|
|
|
|
|
2025-04-08 01:16:47 +00:00
|
|
|
await hub.Completer.AppenAudioBuffer(seg, result.Count);
|
2025-04-07 04:15:27 +00:00
|
|
|
|
|
|
|
|
// Display the audio level
|
|
|
|
|
int audioLevel = CalculateAudioLevel(buffer, result.Count);
|
|
|
|
|
DisplayAudioLevel(audioLevel);
|
|
|
|
|
} while (result.Status == StreamChannelStatus.Open);
|
|
|
|
|
|
|
|
|
|
int CalculateAudioLevel(byte[] buffer, int bytesRecorded)
|
|
|
|
|
{
|
|
|
|
|
// Simple audio level calculation (RMS)
|
2025-04-11 21:29:44 +00:00
|
|
|
int bytesPerSample = 2; // 16-bit PCM = 2 bytes per sample
|
|
|
|
|
int sampleCount = bytesRecorded / bytesPerSample;
|
|
|
|
|
if (sampleCount == 0) return 0;
|
|
|
|
|
|
|
|
|
|
double sum = 0;
|
2025-04-07 04:15:27 +00:00
|
|
|
for (int i = 0; i < bytesRecorded; i += 2)
|
|
|
|
|
{
|
|
|
|
|
if (i + 1 < bytesRecorded)
|
|
|
|
|
{
|
|
|
|
|
short sample = (short)((buffer[i + 1] << 8) | buffer[i]);
|
2025-04-11 21:29:44 +00:00
|
|
|
double normalized = sample / (short.MaxValue * 1.0 + 1);
|
|
|
|
|
sum += normalized * normalized;
|
2025-04-07 04:15:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-04-11 21:29:44 +00:00
|
|
|
|
|
|
|
|
double rms = Math.Sqrt(sum / sampleCount);
|
|
|
|
|
double db = 20 * Math.Log10(rms);
|
|
|
|
|
|
|
|
|
|
if (double.IsInfinity(db) || double.IsNaN(db))
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db = Math.Clamp(db, -100, 0);
|
|
|
|
|
return (int)((db + 100) * 1);
|
2025-04-07 04:15:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DisplayAudioLevel(int level)
|
|
|
|
|
{
|
2025-04-11 21:29:44 +00:00
|
|
|
const int sep = 50;
|
2025-04-07 04:15:27 +00:00
|
|
|
// Normalize level to 0-50 range for display
|
2025-04-11 21:29:44 +00:00
|
|
|
int displayLevel = (level * sep) / 100;
|
2025-04-07 04:15:27 +00:00
|
|
|
|
|
|
|
|
// Clear the current line
|
|
|
|
|
Console.Write("\r" + new string(' ', 60));
|
|
|
|
|
|
|
|
|
|
// Display audio level as a bar
|
|
|
|
|
Console.Write("\rMicrophone: [");
|
|
|
|
|
Console.Write(new string('#', displayLevel));
|
2025-04-11 21:29:44 +00:00
|
|
|
Console.Write(new string(' ', sep - displayLevel));
|
2025-04-07 04:15:27 +00:00
|
|
|
Console.Write("]");
|
|
|
|
|
}
|