diff --git a/tests/01-record-play-cs/Program.cs b/tests/01-record-play-cs/Program.cs index ef50c72..cedc1e5 100644 --- a/tests/01-record-play-cs/Program.cs +++ b/tests/01-record-play-cs/Program.cs @@ -85,6 +85,62 @@ try WavWriter.Write(OutWav, buf, SampleRate); Console.WriteLine($"Wrote {OutWav} ({buf.Length} frames @ {SampleRate} Hz)"); + + Console.WriteLine($"Playing back through device {device}"); + + int playOffset = 0; + object playGate = new(); + var doneEvent = new ManualResetEventSlim(false); + + var outParams = new StreamParameters + { + device = device, + channelCount = Channels, + sampleFormat = SampleFormat.Int16, + suggestedLatency = PortAudio.GetDeviceInfo(device).defaultLowOutputLatency, + hostApiSpecificStreamInfo = IntPtr.Zero, + }; + + Stream.Callback playCb = (IntPtr _, IntPtr output, uint frameCount, + ref StreamCallbackTimeInfo _2, StreamCallbackFlags _3, IntPtr _4) => + { + lock (playGate) + { + int remaining = buf.Length - playOffset; + int take = (int)Math.Min(frameCount, (uint)remaining); + int silence = (int)frameCount - take; + + unsafe + { + short* dst = (short*)output.ToPointer(); + if (take > 0) + { + fixed (short* src = &buf[playOffset]) + Buffer.MemoryCopy(src, dst, take * sizeof(short), take * sizeof(short)); + playOffset += take; + } + if (silence > 0) + { + for (int i = take; i < frameCount; i++) dst[i] = 0; + } + } + + if (playOffset >= buf.Length) + { + doneEvent.Set(); + return StreamCallbackResult.Complete; + } + } + return StreamCallbackResult.Continue; + }; + + using var playStream = new Stream( + null, outParams, SampleRate, BlockFrames, StreamFlags.NoFlag, playCb, IntPtr.Zero); + playStream.Start(); + doneEvent.Wait(); + // Give PortAudio a moment to drain the device buffer before Stop(). + Thread.Sleep(200); + playStream.Stop(); } finally {