Recursive AI

Record from the microphone

All oneaudio recipes

Record audio from the default microphone in C# and save it as a 16-bit WAV file, with the RecursiveAI.OneAudio.Devices package and oneaudio’s WAV encoder.

Terminal
dotnet add package RecursiveAI.OneAudio --version 1.2.1
dotnet add package RecursiveAI.OneAudio.Devices --version 1.2.1
C#
using OneAudio;
using OneAudio.Devices;
 
// Record three seconds from the default input, 48 kHz mono, into a WAV file.
var options = new DeviceStreamOptions { SampleRate = 48000, Channels = 1 };
var format = new AudioFormat(48000, 1);
const long total = 3 * 48000L;
 
using var input = Audio.CreateInput(options);   // opens and starts the device
var settings = new WavEncoderSettings { SampleType = NativeSampleType.PcmSigned16 };
using (var wav = new WavEncoder(File.Create("take.wav"), format, settings,
                                leaveOpen: false))
{
    float[] block = new float[4800];            // 100 ms
    long written = 0;
    var timeout = DateTime.UtcNow.AddSeconds(8);
    while (written < total && DateTime.UtcNow < timeout)
    {
        int read = input.Read(block);           // frames; 0 when nothing waits
        if (read == 0) { Thread.Sleep(20); continue; }
        int keep = (int)Math.Min(read, total - written);
        wav.Write(block.AsSpan(0, keep));
        written += keep;
    }
}   // Dispose finishes the file
 
Console.WriteLine($"{input.BackendName}: {input.Device?.Id ?? "no device"}, " +
                  $"{input.State}, overruns {input.Counters.Overruns}");
using IAudioDecoder check = Audio.Open("take.wav");
AudioFormat f = check.Format;
Console.WriteLine($"take.wav: {f.SampleRate} Hz, {f.Channels} ch, " +
                  $"{check.LengthFrames / (double)f.SampleRate:F2} s, " +
                  $"{new FileInfo("take.wav").Length:N0} bytes");
Text
alsa: default, Running, overruns 0
take.wav: 48000 Hz, 1 ch, 3.00 s, 288,080 bytes

This ran on Linux, through ALSA’s default capture device to PipeWire, recording from a USB webcam microphone.

  • Audio.CreateInput(options) opens the default input and starts it. Audio arrives as 32-bit floats at the rate and channel count you ask for, converted from whatever the hardware runs at.
  • Read copies whatever has arrived, in whole frames, and returns 0 when nothing is waiting. It never blocks, so the loop sleeps briefly between reads.
  • Captured audio waits in a buffer of CaptureBufferMilliseconds (1,000 by default). If you don’t read for longer than that, new audio is dropped and counted in Overruns.
  • WavEncoderSettings.SampleType picks the stored type. The 288,080 bytes are 3 seconds of 16-bit mono at 48 kHz plus the header.
  • To process audio as it arrives instead, pass a CaptureCallback to Audio.CreateInput. It runs on the device thread, so it must not allocate, lock or block.

On macOS and iOS, the program needs microphone permission. On iOS, set DeviceStreamOptions.AppleSession to PlayAndRecord and give the app a microphone usage description. On Windows, the microphone privacy setting has to allow desktop apps. A refused open says so in LastError.

A longer walk-through with a live level meter: Record audio in C#.