forked from SciSharp/BotSharp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWaveStreamChannel.cs
More file actions
113 lines (96 loc) · 3.71 KB
/
WaveStreamChannel.cs
File metadata and controls
113 lines (96 loc) · 3.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
using System.Collections.Concurrent;
using BotSharp.Abstraction.Realtime.Enums;
using NAudio.Wave;
namespace BotSharp.Core.Realtime.Services;
public class WaveStreamChannel : IStreamChannel
{
private readonly IServiceProvider _services;
private WaveInEvent _waveIn;
private WaveOutEvent _waveOut;
private BufferedWaveProvider _bufferedWaveProvider;
private readonly ConcurrentQueue<byte[]> _audioBufferQueue = [];
private readonly ILogger _logger;
public WaveStreamChannel(IServiceProvider services, ILogger<WaveStreamChannel> logger)
{
_services = services;
_logger = logger;
}
public async Task ConnectAsync(string conversationId)
{
// Initialize the WaveInEvent
_waveIn = new WaveInEvent
{
DeviceNumber = 0, // Default recording device
WaveFormat = new WaveFormat(16000, 16, 1), // 24000 Hz, 16-bit PCM, Mono
BufferMilliseconds = 100
};
// Set up the DataAvailable event handler
_waveIn.DataAvailable += WaveIn_DataAvailable;
// Start recording
_waveIn.StartRecording();
// Initialize audio output for streaming
var waveFormat = new WaveFormat(24000, 16, 1); // 24000 Hz, 16-bit PCM, Mono
_bufferedWaveProvider = new BufferedWaveProvider(waveFormat);
_bufferedWaveProvider.BufferDuration = TimeSpan.FromMinutes(10);
//_bufferedWaveProvider.BufferLength = 1024;
_bufferedWaveProvider.DiscardOnBufferOverflow = true;
_waveOut = new WaveOutEvent()
{
DeviceNumber = 0
};
_waveOut.Init(_bufferedWaveProvider);
_waveOut.Play();
}
public async Task<StreamReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellation)
{
// Poll the queue until data is available or cancellation is requested
while (!cancellation.IsCancellationRequested)
{
// Try to dequeue audio data
if (_audioBufferQueue.TryDequeue(out byte[]? audioData))
{
// Copy data to the provided buffer
int bytesToCopy = Math.Min(audioData.Length, buffer.Count);
Array.Copy(audioData, 0, buffer.Array, buffer.Offset, bytesToCopy);
// Return the result
return new StreamReceiveResult
{
Status = StreamChannelStatus.Open,
Count = bytesToCopy
};
}
// No data available yet, wait a short time before checking again
await Task.Delay(10, cancellation);
}
// Cancellation was requested
return new StreamReceiveResult();
}
public Task SendAsync(byte[] data, CancellationToken cancellation)
{
_logger.LogDebug($"Sending audio data of length {data.Length} to the stream channel.");
// Add the incoming data to the buffer for continuous playback
_bufferedWaveProvider.AddSamples(data, 0, data.Length);
return Task.CompletedTask;
}
public void ClearBuffer()
{
_bufferedWaveProvider?.ClearBuffer();
_audioBufferQueue?.Clear();
}
private void WaveIn_DataAvailable(object? sender, WaveInEventArgs e)
{
// Add the buffer to the queue
_audioBufferQueue.Enqueue(e.Buffer);
}
public async Task CloseAsync(StreamChannelStatus status, string description, CancellationToken cancellation)
{
// Stop recording and clean up
_waveIn?.StopRecording();
_waveIn?.Dispose();
_waveIn = null;
// Stop playback and clean up
_waveOut?.Stop();
_waveOut?.Dispose();
_waveOut = null;
}
}