- 示例工程
【免费下载链接】Windows-universal-samples
API samples for the Universal Windows Platform.
本指南以 Windows-universal-samples 仓库中的 AudioCreation 示例(Samples/AudioCreation/README.md)为核心,系统讲解 UWP 平台下基于Windows.Media.Audio命名空间的音频图(Audio Graph)编程模型。你将掌握音频图的节点拓扑、量子(Quantum)机制、六种典型场景(文件播放、设备采集、自定义帧输入、子混音、系统内置效果、自定义效果)的完整实现路径,并能直接在 Visual Studio 中构建与运行该示例验证所学。
音频图:把音频处理拆成一张"节点-连接"的数据流图
AudioCreation 示例的核心价值在于演示Windows.Media.Audio命名空间。音频图本质上是一组互相连接的音频节点,音频数据在其中流动,构成一条可编程的数据流管线:
- 音频输入节点(Input Node):向图中供给音频数据,来源可以是音频输入设备、音频文件,也可以是自定义代码;
- 音频输出节点(Output Node):图中音频处理结果的最终去向,可路由到音频输出设备、音频文件,或自定义代码;
- 子混音节点(Submix Node):接收一个或多个节点的输出,将其合并为单一输出后再路由给图中的其他节点。
当所有节点创建完毕、连接关系建立后,只需启动音频图(graph.Start()),数据便会自动从输入节点出发,流经子混音节点,最终抵达输出节点。正是这种模型,让"从麦克风录音到文件""从文件播放到扬声器""多路音频混合"等场景的实现变得非常简洁——这也是 AudioCreation 示例要演示的核心模式。
在节点之上还有一层**音频效果(Audio Effect)**机制:图中每个节点都可以挂载零个或多个效果,对流经该节点的音频做实时处理。平台内置了回声(Echo)、均衡器(Equalizer)、限制器(Limiter)、混响(Reverb)等效果,仅需几行代码即可挂载;同时开发者也能实现与内置效果完全一致的自定义音频效果。
示例的项目结构
整个示例位于 Samples/AudioCreation,采用 C#(UWP)实现,包含两个工程:
- 主程序AudioCreation(AudioCreation.csproj):包含六个场景页面,每个场景由 XAML 布局与 code-behind 组成,并通过 AudioCreation.sln 组织构建;
- 自定义效果工程CustomEffect(CustomEffect.csproj):独立程序集,仅含一个实现
IBasicAudioEffect接口的回声效果类(CustomEffect.cs),供场景六引用。
场景一:文件播放(File Playback)——最基础的图拓扑
该场景演示如何用音频图播放本地音频文件,操作路径为:点击Load File弹出文件选择器(支持 .mp3、.wav、.wma、.m4a),选中文件后点击Start Graph开始播放;Loop开关控制循环播放;Playback Speed滑杆调节播放速度。
节点搭建与连接
从代码实现(Scenario1_FilePlayback.xaml.cs)可见,图的构建分两步:
- 创建图与默认输出设备节点。使用
AudioGraphSettings指定渲染类别(示例使用AudioRenderCategory.Media),调用AudioGraph.CreateAsync(settings)异步创建;随后通过graph.CreateDeviceOutputNodeAsync()创建代表默认输出设备的AudioDeviceOutputNode:
AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media); CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings); if (result.Status != AudioGraphCreationStatus.Success) { /* 处理创建失败 */ } graph = result.Graph; CreateAudioDeviceOutputNodeResult deviceOutputNodeResult = await graph.CreateDeviceOutputNodeAsync(); deviceOutput = deviceOutputNodeResult.DeviceOutputNode;- 创建文件输入节点并建立连接。文件经
FileOpenPicker选取后,通过graph.CreateFileInputNodeAsync(file)生成AudioFileInputNode,再调用fileInput.AddOutgoingConnection(deviceOutput)把数据接到输出节点。示例对创建结果做了两类防御性校验:AudioFileNodeCreationStatus.Success用于判断文件是否可读;文件时长不足 3 秒(fileInput.Duration <= TimeSpan.FromSeconds(3))则提示换文件。
裁剪、循环与变速
该场景还展示了三个高频实用能力,均在 Scenario1_FilePlayback.xaml.cs 中:
- 裁剪播放区间:
fileInput.StartTime = TimeSpan.FromSeconds(3)让文件从第 3 秒开始播放(注释指出EndTime可用于从文件尾部裁剪); - 循环播放:
fileInput.LoopCount = null表示无限循环;设为0停止循环;设非零值则为有限次循环。切换 Loop 开关时还会处理"文件已播完"的边界情况——先Seek回 StartTime 再开启循环; - 变速播放:
fileInput.PlaybackSpeedFactor = playSpeedSlider.Value,XAML 中滑杆取值范围为 0.5~3.0(见 Scenario1_FilePlayback.xaml)。
页面导航离开时调用graph.Dispose()释放整个图,这是所有场景共用的资源清理模式。
场景二:设备采集(Device Capture)——低延迟录音 + 监听
该场景把麦克风输入同时路由到两个输出:一个用于监听(输出设备),一个用于录音(音频文件),并特别采用LowestLatency量子模式。操作流程:先在下拉列表选择输入/输出设备 →Pick Output File选择录音目标文件 →Create Graph建图 →Record开始录制 →Stop结束并写盘。
低延迟配置与设备选择
实现见 Scenario2_DeviceCapture.xaml.cs,关键配置集中在CreateAudioGraph():
AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media); settings.QuantumSizeSelectionMode = QuantumSizeSelectionMode.LowestLatency; settings.PrimaryRenderDevice = outputDevices[outputDevicesListBox.SelectedIndex - 1];- QuantumSizeSelectionMode.LowestLatency:量子(quantum)指音频图单次处理的音频数据量。设为最低延迟意味着音频处理延迟尽可能小,代价是设备断连等异常必须显式处理——示例为此订阅了
graph.UnrecoverableErrorOccurred事件,在设备出错时释放旧图、重新枚举设备并复位 UI; - 设备枚举:通过
DeviceInformation.FindAllAsync(MediaDevice.GetAudioRenderSelector())与MediaDevice.GetAudioCaptureSelector()分别枚举输出与输入设备,填入两个 ListBox; - 输入节点绑定设备:
graph.CreateDeviceInputNodeAsync(MediaCategory.Other, graph.EncodingProperties, device)中传入graph.EncodingProperties,使输入节点以图自身的编码格式工作。
一分二的路由与文件落盘
SelectOutputFile()中先由FileSavePicker选择目标文件(提供 PCM/WMA/MP3 三种格式选择),按扩展名构造对应的MediaEncodingProfile(MediaEncodingProfile.CreateWav / CreateWma / CreateMp3,均使用AudioEncodingQuality.High),再创建AudioFileOutputNode:
CreateAudioFileOutputNodeResult fileOutputNodeResult = await graph.CreateFileOutputNodeAsync(file, fileProfile); // 输入节点同时接到两个输出节点:监听 + 录音 deviceInputNode.AddOutgoingConnection(fileOutputNode); deviceInputNode.AddOutgoingConnection(deviceOutputNode);录音停止时,示例先graph.Stop()防止数据丢失,再调用await fileOutputNode.FinalizeAsync()完成文件收尾;返回值TranscodeFailureReason为None才表示写盘成功。
场景三:帧输入节点(Frame Input Node)——用自定义代码合成音频
该场景演示如何从自定义代码生成 PCM 数据并注入音频图:点击Generate Audio开始播放由代码实时合成的 1kHz 正弦波,Stop停止。实现位于 Scenario3_FrameInputNode.xaml.cs。
建图与节点创建
AudioFrameInputNode创建时显式复用了图的编码属性并把声道数设为单声道:
AudioEncodingProperties nodeEncodingProperties = graph.EncodingProperties; nodeEncodingProperties.ChannelCount = 1; frameInputNode = graph.CreateFrameInputNode(nodeEncodingProperties); frameInputNode.AddOutgoingConnection(deviceOutputNode); frameInputNode.Stop(); // 先处于停止态,由按钮控制 frameInputNode.QuantumStarted += node_QuantumStarted; graph.Start(); // 图启动,帧输入节点可独立启停QuantumStarted 驱动的数据供给
QuantumStarted事件在音频图需要更多数据时被触发,事件参数携带RequiredSamples(本次需要提供的样本数)。示例强调:按需提供恰好所需样本量即可,多余样本会引入额外延迟。回调中生成AudioFrame并调用frameInputNode.AddFrame(audioData)提交给图消费:
private void node_QuantumStarted(AudioFrameInputNode sender, FrameInputNodeQuantumStartedEventArgs args) { uint numSamplesNeeded = (uint)args.RequiredSamples; if (numSamplesNeeded != 0) { AudioFrame audioData = GenerateAudioData(numSamplesNeeded); frameInputNode.AddFrame(audioData); } }用 unsafe 代码写底层缓冲区
生成正弦波需要直接写入AudioFrame底层内存,示例通过 COM 接口IMemoryBufferByteAccess(Guid5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D)拿到字节指针后强转为float*写入。GenerateAudioData()中缓冲区大小为samples * sizeof(float)(单声道),采样率取自graph.EncodingProperties.SampleRate,以amplitude * sin(theta)逐样本填充 1kHz、幅度 0.3 的正弦波。这要求工程开启AllowUnsafeBlocks(AudioCreation.csproj 中所有配置均设为true)。
场景四:子混音节点(Submix Nodes)——两路混音 + 回声效果
该场景把两个音频文件混音,并在混音输出上挂载回声效果再送往扬声器:Load File 1 / Load File 2选文件,Start Graph启动,Echo开关实时切换效果。实现见 Scenario4_Submix.xaml.cs。
拓扑与增益控制
图包含四个节点:两个AudioFileInputNode、一个AudioSubmixNode、一个AudioDeviceOutputNode。值得注意的实现细节:
- 防削波增益:两路文件同时播放可能削波,因此连接时显式设置增益为 0.5——
fileInputNode1.AddOutgoingConnection(submixNode, 0.5),第二个文件同理; - 子混音输出再衰减:
submixNode.OutgoingGain = 0.5,进一步压低混音输出的响度,代码注释指出所有节点都具备OutgoingGain属性。
回声效果定义与动态开关
echoEffect = new EchoEffectDefinition(graph); echoEffect.WetDryMix = 0.7f; // 湿/干信号混合比例 echoEffect.Feedback = 0.5f; // 反馈量 echoEffect.Delay = 500.0f; // 延迟毫秒数 submixNode.EffectDefinitions.Add(echoEffect); submixNode.DisableEffectsByDefinition(echoEffect); // 初始关闭,由 UI 开关控制开关切换时调用submixNode.EnableEffectsByDefinition(echoEffect)/DisableEffectsByDefinition(echoEffect)。注释明确指出:这一效果模型被所有音频节点类型实现,因此效果可以挂在图中任意位置;子混音节点还可以链式串联,轻松构造多层效果叠加的复杂混音。
场景五:系统内置效果(In-box Effects)——Echo / Reverb / Equalizer / Limiter
该场景集中演示平台内置的四种效果(Echo、Reverb、Equalizer、Limiter),UI 允许加载音频文件后逐个开关并实时调节参数。代码位于 Scenario5_InboxEffects.xaml.cs,拓扑极简:AudioFileInputNode→AudioDeviceOutputNode,四个效果定义全部加入文件输入节点的EffectDefinitions列表,初始均通过DisableEffectsByDefinition关闭。
Echo(回声)
EchoEffectDefinition使用与场景四相同的三个参数(WetDryMix=0.7、Feedback=0.5、Delay=500ms)。UI 中 Delay 滑杆范围 50~2000ms(Scenario5_InboxEffects.xaml),拖动时实时写回echoEffectDefinition.Delay。
Reverb(混响)
ReverbEffectDefinition的初始化参数完整可见:
| 参数 | 初始值 | 说明 |
|---|---|---|
| WetDryMix | 50 | 湿/干混合 |
| ReflectionsDelay | 120 | 反射延迟 |
| ReverbDelay | 30 | 混响延迟 |
| RearDelay | 3 | 后置延迟 |
| DecayTime | 2 | 衰减时间(秒) |
UI 只暴露 DecayTime 一个滑杆,范围 0.5~20 秒,回调中实时写入reverbEffectDefinition.DecayTime。
Limiter(限制器)
LimiterEffectDefinition初始Loudness = 1000、Release = 10;UI 滑杆范围 1~1800,用于实时调节响度值。
Equalizer(均衡器)
EqualizerEffectDefinition内置 4 个频段(Bands[0..3]),示例设定的中心频率与增益为:100Hz(4.033)、900Hz(1.6888)、5kHz(2.4702)、12kHz(5.5958),带宽 1.5~2.0。UI 四个垂直滑杆取值 0~100,代码通过ConvertRange()将其线性映射到 xapofx.h 中 FXEQ 的增益范围(fxeq_min_gain = 0.126到fxeq_max_gain = 7.94),再写入eqEffectDefinition.Bands[i].Gain。
场景六:自定义效果(Custom Effects)——实现 IBasicAudioEffect 并接入图
该场景演示如何编写自定义音频效果并接入音频图:Load File选文件后Start Graph,播放时即应用自定义回声效果。核心代码全部封装在独立工程 CustomEffect 的 CustomEffect.cs 中,主工程通过工程引用(见 AudioCreation.csproj 的ProjectReference)接入。
实现 IBasicAudioEffect 的四个关键成员
AudioEchoEffect类实现了IBasicAudioEffect接口,需覆盖以下成员:
- SupportedEncodingProperties:声明支持的编码格式。构造函数中注册了 44.1kHz 与 48kHz 的单声道 32 位浮点 PCM(
AudioEncodingProperties.CreatePcm(44100, 1, 32),Subtype = MediaEncodingSubtypes.Float); - SetEncodingProperties:系统选定编码后调用。示例按
encodingProperties.SampleRate分配回声缓冲数组echoBuffer,实现恰好 1 秒的延迟线,并记录当前写入游标; - ProcessFrame:音频图每帧调用,入参
ProcessAudioFrameContext同时提供输入帧与输出帧。处理逻辑是经典回声算法——inputData = inputDataInFloat[i] * (1 - Mix),echoData = echoBuffer[当前游标] * Mix,输出为两者之和,再把原始输入写回缓冲、游标循环回绕;同样通过IMemoryBufferByteAccess以unsafe指针方式读写帧缓冲区; - SetProperties / DiscardQueuedFrames / Close:前两者分别用于接收属性集配置与清空缓冲(
Array.Clear回声缓冲并重置游标),Close用于释放资源。
属性集驱动的 Mix 参数
效果暴露一个Mix属性,不设 C# 直接 setter,所有更新必须经属性集完成:SetProperties保存传入的IPropertySet,读取时用TryGetValue("Mix", out val)取值(默认 0.5f)。主工程挂载时正是通过属性集注入初值:
PropertySet echoProperties = new PropertySet(); echoProperties.Add("Mix", 0.5f); AudioEffectDefinition echoEffectDefinition = new AudioEffectDefinition(typeof(AudioEchoEffect).FullName, echoProperties); fileInputNode.EffectDefinitions.Add(echoEffectDefinition);场景六还演示了AudioFileInputNode.FileCompleted事件:文件播完后停止图、调用sender.Reset()复位节点,使再次启动可从文件头重新播放。
系统要求与构建运行
系统要求(按原文档声明):Client 端 Windows 10,Phone 端 Windows 10。工程 AudioCreation.csproj 的TargetPlatformVersion/TargetPlatformMinVersion均为 10.0.22621.0,并依赖Microsoft.NETCore.UniversalWindowsPlatform5.0.0 包。
构建步骤:
- 若以 ZIP 方式下载整个样本集,务必解压整个归档,而不仅是本示例目录——因为工程通过
$(SharedContentDir)引用了仓库根目录 SharedContent 下的共享代码(App.xaml.cs、MainPage.xaml.cs、样式与 SDK 媒体资源等,见 AudioCreation.csproj 的编译项); - 启动 Visual Studio,选择File > Open > Project/Solution;
- 从解压目录进入
Samples/AudioCreation/cs,双击 AudioCreation.sln 打开解决方案(本示例提供 C# 版本;仓库中大量其他示例同时提供 cpp / cppwinrt / vb 等多语言版本,此处以 C# 为准); - 按 Ctrl+Shift+B 或选择Build > Build Solution完成构建。
运行步骤:
- 仅部署:选择Build > Deploy Solution;
- 部署并调试运行:按 F5 或Debug > Start Debugging;不调试直接运行:按 Ctrl+F5 或Debug > Start Without Debugging。
小结
AudioCreation 示例以六个递进的场景完整覆盖了音频图编程的核心能力:从最基础的文件播放图拓扑,到低延迟设备采集、自定义帧数据注入、子混音链路、内置效果体系,直至自研IBasicAudioEffect效果的接入与参数化。结合 README.md 的流程描述与 cs/AudioCreation 下各场景的 code-behind 源码,你可以把它当作一套可运行的音频处理实验台:改一改节点连接、调一调效果参数、或替换GenerateAudioData中的波形算法,即可快速验证路由、混音与处理类音频方案在 UWP 上的落地方式。
- 示例工程
【免费下载链接】Windows-universal-samples
API samples for the Universal Windows Platform.
相关推荐
TiXL 音频系统完全指南:时间线音频剪辑、路由混音图与音频反应式视觉
TiXL 音频系统完全指南:时间线音频剪辑、路由混音图与音频反应式视觉 本篇技术指南围绕 TiXL(开源实时动态图形工具)的音频子系统展开,系统讲解时间线音频剪
音视频图形学桌面应用vscode-cpptools调试器扩展:内存断点完全指南
vscode cpptools调试器扩展:内存断点完全指南 内存断点(Memory Breakpoint)核心痛点解析 在C/C++开发中,开发者常面临内存相关
音频实战精通企业微信自动化:用WorkTool打造你的智能办公助手
实战精通企业微信自动化:用WorkTool打造你的智能办公助手 想象一下,每天早晨你还在通勤路上,团队的工作汇报已经自动发送到企业微信群;重要客户的消息总能第一
GUI 自动化即时通讯RPA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考