WPF应用中使用微软标准组件实现16位PCM音频录播的技术问询
在WPF中实现16-bit PCM音频录制与播放的微软标准方案
你之前的判断完全没错——SoundPlayer、MediaElement和MediaPlayer确实不适合直接处理实时16-bit PCM字节数组块的录制与播放,它们更偏向于处理现成的音频文件,而非实时的原始音频数据。不过微软有专门的标准API能搞定你的需求,主要是基于Windows Runtime的媒体捕获与播放组件,WPF应用可以直接调用这些API,下面给你详细拆解实现思路:
一、录制16-bit PCM音频到字节数组块
我们可以用Windows.Media.Capture.MediaCapture这个类(属于Windows Runtime API,WPF项目里只要添加对Windows.winmd的引用就能用)来实现麦克风录制,只要把捕获格式配置成16-bit PCM,就能把捕获到的音频转成字节数组块。
核心实现步骤:
- 先初始化
MediaCapture,同时请求麦克风权限(这个是必须的,不然没法访问麦克风)。 - 配置音频捕获的参数:指定采样率(比如常用的44100Hz)、通道数(单声道/立体声)、位深(16-bit),确保是标准的PCM格式。
- 订阅音频数据捕获的事件,在回调里把原始的音频帧转换成16-bit PCM字节数组块,这样你就能拿到需要的实时数据了。
关键代码片段:
using Windows.Media.Capture; using Windows.Media.MediaProperties; using Windows.Storage.Streams; private MediaCapture _mediaCapture; private bool _isRecording = false; public async Task InitializeCaptureAsync() { _mediaCapture = new MediaCapture(); var settings = new MediaCaptureInitializationSettings { StreamingCaptureMode = StreamingCaptureMode.Audio, AudioDeviceId = MediaCapture.GetDefaultAudioCaptureId(AudioDeviceRole.Default) }; await _mediaCapture.InitializeAsync(settings); // 配置16-bit PCM格式:44100Hz采样率、单声道、16位深 var audioEncodingProperties = AudioEncodingProperties.CreatePcm(44100, 1, 16); var effectDefinitions = MediaExtensionManager.GetAudioEffectDefinitions("AudioCaptureEffect"); if (effectDefinitions.Any()) _mediaCapture.AddAudioEffect(effectDefinitions[0]); // 启动音频帧读取器并订阅帧到达事件 var reader = await _mediaCapture.CreateFrameReaderAsync(MediaFrameSourceKind.Audio); reader.FrameArrived += OnAudioFrameArrived; await reader.StartAsync(); _isRecording = true; } private void OnAudioFrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args) { using (var frame = sender.TryAcquireLatestFrame()) { if (frame == null || !_isRecording) return; using (var audioBuffer = frame.AudioMediaFrame.Buffer) { byte[] pcmBytes = new byte[audioBuffer.Length]; using (var stream = audioBuffer.AsStream()) { stream.Read(pcmBytes, 0, pcmBytes.Length); } // 在这里处理你的PCM字节数组块——比如缓存、传输或者其他业务逻辑 } } }
二、播放16-bit PCM字节数组块
播放这块可以用Windows.Media.Playback.AudioGraph类,它专门用来处理实时音频流的播放,能直接把PCM字节数组块喂进去播放,完美匹配你的需求。
核心实现步骤:
- 初始化
AudioGraph,把输出设备设成默认扬声器。 - 创建一个
AudioFrameInputNode,这个节点就是用来注入PCM数据的入口。 - 把16-bit PCM字节数组转换成
AudioFrame,然后提交到AudioFrameInputNode,就能直接在扬声器播放了。
关键代码片段:
using Windows.Media.Playback; using Windows.Media.Render; private AudioGraph _audioGraph; private AudioFrameInputNode _frameInputNode; public async Task InitializePlaybackAsync() { // 初始化AudioGraph var settings = new AudioGraphSettings(AudioRenderCategory.Media); var creationResult = await AudioGraph.CreateAsync(settings); if (creationResult.Status != AudioGraphCreationStatus.Success) { // 这里处理初始化失败的情况,比如提示用户音频设备不可用 return; } _audioGraph = creationResult.Graph; // 创建默认扬声器的输出节点 var outputNodeResult = await _audioGraph.CreateDeviceOutputNodeAsync(); if (outputNodeResult.Status != AudioDeviceNodeCreationStatus.Success) { // 处理输出节点创建失败 return; } // 配置输入格式,必须和录制的PCM格式完全一致 var inputFormat = AudioEncodingProperties.CreatePcm(44100, 1, 16); _frameInputNode = _audioGraph.CreateFrameInputNode(inputFormat); _frameInputNode.AddOutgoingConnection(outputNodeResult.DeviceOutputNode); // 启动音频图开始播放 _audioGraph.Start(); } // 播放PCM字节数组块的方法 public void PlayPcmByteArray(byte[] pcmBytes) { if (_frameInputNode == null || !_audioGraph.IsStarted) return; // 创建AudioFrame并填充PCM数据 var audioFrame = new AudioFrame((uint)pcmBytes.Length); using (var buffer = audioFrame.LockBuffer(AudioBufferAccessMode.Write)) using (var stream = buffer.CreateStream()) { stream.Write(pcmBytes, 0, pcmBytes.Length); } // 提交音频帧到输入节点,开始播放 _frameInputNode.AddFrame(audioFrame); }
几个要注意的点
- 确保你的WPF项目目标框架是.NET 6及以上,这样对Windows Runtime API的兼容性更好。
- 如果是打包成MSIX的WPF应用,要在
Package.appxmanifest里声明麦克风权限;如果是未打包的应用,要在运行时动态请求麦克风访问权限。 - 录制和播放的音频参数必须完全一致(采样率、通道数、位深),不然播放的时候会出现杂音或者根本不出声的情况。
内容的提问来源于stack exchange,提问作者Nicolai
相关产品推荐
相关产品推荐

