Beep on detection: fire-and-forget OutputStream, input keeps flowing

This commit is contained in:
2026-06-12 12:03:14 +00:00
parent 2e830eaed4
commit 14e862f50a
+69 -1
View File
@@ -11,6 +11,8 @@ const int Channels = 1;
const uint BlockFrames = WakewordModel.FrameSamples; // 1280 const uint BlockFrames = WakewordModel.FrameSamples; // 1280
const float Threshold = 0.5f; const float Threshold = 0.5f;
const double CooldownSeconds = 1.0; const double CooldownSeconds = 1.0;
const double BeepHz = 880.0;
const double BeepSeconds = 0.2;
// .NET-side mirror for any readers that go through Environment.GetEnvironmentVariable, // .NET-side mirror for any readers that go through Environment.GetEnvironmentVariable,
// AND libc setenv so PortAudio / onnxruntime (which use getenv()) see the values. // AND libc setenv so PortAudio / onnxruntime (which use getenv()) see the values.
@@ -79,6 +81,7 @@ try
using var stream = new Stream( using var stream = new Stream(
inParams, null, SampleRate, BlockFrames, StreamFlags.NoFlag, callback, IntPtr.Zero); inParams, null, SampleRate, BlockFrames, StreamFlags.NoFlag, callback, IntPtr.Zero);
stream.Start(); stream.Start();
short[] beepBuffer = MakeBeep(BeepHz, BeepSeconds, SampleRate);
Console.WriteLine("Listening. Say 'alexa'. Ctrl-C to exit."); Console.WriteLine("Listening. Say 'alexa'. Ctrl-C to exit.");
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
@@ -95,7 +98,7 @@ try
{ {
Console.WriteLine($"DETECTED alexa score={score:0.000} t={now.TotalSeconds:0.0}s"); Console.WriteLine($"DETECTED alexa score={score:0.000} t={now.TotalSeconds:0.0}s");
lastTrigger = now; lastTrigger = now;
// Beep is added in Task 5. FireAndForgetBeep(device, beepBuffer);
} }
} }
} }
@@ -130,6 +133,71 @@ static int FindUsbDevice()
return -1; // unreachable return -1; // unreachable
} }
static short[] MakeBeep(double freqHz, double durationS, int sampleRate)
{
int n = (int)(sampleRate * durationS);
var buf = new short[n];
for (int i = 0; i < n; i++)
{
double t = i / (double)sampleRate;
double v = 0.3 * Math.Sin(2.0 * Math.PI * freqHz * t);
buf[i] = (short)(v * short.MaxValue);
}
return buf;
}
static void FireAndForgetBeep(int device, short[] beepBuffer)
{
int offset = 0;
var done = new ManualResetEventSlim(false);
var outParams = new StreamParameters
{
device = device,
channelCount = 1,
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) =>
{
int remaining = beepBuffer.Length - offset;
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 = &beepBuffer[offset])
Buffer.MemoryCopy(src, dst, take * sizeof(short), take * sizeof(short));
offset += take;
}
for (int i = take; i < frameCount; i++) dst[i] = 0;
}
if (offset >= beepBuffer.Length)
{
done.Set();
return StreamCallbackResult.Complete;
}
return StreamCallbackResult.Continue;
};
var stream = new Stream(
null, outParams, WakewordModel.SampleRate, 1024, StreamFlags.NoFlag, playCb, IntPtr.Zero);
Task.Run(() =>
{
done.Wait();
Thread.Sleep(200);
stream.Stop();
stream.Dispose();
done.Dispose();
});
stream.Start();
}
static class Libc static class Libc
{ {
[DllImport("libc", EntryPoint = "setenv")] [DllImport("libc", EntryPoint = "setenv")]