8.12.11. AUDIO-11 — Recording from the Microphone

This tutorial covers capturing audio from a microphone with the audio/audio_record module: enumerating input devices, recording to a WAV file, and driving the low-level capture loop yourself for live processing.

Capture is independent of playback — you do not need with_audio_system. A dedicated capture device runs on the audio thread and fills a lock-free ring buffer; your script drains it on the main thread. Samples are interleaved 32-bit float at the sample rate and channel count you request.

8.12.11.1. Enumerating Capture Devices

sound_record_list_devices returns the available microphones. Each AudioDeviceInfo has a display name, an is_default flag, and an index you pass to the recording calls as device_index (-1 means the system default):

require audio/audio_record

for (d in sound_record_list_devices()) {
    let tag = d.is_default ? " (default)" : ""
    print("[{d.index}] {d.name}{tag}\n")
}

Listing devices does not open the microphone, so it works before any capture permission prompt.

8.12.11.2. Recording to a WAV File

record_to_wav is the one-call path: it opens the default capture device, records for the requested duration, writes a 16-bit PCM WAV, and returns false if the device could not be opened (no microphone, or the OS denied access). Pass a device_index to target a specific input:

// fname, seconds, rate = 44100, channels = 1, device_index = -1
if (record_to_wav("recording.wav", 2.0, 44100, 1)) {
    print("wrote recording.wav\n")
}

8.12.11.3. The Manual Capture Loop

For live processing you drive the loop yourself. sound_record_start opens the device; sound_record_read drains as many buffered frames as fit in your scratch array, returning the frame count; sound_record_stop closes the device. The rb_frames argument sizes the ring buffer (a sample rate’s worth is about one second of headroom):

require audio/audio_record
require audio
require math

if (sound_record_start(44100, 1, 44100, -1)) {
    var scratch : array<float>
    scratch |> resize(44100)
    var frames = 0
    var peak = 0.0
    while (frames < 44100) {          // ~1 second
        let n = sound_record_read(scratch)
        if (n > 0) {
            for (i in range(n)) {
                peak = max(peak, abs(scratch[i]))
            }
            frames += n
        } else {
            sleep(5u)
        }
    }
    sound_record_stop()
    print("peak {peak}, dropped {sound_record_overflow_frames()} frames\n")
}

sound_record_overflow_frames reports frames the audio thread had to drop because the ring filled up — a non-zero value means your drain loop fell behind. sound_record_available reports how many frames are currently buffered, and sound_is_recording reports whether a capture device is open.

The samples are at the native rate you requested; resample to another rate (for example 16 kHz mono for speech models) with the ma_resampler_* bindings.

8.12.11.4. Running the Tutorial

Run from the project root:

daslang.exe tutorials/dasAudio/11_recording.das

The tutorial lists the capture devices, records two seconds to recording.wav, reads the file back to confirm the round-trip, then runs the manual loop for one second and reports the peak amplitude.

Note

On macOS the microphone is gated by the system privacy settings — the process (or the terminal that launched it) must be granted microphone access, or capture returns silence. Enumeration is unaffected.

See also

Full source: tutorials/dasAudio/11_recording.das

Previous tutorial: AUDIO-10 — Global Controls