Matlab Play Record Audio
MathWorksOfficialCreated by MathWorks
- Skill
- Tool integration
- Science and research
- BSD-2-Clause
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The audiostreamer skill provides a comprehensive reference for the unified MATLAB audio device I/O interface introduced in Audio Toolbox R2025a. It replaces legacy objects like audioDeviceWriter, audioDeviceReader, and audioPlayerRecorder, offering a single object for player, recorder, or full-duplex modes. The skill covers construction, property configuration, callback management, transport control, and diagnostic methods, ensuring users can implement audio streaming, measurement, and processing workflows effectively.
What it does
- Provides configuration for audio playback, recording, and full-duplex I/O.
- Details callback mechanisms for buffer management and event handling.
- Explains transport control methods including start, stop, pause, and resume.
- Lists static methods for enumerating available audio drivers and devices.
- Outlines diagnostic tools for monitoring stream time and underrun counts.
- Documents migration patterns from legacy MATLAB audio APIs.
When to use it
- When implementing audio playback or recording in MATLAB R2025a or later.
- When performing full-duplex audio I/O for measurements or loopback tests.
- When building callback-driven audio processing loops.
- When selecting specific audio drivers like ASIO or WASAPI.
- When migrating existing code from legacy audioDeviceReader or audioDeviceWriter.
Advantages
- Unified interface for all audio device I/O modes.
- Supports callback-based streaming for responsive applications.
- Enables repeatable latency for measurement workflows.
- Provides built-in diagnostic and device enumeration capabilities.
- Allows for explicit control over device buffers and channel mapping.
Usage examples
- “How do I initialize a full-duplex audiostreamer for 48kHz recording and playback?”
- Use as = audiostreamer(Mode='full-duplex', SampleRate=48000) to initialize the object for simultaneous I/O.
- “How can I retrieve a list of available audio devices in MATLAB?”
- Call audiostreamer.getAudioDevices() to return a struct array containing device names, drivers, and channel capabilities.
- “What is the correct way to ensure playback finishes before releasing the device?”
- Call waitfor(as) to block until the output buffer is empty, followed by release(as) to close the device.
- “How do I handle audio data in a loop using audiostreamer?”
- Call start(as) before the loop, then use read(as, frameLength) to retrieve input samples and write(as, output) to send processed data to the output buffer.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/matlab/matlab-play-record-audio/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip matlab-play-record-audio.zip -d .agents/skills
Documentation by MathWorks
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 —
soundorsoundscis fine for one-shot playback of a short clip with no sequencing. For sequential playback (e.g., before/after comparison), useaudiostreamer— itsplay()calls queue automatically, whereas overlappingsoundcalls play simultaneously.Code generation (codegen) —
audiostreamerdoes not yet support codegen; use legacy APIs if targeting codegenSimulink models — Simulink still uses the existing audio I/O blocks, not
audiostreamerAudio Toolbox not available — fall back to
sound/soundsc,audioplayer/audiorecorder, oraudioDeviceWriter(DSP System Toolbox) if the user lacks Audio ToolboxFile I/O only — reading/writing audio files without device playback or recording uses
audioread/audiowrite, not this skillDAQ hardware — National Instruments or similar data acquisition devices use DAQ Toolbox and the
daqobjectMIDI-only devices — MIDI control uses
mididevice/midicontrols, notaudiostreamer
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.
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.NumPlayerSamplesRecorderFcn:event.Type = "Recorder",event.NumRecorderSamplesPlayerCompletedFcn:event.Type = "PlayerCompleted",event.StreamTimeRecorderCompletedFcn:event.Type = "RecorderCompleted",event.StreamTimePlayerUnderrunFcn: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
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)
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
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.
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
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)
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.
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)
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
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:
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
endPattern 9: Full-Duplex with write/start for Control — R2026a+
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.
waitfor(as); % ensure all queued audio finishes playing
release(as);In apps, wrap in try-catch and nil the reference:
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:
as = audiostreamer(Mode="full-duplex", SampleRate=44100);
as.TraceEnabled = true; % logs internal timing and buffer stateCopyright 2026 The MathWorks, Inc.
Files
- ATTRIBUTION.mdAdded by AutoGineer1.0 KiB
- LICENSELicence file added by AutoGineer1.4 KiB
- SKILL.md20.8 KiB
- manifest.yaml446 B
LICENSE
Copyright 2026 The MathWorks, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. In all cases, the software is, and all modifications and derivatives of the software shall be, licensed to you solely for use in conjunction with MathWorks products and service offerings. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- MathWorks
- Publisher
- MathWorks (Official)
- Source
- matlab/matlab-agentic-toolkit · skills-catalog/signal-processing/matlab-play-record-audio · commit 2a7f215
- Licence
- BSD 2-Clause License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by MathWorks or MathWorks.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download matlab-play-record-audio.zip