mcpField
M
matlab-play-record-audio

matlab-play-record-audio

github.com/matlab创意与媒体1.2k52 次安装1 天前更新
代码仓库

关于 matlab-play-record-audio

面向 MATLAB 用户的音频设备 I/O 技能,指导使用 audiostreamer 播放、录音及同步全双工采集,并涵盖回调和设备诊断。需要 Audio Toolbox R2025a 或更高版本。

主要功能

  1. 01播放、录音与全双工模式
  2. 02回调驱动的音频流
  3. 03预缓冲与传输控制
  4. 04音频设备枚举与选择
  5. 05延迟和欠载诊断

适用场景

  1. 01通过声卡播放音频
  2. 02从麦克风采集音频
  3. 03执行回环或声学测量
  4. 04检查可用音频设备

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-play-record-audio

安装后在 Claude Code 中输入/matlab-play-record-audio确认已加载。

附带文件manifest.yaml

详细介绍

audiostreamer — MATLAB 音频设备 I/O(R2025a+)

audiostreamer 是 audioDeviceWriter、audioDeviceReader 和 audioPlayerRecorder 的统一替代方案。它提供仅播放、仅录制或全双工模式,并支持回调、预缓冲、传输控制和测量辅助功能。

版本要求: audiostreamer 需要 Audio Toolbox R2025a 或更高版本。start 和 write 方法于 R2026a 中新增。

适用场景

  • 通过声卡或 USB 音频设备播放音频
  • 从麦克风或音频接口录制音频
  • 全双工播放和录制(例如,声学测量、回环测试)
  • 列出或选择音频设备和驱动程序
  • 在 MATLAB 中涉及音频硬件 I/O 的任何工作流

不适用场景

  • 播放单个独立声音 — 对于无需排序、仅播放一次的短音频片段,sound 或 soundsc 即可。对于顺序播放(例如,前后对比),请使用 audiostreamer — 它的 play() 调用会自动排队,而重叠调用 sound 会同时播放。
  • 代码生成(codegen) — audiostreamer 目前尚不支持 codegen;如果目标是 codegen,请使用旧版 API
  • Simulink 模型 — Simulink 仍使用现有音频 I/O 模块,而非 audiostreamer
  • Audio Toolbox 不可用 — 如果用户没有 Audio Toolbox,请改用 sound/soundsc、audioplayer/audiorecorder,或 audioDeviceWriter(DSP System Toolbox)
  • 仅文件 I/O — 无需设备播放或录制的音频文件读写应使用 audioread/audiowrite,而非此工具
  • DAQ 硬件 — National Instruments 或类似数据采集设备使用 DAQ Toolbox 和 daq 对象
  • 仅限 MIDI 的设备 — MIDI 控制使用 mididevice/midicontrols,而非 audiostreamer

构造

对 Mode 和 SampleRate 使用名称-值对(也支持位置参数简写,但不支持自动补全):

默认 Mode 为 "player"。如果要录制,必须显式设置 Mode — 设置为 Mode="recorder" 或 Mode="full-duplex"。系统不会根据 Recorder 或 RecorderChannels 等其他属性推断 Mode。

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])

属性

设备配置(开始流式传输前设置)
属性类型默认值说明
Mode"player" / "recorder" / "full-duplex""player"在构造时或通过属性设置
Driver"DirectSound" / "ASIO" / "WASAPI"(Win);"CoreAudio"(Mac);"ALSA"(Linux)OS 默认值仅在 Windows 上设置(Mac/Linux 各只有一个驱动程序)。构造时设置会选择该驱动程序的默认设备。
Player字符串该驱动程序的系统默认设备输出设备名称。省略时使用所选驱动程序的默认设备。
Recorder字符串该驱动程序的系统默认设备输入设备名称。省略时使用所选驱动程序的默认设备。
SampleRate正标量44100Hz
DeviceBufferSize正整数或 "auto""auto"对 ASIO 固定(使用 asiosettings)。
DeviceBitFormat"single" / "int24" / "int16""int24"ASIO 上的 int16 会静默使用 int24
PlayerChannels行向量或 "auto""auto"从 1 开始的通道映射。"auto" 会将单声道升混为立体声;对于 N≥2 个通道,会在设备上打开 N 个通道
RecorderChannels行向量1从 1 开始的通道映射。默认录制 1 个通道 — 例如,设置为 1:2 可录制立体声
ExclusiveMode开/关"on"仅限 WASAPI — 禁用操作系统混音/重采样
ConstantLatency"off" / "dropPlayer" / "dropRecorder""off"全双工丢帧处理

重要: 开始流式传输后,Mode、SampleRate、Driver、DeviceBufferSize、DeviceBitFormat、ExclusiveMode、ConstantLatency、PlayerChannels 和 RecorderChannels 将被锁定。更改这些属性前,请调用 release(as),以避免系统自动释放并发出警告。

回调属性
属性签名触发条件
PlayerFcn@(obj, event)Player 缓冲区低于 PlayerMinSamples
PlayerMinSamples正整数(默认值为 16384)PlayerFcn 的触发阈值
RecorderFcn@(obj, event)Recorder 缓冲区超过 RecorderMinSamples
RecorderMinSamples正整数(默认值为 1024)RecorderFcn 的触发阈值
PlayerCompletedFcn@(obj, event)输出队列为空
RecorderCompletedFcn@(obj, event)定长录音完成
PlayerUnderrunFcn@(obj, event)Player 发生欠载

所有回调都必须恰好接受 2 个参数。 第 1 个参数是 audiostreamer 对象。第 2 个参数是带有 .Type 字段的事件结构体。如果不需要事件,请使用 @(obj, ~)。

不同类型的事件结构体字段:

  • 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
只读状态
属性说明
NumPlayerSamples输出缓冲区中当前排队的采样数
NumRecorderSamples无需阻塞即可通过 read() 读取的采样数
MaxPlayerChannels所选设备支持的最大输出通道数
MaxRecorderChannels所选设备支持的最大输入通道数

方法

播放
方法说明
play(obj, x)将 x 加入队列并播放。会阻塞,直到输出缓冲区 ≤ PlayerMinSamples(返回时最多还有 PlayerMinSamples 个采样尚未播放)。最后一次调用 play 后,请调用 waitfor(as),以确保在 release 前完成播放。
play(obj, x, "non-blocking")将 x 加入队列后立即返回,不受缓冲区大小影响。
play(obj)启动 PlayerFcn 回调循环(不带数据参数)。
write(obj, x)[R2026a+] 将 x 加入输出缓冲区,但不启动播放。与 start() 配合使用。
write(obj, x, "non-blocking")将 x 加入队列后立即返回,不受缓冲区大小影响。[R2026a+]
录音
方法说明
record(obj)无限期开始录音。如果缓冲区中还有未读取的采样,则发出警告。若要避免此情况,请调用 stop(as)(全双工模式下调用 stop(as, "recorder")),然后调用 read(as) 清空缓冲区。如果采样已由回调或 read 读取,则无需执行此操作。
record(obj, numSamples)录制恰好 numSamples 个采样,然后停止。此方法同样会对未读取的采样发出警告。
read(obj)立即返回所有可用的录音采样(非阻塞)。如果没有可用采样,则返回空值。
read(obj, numSamples)会阻塞,直到有 numSamples 个采样可用,然后返回这些采样。
全双工
方法说明
playrec(obj, x)同时播放 x 并录音。非阻塞——录音会在后台继续;使用 read 获取数据。
playrec(obj, x, numSamples)播放 x 并录制 numSamples 个采样。阻塞——返回录制的矩阵。
playrec(obj)启动由回调驱动的全双工模式(需要 RecorderFcn 和/或 PlayerFcn)。

playrec 会暂停 Player 和 Recorder,将音频加入队列,然后同时恢复二者,以实现可重复的延迟。这对使用 impzest 进行测量至关重要。

传输控制
方法描述
start(obj)[R2026a+] 以当前模式开始流式传输。
start(obj, Mode="player")[R2026a+] 仅启动播放器(全双工)。
start(obj, Mode="recorder", SamplesToRecord=N)[R2026a+] 启动录音器并录制固定数量的样本。
stop(obj)停止所有流式传输。保留尚未读取的输入样本。重置欠载计数(getUnderrunCount 也会重置)。
stop(obj, "player"/"recorder"/"both")停止指定的一侧。
pause(obj) / pause(obj, "player"/"recorder"/"both")暂停并保留状态。
resume(obj) / resume(obj, "player"/"recorder"/"both")从暂停状态恢复。
waitfor(obj) / waitfor(obj, "player"/"recorder"/"both")阻塞,直到操作完成。
release(obj)停止、刷新、关闭设备并拆除。删除尚未读取的样本。
查询 / 诊断
方法描述
isPlaying(obj)返回 OnOffSwitchState
isRecording(obj)返回 OnOffSwitchState
isPlayerPaused(obj)返回 OnOffSwitchState
isRecorderPaused(obj)返回 OnOffSwitchState
getUnderrunCount(obj)返回自上次调用以来的欠载样本数。重置计数器(stop 也会重置)。
getStreamTime(obj)流式传输经过的时间(秒)。
getStreamTime(obj, "reset")重置流计时器。
measureLoopbackLatency(obj)仅适用于全双工和单声道。返回延迟的样本数。
静态设备枚举
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)

注意: getAudioDevices() 返回的 MaxRecorderChannels 和 MaxPlayerChannels 类型为 int32。在 UI 组件(例如 uispinner 的 Limits)中使用这些值,或用于要求 double 的算术运算之前,请先转换为 double()。

重要:没有 setup() 方法

audiostreamer 没有公开的 setup() 方法。设备会在首次调用 play()、record()、playrec() 或 start() 时隐式初始化。请勿调用 setup(),否则会报错。

如果设置了 PlayerFcn,首次调用流式传输方法时会反复调用它,以便在设备打开前预缓冲至少 8192 个样本(或 PlayerMinSamples 指定的数量,取较大者)。

常见模式

模式 1:简单的阻塞测量(扫频 + 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);
模式 2:非阻塞播放 + 录音,并使用 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);
模式 3:预缓冲播放(write + start)— R2026a+

如果需要精确控制播放开始时间(例如,同步启动全双工),请使用 write+start。对于简单播放,play(as, signal) 效果相同——它会将数据排队并自动启动。

as = audiostreamer(Mode="player", SampleRate=48000);
write(as, signal);       % queue without starting
start(as);               % begin playback
waitfor(as);             % wait for completion
release(as);
模式 4:由回调驱动的流式播放器
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);
模式 5:由回调驱动的电平计量(录音器)
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);
模式 6:逐帧处理循环(全双工)

这是替代旧版 audioDeviceReader/audioDeviceWriter 循环最直接的方式。在循环前调用 start(as),确保 read 有可用样本。

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);

在 R2026a 之前,请使用 record(as) + play(as, out),而非 start/write。

仅使用播放器时(例如,文件输入 → 设备输出),调用 play(as, out) 即可,无需调用 start——play 会将数据排队并自动启动。

模式 7:使用回调进行重复测量(全双工)
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);
模式 8:使用基于计时器的 GUI 更新的应用
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);

在计时器回调中,在执行耗时的 GUI 操作前检查缓冲区状态:

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
模式 9:使用 write/start 控制全双工 — 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);

拆解最佳实践

release(as) 已足够——它会隐式停止流传输、刷新缓冲区并关闭设备。无需先调用 stop。不过,release 会丢弃尚未播放的样本——如果必须等播放完成,请先调用 waitfor(as)。

waitfor(as);   % ensure all queued audio finishes playing
release(as);

在应用中,请用 try-catch 包装并将引用设为 nil:

try
    release(as);
catch
end
as = [];

析构函数会自动调用 release(),但在应用中,最好显式清理,以避免设备锁死。在 PlayerCompletedFcn 中调用 release 是安全的,不会导致死锁。

注意: 即使释放后,isvalid(as) 仍会返回 true——不能用它来检测 audiostreamer 是否已释放。要跟踪释放状态,请将对象引用设为 nil,并使用 isempty 检查。

ConstantLatency 模式(全双工)

值行为适用场景
"off"发生丢帧后插入静音帧(延迟增加)常规用途
"dropPlayer"丢弃迟到的输出帧;延迟保持恒定使用 impzest 的测量(基于扫频)
"dropRecorder"丢弃输入帧;延迟保持恒定自适应滤波器(与 impzest 不兼容)

错误情况

错误 ID原因
audio:device:methodRequiresModes调用的方法对当前 Mode 无效(例如,在播放器模式下调用 record())。如果需要同时使用播放和录制方法,请将 Mode 设为 "full-duplex"。
audio:device:invalidChannelMap通道索引超出设备最大值。请检查 MaxPlayerChannels 或 MaxRecorderChannels,并调整映射。
audio:device:callbackNargin回调未接受恰好 2 个参数。请使用 @(obj, ~) 或 @(obj, event) 签名。
audio:device:playrecRecorderFcnConflict设置了 RecorderFcn 时调用 playrec 并提供输出参数——回调通过 read() 消费样本,导致返回值没有可用数据。在阻塞式 playrec 前,请清除 RecorderFcn。
audio:device:startModePlayerNotValid在仅录制模式下调用 start(Mode="player")
audio:device:startModeRecorderNotValid在仅播放模式下调用 start(Mode="recorder")
MATLAB:validators:mustBeFinite音频数据包含 NaN 或 Inf
MATLAB:validators:mustBeReal音频数据为复数

audiostreamer 与旧版音频 API 的对比

只要 Audio Toolbox 可用,强烈建议使用 audiostreamer 进行所有音频设备 I/O。未安装 Audio Toolbox 或某些特殊情况下,旧版替代方案可能适合作为备用方案。

旧版 API局限性audiostreamer 对应方案
audiodevinfo不支持 ASIO;设备列表不完整audiostreamer.getAudioDevices()、audiostreamer.getPlayerNames() 等
audioplayer / audiorecorder不支持 ASIO/WASAPI 独占模式;驱动程序模型有限;没有回调使用播放器/录制器/全双工模式的 audiostreamer
sound / soundsc底层会创建 audioplayer;并发调用会重叠(不要排队)使用 play() 顺序播放的 audiostreamer
audioDeviceWriter / audioDeviceReader对象分离;没有回调;没有预缓冲;只能逐帧循环单个 audiostreamer 对象,支持阻塞/非阻塞模式
audioPlayerRecorder全双工能力有限;没有传输控制;无法测量延迟playrec、measureLoopbackLatency、start/stop/pause/resume

可以调用 audiodevreset——它会重置音频子系统,无论使用哪种 API,都有助于从设备错误中恢复。

适合使用 sound/soundsc 的情况: 仅限单次独立播放,且不需要编排播放顺序。如果需要连续播放两个片段(例如进行前后对比),请使用 audiostreamer——它的 play() 调用会自动排队。

迁移注意事项(audioDeviceReader/Writer → audiostreamer)
旧版audiostreamer注意事项
audioDeviceReader with NumChannels=2RecorderChannels=1:2audiostreamer 默认录制 1 个声道。要录制立体声或多声道,必须显式设置 RecorderChannels。
audioDeviceWriter returns underrun count per framegetUnderrunCount(as) after loopplay() 没有返回值。需要获取总数时,请调用 getUnderrunCount——每次调用都会重置计数器。
audioDeviceReader returns overrun count per frameNo equivalent neededaudiostreamer 会在内部缓冲所有录制的样本,因此录音器不会发生溢出。
Device='Default'Omit Player/Recorder没有 "Default" 字符串——省略该属性会为当前驱动选择系统默认设备。
[data, nOverrun] = deviceReader()record(as) then data = read(as, N)必须在循环前调用 record(as)(或 start(as) [R2026a+]),否则 read 会因等待样本而无限阻塞。
两个独立对象分别用于读取和写入audiostreamer(Mode="full-duplex") 单个对象一个对象可处理两个方向。如果设备需要不同的驱动,或同时打开时会发生冲突,请使用两个独立对象。

诊断

要调试流式处理问题,请启用诊断跟踪:

as = audiostreamer(Mode="full-duplex", SampleRate=44100);
as.TraceEnabled = true;   % logs internal timing and buffer state

版权所有 2026 The MathWorks, Inc.


评论

支持 Markdown