Callum.Voice
0.0.1
See the version list below for details.
dotnet add package Callum.Voice --version 0.0.1
NuGet\Install-Package Callum.Voice -Version 0.0.1
<PackageReference Include="Callum.Voice" Version="0.0.1" />
<PackageVersion Include="Callum.Voice" Version="0.0.1" />
<PackageReference Include="Callum.Voice" />
paket add Callum.Voice --version 0.0.1
#r "nuget: Callum.Voice, 0.0.1"
#:package Callum.Voice@0.0.1
#addin nuget:?package=Callum.Voice&version=0.0.1
#tool nuget:?package=Callum.Voice&version=0.0.1
Callum.Voice — کتابخانه صوت بلادرنگ (Real-Time Voice Chat)
کتابخانهی .NET برای ارتباط صوتی بلادرنگ چندسکویی از طریق سرور Go (phonil-opus).
تمام منطق ضبط میکروفن، پخش صدا، اتصال WebSocket، میکس صدای چند کاربر، تشخیص صحبت و مدیریت اتاق در این کتابخانه پیادهسازی شده است.
فهرست
- معماری کتابخانه
- ساختار فایلها
- پروتکل ارتباط
- پلتفرمهای پشتیبانیشده
- API Reference
- VoiceConfig
- Events
- نکات مهم
معماری کتابخانه
┌──────────────────────────────────────────────────────────┐
│ Callum.Voice │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ VoiceClient │────▶│ IAudioEngine │────▶│ Platform │ │
│ │ │◀────│ (interface) │◀────│ Audio │ │
│ │ • WebSocket │ │ │ │ Engine │ │
│ │ • Room Mgmt │ │ • Capture │ │ │ │
│ │ • Peer Track│ │ • Playback │ │ • WASAPI │ │
│ │ • Speaking │ │ • Volume │ │ • AVAudio│ │
│ │ • Reconnect │ │ • Power Mgmt │ │ • AudioR.│ │
│ └─────────────┘ └──────────────┘ └──────────┘ │
│ │ ▲ │
│ │ WebSocket (PCM 16-bit mono) │ │
│ ▼ │ │
│ ┌─────────────┐ │ │
│ │ Go Server │────── PCM Audio ────────────┘ │
│ │ (phonil-opus)│ │
│ └─────────────┘ │
└──────────────────────────────────────────────────────────┘
لایهها
| لایه | مسئولیت |
|---|---|
| VoiceClient | منطق شبکه (WebSocket)، مدیریت اتاق، ردیابی کاربران، تشخیص صحبت، اتصال مجدد خودکار |
| IAudioEngine | رابط (interface) برای ضبط و پخش صدا — هر پلتفرم پیادهسازی خاص خود را دارد |
| AudioEngine (Platform) | پیادهسازی اختصاصی هر پلتفرم: Android (AudioRecord/AudioTrack)، iOS (AVAudioEngine)، Windows (WASAPI) |
ساختار فایلها
Callum.Voice/
├── VoiceClient.cs # منطق اصلی: WebSocket، اتاق، میکس، تشخیص صحبت
├── VoiceConfig.cs # تنظیمات اتصال (سرور، API Key، نرخ نمونهبرداری)
├── VoiceClientState.cs # وضعیتهای اتصال (Disconnected → Connected → InRoom)
├── IAudioEngine.cs # رابط انتزاعی موتور صدا
├── Platforms/
│ ├── Android/
│ │ ├── AudioEngine.cs # ضبط/پخش با AudioRecord/AudioTrack + Ring Buffer
│ │ └── VoiceForegroundService.cs # سرویس پسزمینه Android
│ ├── iOS/
│ │ └── AudioEngine.cs # ضبط/پخش با AVAudioEngine + AVAudioPlayerNode
│ ├── MacCatalyst/
│ │ └── (از همان iOS engine استفاده میشود)
│ └── Windows/
│ └── AudioEngine.cs # ضبط/پخش با WASAPI (COM Interop)
└── Callum.Voice.csproj
پروتکل ارتباط
WebSocket Connection
ws(s)://{server}/ws?room=__lobby__&peer={peerId}&api_key={apiKey}
پیام متنی (JSON) — ورود به اتاق
{
"type": "join",
"room": "room-id",
"peer": "user-123",
"sampleRate": 48000
}
پیام باینری (صدا) — فرمت بسته
[1B RoomLen][RoomID][1B PeerLen][PeerID][PCM Audio Data]
- PCM Format: 16-bit signed integer, mono, 48 kHz
- هر بسته شامل شناسه اتاق، شناسه فرستنده و دادهی صوتی خام است
پلتفرمهای پشتیبانیشده
۱. .NET MAUI
وضعیت: پشتیبانی کامل — آماده تولید
کتابخانه در حال حاضر با <UseMaui>true</UseMaui> بیلد میشود و شامل پیادهسازی اختصاصی برای Android، iOS، MacCatalyst و Windows است.
Target Frameworks:
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst;net10.0-windows10.0.19041.0</TargetFrameworks>
مثال MAUI — صفحه اصلی
using Callum.Voice;
public partial class VoicePage : ContentPage
{
private VoiceClient? _client;
public VoicePage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
base.OnAppearing();
// 1. ساخت موتور صدا (پیادهسازی خودکار بر اساس پلتفرم)
var audio = new AudioEngine(sampleRate: 48_000);
// 2. ساخت کلاینت
var config = new VoiceConfig
{
Server = "callem.cloudfort.ir",
ApiKey = "vc_live_YOUR_API_KEY",
PeerId = $"user-{Guid.NewGuid():N}",
AutoReconnect = true,
};
_client = new VoiceClient(config, audio);
// 3. ثبت رویدادها
_client.Connected += () =>
MainThread.BeginInvokeOnMainThread(() =>
lblStatus.Text = "Connected");
_client.PeerJoined += peerId =>
MainThread.BeginInvokeOnMainThread(() =>
lblPeers.Text += $"\n{peerId}");
_client.PeerSpeaking += peerId =>
MainThread.BeginInvokeOnMainThread(() =>
lblSpeaking.Text = $"{peerId} is speaking...");
_client.Error += ex =>
MainThread.BeginInvokeOnMainThread(() =>
lblStatus.Text = $"Error: {ex.Message}");
// 4. اتصال و ورود به اتاق
await _client.ConnectAsync();
await _client.JoinRoomAsync("general");
// 5. فعالسازی میکروفن
await _client.EnableMicAsync();
}
private async void OnToggleMic(object sender, EventArgs e)
{
bool isOn = await _client!.ToggleMicAsync();
btnMic.Text = isOn ? "🎤 Mute" : "🎤 Unmute";
}
protected override void OnDisappearing()
{
_client?.Dispose();
base.OnDisappearing();
}
}
۲. WPF (Windows)
وضعیت: قابل استفاده — نیاز به پیادهسازی IAudioEngine مستقل از MAUI
کتابخانه فعلی <UseMaui>true</UseMaui> دارد. برای WPF دو راه وجود دارد:
راه اول: استفاده مستقیم از AudioEngine ویندوز (توصیه میشود)
کلاس AudioEngine ویندوز (WASAPI) در Platforms/Windows/AudioEngine.cs کاملاً مستقل از MAUI است و فقط از System.Runtime.InteropServices و WASAPI COM Interop استفاده میکند.
مراحل:
- کتابخانه را با target
net10.0-windows10.0.19041.0بیلد بگیرید (بدون نیاز به MAUI SDK) - یا کلاس
AudioEngineویندوز را در پروژه WPF خود کپی کنید
مثال WPF
using System.Windows;
using Callum.Voice;
namespace MyWpfApp;
public partial class MainWindow : Window
{
private VoiceClient? _client;
public MainWindow()
{
InitializeComponent();
}
private async void BtnConnect_Click(object sender, RoutedEventArgs e)
{
// AudioEngine ویندوز (WASAPI) — بدون وابستگی به MAUI
var audio = new AudioEngine(sampleRate: 48_000);
var config = new VoiceConfig
{
Server = "callem.cloudfort.ir",
ApiKey = "vc_live_YOUR_API_KEY",
PeerId = $"wpf-user-{Guid.NewGuid():N}",
};
_client = new VoiceClient(config, audio);
_client.Connected += () =>
Dispatcher.Invoke(() => txtStatus.Text = "Connected");
_client.PeerSpeaking += peerId =>
Dispatcher.Invoke(() => txtSpeaking.Text = $"{peerId} speaking");
await _client.ConnectAsync();
await _client.JoinRoomAsync("general");
await _client.EnableMicAsync();
}
private async void BtnToggleMic_Click(object sender, RoutedEventArgs e)
{
bool isOn = await _client!.ToggleMicAsync();
btnMic.Content = isOn ? "Mute" : "Unmute";
}
protected override void OnClosed(EventArgs e)
{
_client?.Dispose();
base.OnClosed(e);
}
}
نکته: برای بیلد بدون MAUI، باید
Callum.Voice.csprojرا اصلاح کنید:<TargetFrameworks>net10.0-windows10.0.19041.0</TargetFrameworks> <UseMaui>false</UseMaui>و فقط فایلهای
Platforms/Windows/AudioEngine.csو فایلهای مشترک را نگه دارید.
راه دوم: پیادهسازی IAudioEngine سفارشی برای WPF
using Callum.Voice;
using NAudio.Wave; // NuGet: NAudio
public class WpfAudioEngine : IAudioEngine
{
private WaveInEvent? _waveIn;
private WaveOutEvent? _waveOut;
// ... پیادهسازی با NAudio
public int SampleRate => 48_000;
public event Action<short[]>? AudioCaptured;
public event Action<string>? ErrorOccurred;
public void StartCapture()
{
_waveIn = new WaveInEvent { WaveFormat = new WaveFormat(48000, 16, 1) };
_waveIn.DataAvailable += (s, e) =>
{
var samples = new short[e.BytesRecorded / 2];
Buffer.BlockCopy(e.Buffer, 0, samples, 0, e.BytesRecorded);
AudioCaptured?.Invoke(samples);
};
_waveIn.StartRecording();
}
public void StopCapture() => _waveIn?.StopRecording();
public void StartPlayback() { /* ... */ }
public void StopPlayback() { /* ... */ }
public void EnqueuePeerAudio(string peerId, short[] samples) { /* ... */ }
public void RemovePeer(string peerId) { /* ... */ }
public void ClearPeers() { /* ... */ }
public void SetPlaybackVolume(float volume) { /* ... */ }
public void KeepScreenAwake() { /* no-op on WPF */ }
public void ReleaseScreenAwake() { /* no-op on WPF */ }
public Task<bool> RequestMicrophonePermission() => Task.FromResult(true);
public void Dispose() { StopCapture(); StopPlayback(); }
}
۳. ASP.NET Core MVC
وضعیت: فقط سمت سرور — بدون ضبط/پخش صدا
ASP.NET Core یک فریمورک سمت سرور است و دسترسی مستقیم به میکروفن/اسپیکر ندارد. اما میتواند:
- سرور واسط برای مسیریابی صدا باشد
- SDK جاوااسکریپت (
voice-chat-sdk.js) را به مرورگر ارائه دهد - از
VoiceClientبرای اتصال سرور به اتاق (مثلاً برای ضبط یا نظارت) استفاده کند
مثال ASP.NET Core — ارائه SDK به مرورگر
// Controllers/VoiceController.cs
using Microsoft.AspNetCore.Mvc;
namespace MyWebApp.Controllers;
public class VoiceController : Controller
{
public IActionResult Chat()
{
// صفحه HTML با SDK جاوااسکریپت
return View();
}
}
<!DOCTYPE html>
<html>
<head>
<title>Voice Chat</title>
<script src="~/js/voice-chat-sdk.js"></script>
</head>
<body>
<h1>Voice Chat Room</h1>
<button onclick="join()">Join</button>
<button onclick="toggleMic()">Toggle Mic</button>
<script>
const client = new VoiceChatSDK({
server: 'callem.cloudfort.ir',
apiKey: 'vc_live_YOUR_API_KEY',
peerId: 'web-user-@ViewBag.UserId',
});
client.on('connected', () => client.joinRoom('general'));
client.on('peer-speaking', (id) => console.log(`${id} speaking`));
client.connect();
async function join() {
await client.joinRoom('general');
await client.enableMic();
}
async function toggleMic() { await client.toggleMic(); }
</script>
</body>
</html>
مثال ASP.NET Core — استفاده از VoiceClient سمت سرور (نظارت/ضبط)
// Services/VoiceMonitor.cs
using Callum.Voice;
public class VoiceMonitorService : BackgroundService
{
private VoiceClient? _client;
protected override async Task ExecuteAsync(CancellationToken ct)
{
// سرور بدون میکروفن — فقط برای شنود/نظارت
var audio = new HeadlessAudioEngine(); // پیادهسازی بدون صدا
var config = new VoiceConfig
{
Server = "callem.cloudfort.ir",
ApiKey = "vc_live_YOUR_API_KEY",
PeerId = "server-monitor-01",
};
_client = new VoiceClient(config, audio);
_client.PeerSpeaking += peerId =>
Console.WriteLine($"[Monitor] {peerId} is speaking");
await _client.ConnectAsync(ct);
await _client.JoinRoomAsync("general");
}
}
// پیادهسازی HeadlessAudioEngine (بدون سختافزار صدا)
public class HeadlessAudioEngine : IAudioEngine
{
public int SampleRate => 48_000;
public event Action<short[]>? AudioCaptured;
public event Action<string>? ErrorOccurred;
public Task<bool> RequestMicrophonePermission() => Task.FromResult(true);
public void StartCapture() { }
public void StopCapture() { }
public void StartPlayback() { }
public void StopPlayback() { }
public void EnqueuePeerAudio(string peerId, short[] samples)
{
// صدا را دریافت کنید ولی پخش نکنید (فقط برای نظارت)
}
public void RemovePeer(string peerId) { }
public void ClearPeers() { }
public void SetPlaybackVolume(float volume) { }
public void KeepScreenAwake() { }
public void ReleaseScreenAwake() { }
public void Dispose() { }
}
۴. Blazor WebAssembly
وضعیت: قابل استفاده — با Web Audio API از طریق JS Interop
Blazor WASM در مرورگر اجرا میشود، بنابراین باید از voice-chat-sdk.js (SDK جاوااسکریپت) از طریق IJSRuntime استفاده کند.
مثال Blazor WASM
@page "/voice"
@inject IJSRuntime JS
@implements IAsyncDisposable
<h3>Voice Chat</h3>
<p>Status: @_status</p>
<p>Speaking: @_speaking</p>
<button @onclick="JoinRoom">Join</button>
<button @onclick="ToggleMic">Toggle Mic</button>
@code {
private string _status = "Disconnected";
private string _speaking = "";
private IJSObjectReference? _jsModule;
protected async override Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsModule = await JS.InvokeAsync<IJSObjectReference>(
"import", "./js/voice-interop.js");
}
}
public async Task JoinRoom()
{
if (_jsModule is not null)
await _jsModule.InvokeVoidAsync("joinVoiceRoom", "general");
}
public async Task ToggleMic()
{
if (_jsModule is not null)
await _jsModule.InvokeVoidAsync("toggleMic");
}
public async ValueTask DisposeAsync()
{
if (_jsModule is not null)
await _jsModule.DisposeAsync();
}
}
// wwwroot/js/voice-interop.js
import VoiceChatSDK from './voice-chat-sdk.js';
const client = new VoiceChatSDK({
server: 'callem.cloudfort.ir',
apiKey: 'vc_live_YOUR_API_KEY',
peerId: 'blazor-user-' + Date.now(),
});
client.on('connected', () => {
DotNet.invokeMethodAsync('MyBlazorApp', 'OnVoiceStatus', 'Connected');
});
client.on('peer-speaking', (id) => {
DotNet.invokeMethodAsync('MyBlazorApp', 'OnPeerSpeaking', id);
});
client.connect();
export function joinVoiceRoom(roomId) {
client.joinRoom(roomId).then(() => client.enableMic());
}
export function toggleMic() {
client.toggleMic();
}
۵. Avalonia UI
وضعیت: قابل استفاده — مشابه WPF
Avalonia یک فریمورک cross-platform UI است. برای استفاده از Callum.Voice:
- Windows: از
AudioEngineویندوز (WASAPI) استفاده کنید - Linux/macOS: باید
IAudioEngineسفارشی با PulseAudio/CoreAudio پیادهسازی کنید
مثال Avalonia
// MainWindowViewModel.cs
using Callum.Voice;
public class MainWindowViewModel : ViewModelBase
{
private VoiceClient? _client;
private string _status = "Disconnected";
public string Status
{
get => _status;
set => this.RaiseAndSetIfChanged(ref _status, value);
}
public async Task ConnectAsync()
{
var audio = new AudioEngine(sampleRate: 48_000); // Windows WASAPI
var config = new VoiceConfig
{
Server = "callem.cloudfort.ir",
ApiKey = "vc_live_YOUR_API_KEY",
PeerId = $"avalonia-{Guid.NewGuid():N}",
};
_client = new VoiceClient(config, audio);
_client.Connected += () => Status = "Connected";
_client.PeerSpeaking += id => Status = $"{id} speaking";
await _client.ConnectAsync();
await _client.JoinRoomAsync("general");
await _client.EnableMicAsync();
}
}
۶. Console App (.NET)
وضعیت: قابل استفاده — Headless (بدون پخش/ضبط)
برای ابزارهای خط فرمان، باتها، یا سرویسهای پسزمینه:
مثال Console — ربات نظارت
using Callum.Voice;
var audio = new HeadlessAudioEngine();
var config = new VoiceConfig
{
Server = "callem.cloudfort.ir",
ApiKey = "vc_live_YOUR_API_KEY",
PeerId = $"bot-{Guid.NewGuid():N}",
};
using var client = new VoiceClient(config, audio);
client.Connected += () => Console.WriteLine("[+] Connected");
client.PeerJoined += id => Console.WriteLine($"[+] Peer joined: {id}");
client.PeerSpeaking += id => Console.WriteLine($"[!] {id} is speaking");
client.PeerLeft += id => Console.WriteLine($"[-] Peer left: {id}");
client.Disconnected += () => Console.WriteLine("[-] Disconnected");
await client.ConnectAsync();
await client.JoinRoomAsync("general");
Console.WriteLine("Monitoring room... Press Enter to exit.");
Console.ReadLine();
API Reference
VoiceClient
| Method | Return Type | توضیحات |
|---|---|---|
ConnectAsync(ct) |
Task |
اتصال به سرور |
Disconnect() |
void |
قطع اتصال |
JoinRoomAsync(roomId) |
Task |
ورود به اتاق |
LeaveRoomAsync() |
Task |
خروج از اتاق |
EnableMicAsync() |
Task |
فعالسازی میکروفن (با درخواست مجوز) |
DisableMic() |
void |
غیرفعالسازی میکروفن |
ToggleMicAsync() |
Task<bool> |
تغییر وضعیت میکروفن |
EnableSpeaker() |
void |
فعالسازی بلندگو (Android) |
DisableSpeaker() |
void |
غیرفعالسازی بلندگو |
ToggleSpeaker() |
bool |
تغییر وضعیت بلندگو |
IsPeerSpeaking(peerId) |
bool |
آیا کاربر در حال صحبت است؟ |
IsPeerSpeaking(peerId, out level) |
bool |
سطح RMS را هم برمیگرداند |
Dispose() |
void |
آزادسازی منابع |
Properties
| Property | Type | توضیحات |
|---|---|---|
State |
VoiceClientState |
وضعیت فعلی اتصال |
IsConnected |
bool |
آیا متصل است؟ |
IsInRoom |
bool |
آیا در اتاق است؟ |
IsMicEnabled |
bool |
آیا میکروفن فعال است؟ |
CurrentRoom |
string? |
شناسه اتاق فعلی |
Peers |
IReadOnlyCollection<string> |
لیست کاربران حاضر |
VoiceConfig
| Property | Type | Default | توضیحات |
|---|---|---|---|
Server |
string |
— | آدرس سرور (مثال: callem.cloudfort.ir) |
ApiKey |
string |
— | کلید API (vc_live_...) |
PeerId |
string |
— | شناسه یکتای کاربر |
UseTls |
bool? |
null |
استفاده از WSS (null = خودکار) |
SampleRate |
int |
48000 |
نرخ نمونهبرداری (Hz) |
AutoReconnect |
bool |
true |
اتصال مجدد خودکار |
MaxReconnectAttempts |
int |
5 |
حداکثر تلاش reconnect |
ReconnectDelay |
TimeSpan |
2s |
فاصله بین تلاشها |
EchoCancellation |
bool |
true |
حذف اکو |
NoiseSuppression |
bool |
true |
حذف نویز |
AutoGainControl |
bool |
true |
کنترل خودکار gain |
Events
| Event | Type | توضیحات |
|---|---|---|
Connected |
Action |
اتصال به سرور برقرار شد |
Disconnected |
Action |
اتصال قطع شد |
Reconnecting |
Action<int> |
تلاش برای اتصال مجدد (با شماره تلاش) |
AuthFailed |
Action<string> |
احراز هویت ناموفق |
RoomJoined |
Action<string> |
ورود به اتاق |
RoomLeft |
Action<string> |
خروج از اتاق |
PeerJoined |
Action<string> |
ورود کاربر جدید |
PeerLeft |
Action<string> |
خروج کاربر |
PeerSpeaking |
Action<string> |
کاربر شروع به صحبت کرد |
PeerStopped |
Action<string> |
کاربر صحبت را متوقف کرد |
MicEnabled |
Action |
میکروفن فعال شد |
MicDisabled |
Action |
میکروفن غیرفعال شد |
Error |
Action<Exception> |
خطا رخ داد |
StateChanged |
Action<VoiceClientState> |
تغییر وضعیت اتصال |
نکات مهم
خلاصه پشتیبانی پلتفرمها
| پلتفرم | ضبط/پخش | VoiceClient | نیاز به IAudioEngine سفارشی |
|---|---|---|---|
| MAUI Android | ✅ آماده | ✅ آماده | ❌ ندارد |
| MAUI iOS | ✅ آماده | ✅ آماده | ❌ ندارد |
| MAUI Windows | ✅ آماده | ✅ آماده | ❌ ندارد |
| WPF | ❌ نیاز به اصلاح csproj | ✅ آماده | ⚠️ یا WASAPI کپی یا NAudio |
| Avalonia | ⚠️ فقط Windows | ✅ آماده | ⚠️ برای Linux/macOS |
| ASP.NET Core | ❌ سمت سرور | ✅ آماده | ✅ HeadlessAudioEngine |
| Blazor WASM | ✅ مرورگر | ✅ از طریق JS Interop | ✅ SDK جاوااسکریپت |
| Console | ❌ بدون سختافزار | ✅ آماده | ✅ HeadlessAudioEngine |
مجوزهای لازم
| پلتفرم | مجوز |
|---|---|
| Android | RECORD_AUDIO (runtime) |
| iOS | NSMicrophoneUsageDescription (Info.plist) |
| Windows | میکروفن (از تنظیمات OS) |
فرمت صوتی
- PCM 16-bit signed integer
- Mono (تک کاناله)
- 48,000 Hz sample rate (پیشفرض)
- Bitrate: ~768 kbps (48000 × 16 × 1)
سرور
سرور Go (phonil-opus) در آدرس callem.cloudfort.ir در دسترس است.
برای راهاندازی سرور اختصاصی، README-SDK.md سرور را مطالعه کنید.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0-android36.0 is compatible. net10.0-ios26.0 is compatible. net10.0-maccatalyst26.0 is compatible. net10.0-windows10.0.19041 is compatible. |
-
net10.0-android36.0
- Xamarin.AndroidX.Core (>= 1.15.0.2)
-
net10.0-ios26.0
- No dependencies.
-
net10.0-maccatalyst26.0
- No dependencies.
-
net10.0-windows10.0.19041
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.