Reference for MATLAB audiostreamer (Audio Toolbox R2025a+). Without this skill, agents consistently default to legacy audioDeviceWriter/audioDeviceReader or base MATLAB sound(), producing less capable code. Use when writing code for audio playback, recording, full-duplex device I/O, real-time audio measurements, or audio I/O processing with callbacks. Also use when debugging audiostreamer errors, dropouts, or latency issues, or migrating from audioDeviceReader, audioDeviceWriter, audioPlayerR...
Scanned 9/5/2026
Install to Claude Code
npx -y skills add matlab/matlab-agentic-toolkit --skill matlab-play-record-audio --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Matlab Play Record Audio?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/matlab-matlab-play-record-audio)More formats (shields.io, HTML) on the badges page.
---
name: matlab-play-record-audio
description: Reference for MATLAB audiostreamer (Audio Toolbox R2025a+). Without this skill, agents consistently default to legacy audioDeviceWriter/audioDeviceReader or base MATLAB sound(), producing less capable code. Use when writing code for audio playback, recording, full-duplex device I/O, real-time audio measurements, or audio I/O processing with callbacks. Also use when debugging audiostreamer errors, dropouts, or latency issues, or migrating from audioDeviceReader, audioDeviceWriter, audioPlayerRecorder, or audioplayer/audiorecorder.
license: https://www.mathworks.com/content/dam/mathworks/license/pmrl/license.md
metadata:
author: MathWorks
version: "1.0"
---
# audiostreamer — MATLAB Audio Device I/O (R2025a+)
`audiostreamer` is the unified replacement for `audioDeviceWriter`, `audioDeviceReader`, and `audioPlayerRecorder`. It provides player-only, recorder-only, or full-duplex modes with callbacks, pre-buffering, transport control, and measurement helpers.
**Version requirements:** `audiostreamer` requires Audio Toolbox R2025a or later. The `start` and `write` methods were added in R2026a.
## When to Use
- Playing audio through a sound card or USB audio device
- Recording audio from a microphone or audio interface
- Full-duplex playback + recording (e.g., acoustic measurements, loopback tests)
- Listing or selecting audio devices and drivers
- Any workflow that involves audio hardware I/O in MATLAB
## When NOT to Use
- **Playing a single isolated sound** — `sound` or `soundsc` is fine for one-shot playback of a short clip with no sequencing. For sequential playback (e.g., before/after comparison), use `audiostreamer` — its `play()` calls queue automatically, whereas overlapping `sound` calls play simultaneously.
- **Code generation (codegen)** — `audiostreamer` does not yet support codegen; use legacy APIs if targeting codegen
- **Simulink models** — Simulink still uses the existing audio I/O blocks, not `audiostreamer`
- **Audio Toolbox not available** — fall back to `sound`/`soundsc`, `audioplayer`/`audiorecorder`, or `audioDeviceWriter` (DSP System Toolbox) if the user lacks Audio Toolbox
- **File I/O only** — reading/writing audio files without device playback or recording uses `audioread`/`audiowrite`, not this skill
- **DAQ hardware** — National Instruments or similar data acquisition devices use DAQ Toolbox and the `daq` object
- **MIDI-only devices** — MIDI control uses `mididevice`/`midicontrols`, not `audiostreamer`
## Construction
Use Name-Value pairs for `Mode` and `SampleRate` (positional shorthand exists but does not support tab-completion):
**The default Mode is `"player"`. You MUST set Mode explicitly if recording** — either `Mode="recorder"` or `Mode="full-duplex"`. Mode is not inferred from other properties like `Recorder` or `RecorderChannels`.
```matlab
as = audiostreamer % default: player mode, 44100 Hz
as = audiostreamer(Mode="player", SampleRate=fs) % player at fs Hz
as = audiostreamer(Mode="recorder", SampleRate=fs) % recorder at fs Hz
as = audiostreamer(Mode="full-duplex", SampleRate=fs) % simultaneous play + record
as = audiostreamer(Mode="full-duplex", SampleRate=48000, Driver="ASIO", ...
Player="Focusrite USB ASIO", Recorder="Focusrite USB ASIO", ...
PlayerChannels=[1 2], RecorderChannels=[1 2])
```
## Properties
### Device Configuration (set BEFORE streaming starts)
| Property | Type | Default | Notes |
|----------|------|---------|-------|
| `Mode` | `"player"` / `"recorder"` / `"full-duplex"` | `"player"` | Set at construction or via property |
| `Driver` | `"DirectSound"` / `"ASIO"` / `"WASAPI"` (Win); `"CoreAudio"` (Mac); `"ALSA"` (Linux) | OS default | Only set on Windows (Mac/Linux have one driver each). Setting at construction selects the default device for that driver. |
| `Player` | string | System default for driver | Output device name. Omit to use the default device for the selected driver. |
| `Recorder` | string | System default for driver | Input device name. Omit to use the default device for the selected driver. |
| `SampleRate` | positive scalar | 44100 | Hz |
| `DeviceBufferSize` | positive int or `"auto"` | `"auto"` | Fixed for ASIO (use `asiosettings`). |
| `DeviceBitFormat` | `"single"` / `"int24"` / `"int16"` | `"int24"` | int16 on ASIO silently uses int24 |
| `PlayerChannels` | row vector or `"auto"` | `"auto"` | 1-based mapping. "auto" upmixes mono→stereo; for N≥2 channels, opens N channels on the device |
| `RecorderChannels` | row vector | `1` | 1-based mapping. **Records 1 channel by default** — set e.g. `1:2` for stereo |
| `ExclusiveMode` | on/off | `"on"` | WASAPI only — disables OS mixing/resampling |
| `ConstantLatency` | `"off"` / `"dropPlayer"` / `"dropRecorder"` | `"off"` | Full-duplex dropout handling |
**IMPORTANT:** `Mode`, `SampleRate`, `Driver`, `DeviceBufferSize`, `DeviceBitFormat`, `ExclusiveMode`, `ConstantLatency`, `PlayerChannels`, and `RecorderChannels` lock once streaming starts. Call `release(as)` before changing any of these properties to avoid an automatic release with a warning.
### Callback Properties
| Property | Signature | Trigger |
|----------|-----------|---------|
| `PlayerFcn` | `@(obj, event)` | Player buffer drops below `PlayerMinSamples` |
| `PlayerMinSamples` | positive int (default 16384) | Threshold for `PlayerFcn` trigger |
| `RecorderFcn` | `@(obj, event)` | Recorder buffer exceeds `RecorderMinSamples` |
| `RecorderMinSamples` | positive int (default 1024) | Threshold for `RecorderFcn` trigger |
| `PlayerCompletedFcn` | `@(obj, event)` | Output queue empties |
| `RecorderCompletedFcn` | `@(obj, event)` | Fixed-length recording finishes |
| `PlayerUnderrunFcn` | `@(obj, event)` | Player underrun occurs |
**ALL callbacks MUST accept exactly 2 arguments.** First arg = the audiostreamer object. Second arg = event struct with `.Type` field. Use `@(obj, ~)` if you don't need the event.
Event struct fields by type:
- `PlayerFcn`: `event.Type = "Player"`, `event.NumPlayerSamples`
- `RecorderFcn`: `event.Type = "Recorder"`, `event.NumRecorderSamples`
- `PlayerCompletedFcn`: `event.Type = "PlayerCompleted"`, `event.StreamTime`
- `RecorderCompletedFcn`: `event.Type = "RecorderCompleted"`, `event.StreamTime`
- `PlayerUnderrunFcn`: `event.Type = "PlayerUnderrun"`, `event.SamplesUnderrun`
### Read-Only Status
| Property | Description |
|----------|-------------|
| `NumPlayerSamples` | Samples currently queued in output buffer |
| `NumRecorderSamples` | Samples available to `read()` without blocking |
| `MaxPlayerChannels` | Max output channels on selected device |
| `MaxRecorderChannels` | Max input channels on selected device |
## Methods
### Playback
| Method | Description |
|--------|-------------|
| `play(obj, x)` | Queue `x` and play. **Blocks** until output buffer <= `PlayerMinSamples` (up to `PlayerMinSamples` samples remain unplayed when it returns). Call `waitfor(as)` after the last `play` to ensure complete playback before `release`. |
| `play(obj, x, "non-blocking")` | Queue `x` and return immediately regardless of buffer level. |
| `play(obj)` | Start PlayerFcn callback loop (no data argument). |
| `write(obj, x)` | [R2026a+] Queue `x` to output buffer WITHOUT starting playback. Use with `start()`. |
| `write(obj, x, "non-blocking")` | [R2026a+] Queue `x` and return immediately regardless of buffer level. |
### Recording
| Method | Description |
|--------|-------------|
| `record(obj)` | Start recording indefinitely. Warns if unread samples remain in the buffer. To avoid: `stop(as)` (or `stop(as, "recorder")` in full-duplex), then `read(as)` to flush. Not needed if samples were already consumed by a callback or `read`. |
| `record(obj, numSamples)` | Record exactly `numSamples` then stop. Same unread-samples warning applies. |
| `read(obj)` | Return all available recorded samples immediately (non-blocking). Returns empty if none available. |
| `read(obj, numSamples)` | **Blocks** until `numSamples` available, then returns them. |
### Full-Duplex
| Method | Description |
|--------|-------------|
| `playrec(obj, x)` | Play `x` and record simultaneously. Non-blocking — recording continues in the background; retrieve data with `read`. |
| `playrec(obj, x, numSamples)` | Play `x` and record `numSamples`. Blocking — returns recorded matrix. |
| `playrec(obj)` | Start callback-driven full-duplex (requires RecorderFcn and/or PlayerFcn). |
`playrec` pauses both player and recorder, queues audio, then resumes both simultaneously for **repeatable latency**. This is critical for measurements with `impzest`.
### Transport Control
| Method | Description |
|--------|-------------|
| `start(obj)` | [R2026a+] Start streaming in current mode. |
| `start(obj, Mode="player")` | [R2026a+] Start only player (full-duplex). |
| `start(obj, Mode="recorder", SamplesToRecord=N)` | [R2026a+] Start recorder with fixed count. |
| `stop(obj)` | Stop all streaming. Preserves unread input samples. Resets underrun count (as does `getUnderrunCount`). |
| `stop(obj, "player"/"recorder"/"both")` | Stop specific side. |
| `pause(obj)` / `pause(obj, "player"/"recorder"/"both")` | Pause with state preservation. |
| `resume(obj)` / `resume(obj, "player"/"recorder"/"both")` | Resume from pause. |
| `waitfor(obj)` / `waitfor(obj, "player"/"recorder"/"both")` | Block until complete. |
| `release(obj)` | Stop, flush, close device, tear down. Deletes unread samples. |
### Query / Diagnostics
| Method | Description |
|--------|-------------|
| `isPlaying(obj)` | Returns OnOffSwitchState |
| `isRecording(obj)` | Returns OnOffSwitchState |
| `isPlayerPaused(obj)` | Returns OnOffSwitchState |
| `isRecorderPaused(obj)` | Returns OnOffSwitchState |
| `getUnderrunCount(obj)` | Underrun sample count since last call. Resets counter (as does `stop`). |
| `getStreamTime(obj)` | Elapsed stream time in seconds. |
| `getStreamTime(obj, "reset")` | Reset stream timer. |
| `measureLoopbackLatency(obj)` | Full-duplex only, single channel. Returns delay in samples. |
### Static Device Enumeration
```matlab
audiostreamer.getDrivers() % Available drivers for this OS
audiostreamer.getPlayerNames() % All output devices
audiostreamer.getPlayerNames("ASIO") % Output devices for specific driver
audiostreamer.getRecorderNames() % All input devices
audiostreamer.getRecorderNames("ASIO") % Input devices for specific driver
audiostreamer.getAudioDevices() % Struct array: Name, Driver, MaxRecorderChannels, MaxPlayerChannels, SampleRate (channel counts are int32)
```
**Note:** `getAudioDevices()` returns `int32` for `MaxRecorderChannels` and `MaxPlayerChannels`. Cast to `double()` before using these values in UI components (e.g., `uispinner` Limits) or arithmetic that expects double.
## CRITICAL: There is NO `setup()` Method
The `audiostreamer` does NOT have a public `setup()` method. Device initialization happens implicitly on the first `play()`, `record()`, `playrec()`, or `start()` call. Do NOT call `setup()` — it will error.
If `PlayerFcn` is set, the first streaming call invokes it repeatedly to pre-buffer at least 8192 samples (or `PlayerMinSamples`, whichever is greater) before the device opens.
## Common Patterns
### Pattern 1: Simple Blocking Measurement (Sweep + IR)
```matlab
as = audiostreamer(Mode="full-duplex", SampleRate=48000, ...
PlayerChannels=1, RecorderChannels=1);
x = sweeptone(2, 1, 48000);
y = playrec(as, x, size(x, 1)); % blocking: returns recorded audio
underruns = getUnderrunCount(as);
ir = impzest(x, y);
release(as);
```
### Pattern 2: Non-Blocking Play + Record with waitfor
```matlab
as = audiostreamer(Mode="full-duplex", SampleRate=48000);
x = sweeptone(3, 2, 48000);
playrec(as, x); % non-blocking (no output arg)
waitfor(as); % block until done
y = read(as); % retrieve recorded data
release(as);
```
### Pattern 3: Pre-Buffered Playback (write + start) — R2026a+
Use `write`+`start` when you need to control exactly when playback begins (e.g., synchronized full-duplex start). For simple playback, `play(as, signal)` achieves the same result — it queues and starts automatically.
```matlab
as = audiostreamer(Mode="player", SampleRate=48000);
write(as, signal); % queue without starting
start(as); % begin playback
waitfor(as); % wait for completion
release(as);
```
### Pattern 4: Callback-Driven Streaming Player
```matlab
as = audiostreamer(Mode="player", SampleRate=48000, DeviceBufferSize=1024);
gen = dsp.ColoredNoise("pink", NumChannels=2, SamplesPerFrame=1024);
as.PlayerFcn = @(obj, ~) play(obj, gen());
as.PlayerMinSamples = 4096;
play(as); % or start(as) [R2026a+] — begins callback loop
% ... later ...
stop(as);
release(as);
```
### Pattern 5: Callback-Driven Level Metering (Recorder)
```matlab
as = audiostreamer(Mode="recorder", SampleRate=48000);
as.RecorderFcn = @(obj, ~) updateMeter(read(obj));
as.RecorderMinSamples = 1024;
start(as); % or record(as) before R2026a
% ... meter updates in background ...
stop(as);
release(as);
```
### Pattern 6: Frame-at-a-Time Processing Loop (Full-Duplex)
The most direct replacement for legacy `audioDeviceReader`/`audioDeviceWriter` loops. Call `start(as)` before the loop so that `read` has samples available.
```matlab
as = audiostreamer(Mode="full-duplex", SampleRate=48000, RecorderChannels=1:2);
start(as);
for iter = 1:numIterations
in = read(as, frameLength); % blocks until frameLength samples available
out = process(myPlugin, in);
write(as, out); % blocks until buffer has room
end
nUnderruns = getUnderrunCount(as); % total underruns since last call (resets counter)
release(as);
```
Before R2026a, use `record(as)` + `play(as, out)` instead of `start`/`write`.
For player-only (e.g., file input → device output), use `play(as, out)` with no `start` needed — `play` queues and starts automatically.
### Pattern 7: Repeated Measurements with Callbacks (Full-Duplex)
```matlab
as = audiostreamer(Mode="full-duplex", SampleRate=48000, PlayerChannels=1, RecorderChannels=1);
x = sweeptone(2, 1, 48000);
as.RecorderMinSamples = size(x, 1);
as.RecorderFcn = @(obj, ~) processMeasurement(obj, x);
as.PlayerFcn = @(obj, ~) write(obj, x); % or play(obj, x) before R2026a
as.PlayerMinSamples = size(x, 1);
as.ConstantLatency = "dropPlayer"; % keep in sync for impzest
playrec(as); % starts callback-driven measurement loop
% ... runs continuously ...
stop(as);
release(as);
```
### Pattern 8: App with Timer-Based GUI Updates
```matlab
as = audiostreamer(Mode="player", SampleRate=fs, DeviceBufferSize=1024);
as.PlayerFcn = @(obj, ~) play(obj, getNextFrame());
as.PlayerMinSamples = 20 * 1024;
as.PlayerUnderrunFcn = @(~, ev) fprintf("Dropped %d samples\n", ev.SamplesUnderrun);
figTimer = timer(ExecutionMode="fixedRate", Period=0.05, ...
TimerFcn=@(~,~) updatePlot(as));
play(as); % starts callback loop
start(figTimer); % starts GUI updates
% ...
stop(as);
release(as);
stop(figTimer);
delete(figTimer);
```
In the timer callback, check buffer health before expensive GUI operations:
```matlab
function updatePlot(as)
if as.NumPlayerSamples < 0.5 * as.PlayerMinSamples
return % skip GUI update to prevent dropout
end
% ... update plots ...
if as.NumPlayerSamples > 0.9 * as.PlayerMinSamples
drawnow("limitrate");
end
end
```
### Pattern 9: Full-Duplex with write/start for Control — R2026a+
```matlab
as = audiostreamer(Mode="full-duplex", SampleRate=48000);
write(as, excitation); % queue output
start(as, SamplesToRecord=size(excitation,1)); % start both
waitfor(as, "both");
y = read(as);
release(as);
```
## Teardown Best Practice
`release(as)` is sufficient — it implicitly stops streaming, flushes buffers, and closes the device. No need to call `stop` first. However, `release` discards any unplayed samples — call `waitfor(as)` first if playback must complete.
```matlab
waitfor(as); % ensure all queued audio finishes playing
release(as);
```
In apps, wrap in try-catch and nil the reference:
```matlab
try
release(as);
catch
end
as = [];
```
The destructor calls `release()` automatically, but explicit cleanup is preferred in apps to avoid device lock-up. Calling `release` from within `PlayerCompletedFcn` is safe and does not deadlock.
**Note:** `isvalid(as)` returns `true` even after release — it cannot be used to detect a released audiostreamer. To track released state, nil the object reference and check with `isempty`.
## ConstantLatency Modes (Full-Duplex)
| Value | Behavior | Use For |
|-------|----------|---------|
| `"off"` | After dropout, inserts silence frame (latency increases) | General use |
| `"dropPlayer"` | Late output frames dropped; latency stays constant | Measurements with `impzest` (sweep-based) |
| `"dropRecorder"` | Input frames dropped; latency constant | Adaptive filters (NOT compatible with `impzest`) |
## Error Conditions
| Error ID | Cause |
|----------|-------|
| `audio:device:methodRequiresModes` | Calling method invalid for current Mode (e.g., `record()` in player mode). Set Mode to `"full-duplex"` if you need both playback and recording methods. |
| `audio:device:invalidChannelMap` | Channel indices exceed device max. Check `MaxPlayerChannels` or `MaxRecorderChannels` and adjust mapping. |
| `audio:device:callbackNargin` | Callback doesn't accept exactly 2 arguments. Use `@(obj, ~)` or `@(obj, event)` signature. |
| `audio:device:playrecRecorderFcnConflict` | `playrec` called with output argument while `RecorderFcn` is set — callback consumes samples via `read()`, leaving nothing for the return value. Clear `RecorderFcn` before blocking `playrec`. |
| `audio:device:startModePlayerNotValid` | `start(Mode="player")` in recorder-only mode |
| `audio:device:startModeRecorderNotValid` | `start(Mode="recorder")` in player-only mode |
| `MATLAB:validators:mustBeFinite` | Audio data contains NaN or Inf |
| `MATLAB:validators:mustBeReal` | Audio data is complex |
## audiostreamer vs. Legacy Audio APIs
`audiostreamer` is strongly preferred for all audio device I/O when Audio Toolbox is available. Legacy alternatives may be useful as fallbacks when Audio Toolbox is not installed or in edge cases.
| Legacy API | Limitation | audiostreamer Equivalent |
|------------|-----------|-----------|
| `audiodevinfo` | Does not support ASIO; incomplete device list | `audiostreamer.getAudioDevices()`, `audiostreamer.getPlayerNames()`, etc. |
| `audioplayer` / `audiorecorder` | No ASIO/WASAPI exclusive; limited driver model; no callbacks | `audiostreamer` in player/recorder/full-duplex mode |
| `sound` / `soundsc` | Creates an `audioplayer` under the hood; concurrent calls overlap (do NOT queue) | `audiostreamer` with `play()` for sequential playback |
| `audioDeviceWriter` / `audioDeviceReader` | Separate objects; no callbacks; no pre-buffering; frame-at-a-time loops only | Single `audiostreamer` object with blocking/non-blocking modes |
| `audioPlayerRecorder` | Limited full-duplex; no transport control; no latency measurement | `playrec`, `measureLoopbackLatency`, start/stop/pause/resume |
**`audiodevreset`** is fine to call — it resets the audio subsystem and can help recover from device errors regardless of which API you use.
**When `sound`/`soundsc` is acceptable:** Only for a single isolated playback with no sequencing. If you need to play two clips back-to-back (e.g., before/after comparison), use `audiostreamer` — its `play()` calls queue automatically.
### Migration Pitfalls (audioDeviceReader/Writer → audiostreamer)
| Legacy | audiostreamer | Gotcha |
|--------|--------------|--------|
| `audioDeviceReader` with `NumChannels=2` | `RecorderChannels=1:2` | audiostreamer records **1 channel by default**. You must set `RecorderChannels` explicitly for stereo/multichannel. |
| `audioDeviceWriter` returns underrun count per frame | `getUnderrunCount(as)` after loop | `play()` has no return value. Call `getUnderrunCount` when you need the total — it resets the counter each call. |
| `audioDeviceReader` returns overrun count per frame | No equivalent needed | audiostreamer buffers all recorded samples internally — recorder cannot overrun. |
| `Device='Default'` | Omit `Player`/`Recorder` | No "Default" string — omitting the property selects the system default for the current driver. |
| `[data, nOverrun] = deviceReader()` | `record(as)` then `data = read(as, N)` | Must call `record(as)` (or `start(as)` [R2026a+]) before the loop — otherwise `read` blocks forever waiting for samples. |
| Two separate objects for reader+writer | Single `audiostreamer(Mode="full-duplex")` | One object handles both directions. Use two separate objects if devices require different drivers or conflict when opened together. |
## Diagnostics
For debugging streaming issues, enable the diagnostic trace:
```matlab
as = audiostreamer(Mode="full-duplex", SampleRate=44100);
as.TraceEnabled = true; % logs internal timing and buffer state
```
----
Copyright 2026 The MathWorks, Inc.
----
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!