-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCircularAudioBuffer.cs
124 lines (105 loc) · 4.92 KB
/
CircularAudioBuffer.cs
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
114
115
116
117
118
119
120
121
122
123
124
using System;
namespace OpenVoiceSharp
{
public enum RecommendedChunkAmount
{
Unity = 18,
}
/// <summary>
/// Creates a circular audio buffer that can be read and written.
/// Takes in chunks/frames of audio (or any struct).
/// It is recommended to wait for the buffer to be full to read the entirety of it.
/// Useful for Unity or other engines that do not support streamed pcm reading by default.
/// </summary>
/// <typeparam name="T">Byte/short/float depending on your needs.</typeparam>
public struct CircularAudioBuffer<T> where T: struct
{
/// <summary>
/// The raw length of the buffer, in samples.
/// </summary>
public int BufferLength { get; private set; }
/// <summary>
/// The raw length of a chunk, in samples.
/// </summary>
public int ChunkSize { get; private set; }
public readonly int BufferAvailable => ChunksAvailable * ChunkSize;
public int ChunksAvailable = 0;
private readonly T[] Buffer;
public readonly bool BufferFull => BufferAvailable == BufferLength;
public readonly bool CanReadChunk => ChunksAvailable > 0;
// no need to do a for loop to rewrite the buffer.
// just dont give it.
/// <summary>
/// Reads the first chunk available at the front of the buffer.
/// </summary>
/// <returns>Chunk</returns>
public T[] ReadChunk()
{
if (!CanReadChunk)
throw new Exception("No chunks are available.");
// slice the chunk
T[] chunk = Buffer[ChunksAvailable..ChunkSize];
// grab the rest and put it at the front
chunk.CopyTo(Buffer[ChunkSize..BufferLength], 0);
ChunksAvailable--;
return chunk;
}
/// <summary>
/// Reads the first chunk available at the front of the buffer and copies it to another specified buffer.
/// </summary>
/// <param name="target">The target buffer to which it'll be copied to</param>
/// <param name="offset">The begin offset to which it'll begin copying to</param>
public void ReadChunkTo(T[] target, int offset = 0) => ReadChunk().CopyTo(target, offset);
/// <summary>
/// Reads all the buffer that is available.
/// It is highly recommended you wait for it to be full to read.
/// </summary>
/// <returns>Buffer</returns>
public T[] ReadAllBuffer()
{
int available = BufferAvailable;
ChunksAvailable = 0;
// read everything thats available
return Buffer[0..available];
}
/// <summary>
/// Reads all the buffer that is available and copies it to another buffer.
/// It is highly recommended you wait for it to be full to read.
/// </summary>
/// <param name="target">The target buffer to which it'll be copied to</param>
/// <param name="offset">The begin offset to which it'll begin copying to</param>
public void ReadAllBufferTo(T[] target, int offset = 0) => ReadAllBuffer().CopyTo(target, offset);
/// <summary>
/// Pushes a chunk into the buffer. Will be ignored if the buffer is full. (no overflow)
/// </summary>
/// <param name="chunk">Chunk/frame</param>
public void PushChunk(T[] chunk)
{
// push chunk back if not full
if (BufferFull) return;
// if size doesnt match
if (chunk.Length != ChunkSize) throw new Exception($"Invalid chunk size. Submitted {chunk.Length} - should be {ChunkSize}");
// copy to
Array.Copy(chunk, 0, Buffer, BufferAvailable, chunk.Length);
ChunksAvailable++;
}
/// <summary>
/// Creates a circular audio buffer.
/// </summary>
/// <param name="chunkSize">Chunk raw size of ONE frame. Use VoiceUtilities if you need to figure out for your sample.</param>
/// <param name="amountOfChunks">Amount of chunks the circular audio buffer can take in. Higher values are usually more stable and lower values usually cause more audio cracking, but will do more latency (20 * amountOfChunks ms).</param>
public CircularAudioBuffer(int chunkSize, int amountOfChunks = 18)
{
ChunkSize = chunkSize;
BufferLength = chunkSize * amountOfChunks;
Buffer = new T[BufferLength];
}
/// <summary>
/// Creates a circular audio buffer.
/// </summary>
/// <param name="chunkSize">Chunk raw size of ONE frame. Use VoiceUtilities if you need to figure out for your sample.</param>
/// <param name="engine">Recommended engine for the amount of chunks the circular audio buffer can take in.</param>
public CircularAudioBuffer(int chunkSize, RecommendedChunkAmount engine)
: this(chunkSize, (int)engine) { }
}
}