v0.9: VoicePipeline refactor — queue-based pipeline, lock/release model, thread-based TCP
This commit is contained in:
@@ -120,6 +120,71 @@ public sealed class LibPiperTtsEngine : ITtsEngine
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<AudioChunk> SynthesizeSync(string text)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
if (!_initialized)
|
||||
throw new InvalidOperationException("Engine not initialized.");
|
||||
|
||||
var options = new PiperSynthesizeOptions
|
||||
{
|
||||
SpeakerId = 0,
|
||||
LengthScale = _lengthScale,
|
||||
NoiseScale = _noiseScale,
|
||||
NoiseWScale = _noiseWScale,
|
||||
};
|
||||
|
||||
byte[] textBytes = System.Text.Encoding.UTF8.GetBytes(EnsureTerminator(text) + "\0");
|
||||
GCHandle textPin = GCHandle.Alloc(textBytes, GCHandleType.Pinned);
|
||||
try
|
||||
{
|
||||
int startResult = PiperNative.piper_synthesize_start(
|
||||
_synth, textPin.AddrOfPinnedObject(), in options);
|
||||
if (startResult != PiperNative.PiperOk)
|
||||
throw new InvalidOperationException($"piper_synthesize_start failed: {startResult}");
|
||||
|
||||
while (true)
|
||||
{
|
||||
PiperAudioChunk chunk = default;
|
||||
int result = PiperNative.piper_synthesize_next(_synth, out chunk);
|
||||
|
||||
if (chunk.NumSamples > 0 && chunk.Samples != IntPtr.Zero)
|
||||
{
|
||||
int numSamples = (int)chunk.NumSamples;
|
||||
float[] samples = new float[numSamples];
|
||||
Marshal.Copy(chunk.Samples, samples, 0, numSamples);
|
||||
|
||||
if (chunk.SampleRate > 0)
|
||||
_sampleRate = chunk.SampleRate;
|
||||
|
||||
yield return new AudioChunk(samples, _sampleRate);
|
||||
}
|
||||
|
||||
if (result == PiperNative.PiperDone || chunk.IsLast)
|
||||
break;
|
||||
|
||||
if (result < 0)
|
||||
throw new InvalidOperationException($"piper_synthesize_next failed: {result}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
textPin.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public void SynthesizeDrain()
|
||||
{
|
||||
if (!_initialized || _synth == IntPtr.Zero)
|
||||
return;
|
||||
|
||||
PiperAudioChunk chunk;
|
||||
while (PiperNative.piper_synthesize_next(_synth, out chunk) != PiperNative.PiperDone)
|
||||
{
|
||||
if (chunk.IsLast) break;
|
||||
}
|
||||
}
|
||||
|
||||
private static string EnsureTerminator(string text)
|
||||
{
|
||||
string trimmed = text.TrimEnd();
|
||||
|
||||
Reference in New Issue
Block a user