三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

SimpleAudioPlayer 使用手册(四):录制功能详解

SimpleAudioPlayer 使用手册(四):录制功能详解

目录

  1. 概述

  2. AudioRecorder 快速入门

  3. 输出格式详解

  4. 录制到流

  5. 停止录制与统计信息

  6. 模拟录制模式

  7. 完整示例:录音并回放

  8. 录制过程中的错误处理

  9. 平台注意事项

  10. 进阶用法


概述

AudioRecorder 是 SimpleAudioPlayer 提供的录音组件,与 AudioPlayer 同属 SimpleAudio 命名空间。它使用本机音频 API 从麦克风或其他输入设备捕获音频数据,支持多种输出格式。

using SimpleAudioPlayer;// 创建录音机
using var recorder = new AudioRecorder();// 开始录制到文件
recorder.Start(@"C:\Recordings\test.wav");// 停止录制
recorder.Stop();

核心功能:

  • 支持 WAV、AAC (ADTS)、M4A、Raw PCM 四种输出格式

  • 输出到文件或任意 Stream

  • 录制统计信息(帧数、丢帧数)

  • 模拟录制模式(测试用,不依赖麦克风硬件)

  • 跨平台兼容


AudioRecorder 快速入门

录制到 WAV 文件

WAV 是最简单、兼容性最好的格式,不需要编码器,所有平台都支持:

using SimpleAudioPlayer;using var recorder = new AudioRecorder();Console.WriteLine("开始录音... 按 Enter 停止");
recorder.Start(@"C:\Recordings\my_audio.wav");
Console.ReadLine();
recorder.Stop();Console.WriteLine("录音已保存到 C:\\Recordings\\my_audio.wav");

录制参数: Start(filename) 使用默认参数(44.1kHz, 16-bit, mono)。

录制到 AAC 文件

AAC 格式需要编码器,文件体积更小:

using SimpleAudioPlayer;using var recorder = new AudioRecorder();recorder.Start(@"C:\Recordings\my_audio.aac");
Console.WriteLine("正在录制 AAC...");
Thread.Sleep(5000); // 录制 5 秒
recorder.Stop();Console.WriteLine("录音完成");

输出格式详解

设置输出格式

// 设置格式后再开始录制
recorder.OutputFormat = RecordingFileFormat.Wav;   // WAV
recorder.OutputFormat = RecordingFileFormat.Aac;   // AAC (ADTS)
recorder.OutputFormat = RecordingFileFormat.M4A;   // M4A (AAC in MP4)
recorder.OutputFormat = RecordingFileFormat.Pcm; // Raw PCM

格式对比

格式

文件扩展名

有损/无损

编码器需求

兼容性

文件大小

适用场景

WAV

.wav

无损 (PCM)

极高

录音编辑、存档

AAC (ADTS)

.aac

有损

FFmpeg

语音笔记、播客

M4A

.m4a

有损

FFmpeg

移动设备分享

Raw PCM

.raw

无损 (PCM)

低(无头)

自定义处理

设置采样率和声道

// 录制前配置参数
recorder.SampleRate = 48000;   // 48kHz
recorder.Channels = 2;         // 立体声
recorder.BitDepth = 16;        // 16-bitrecorder.OutputFormat = RecordingFileFormat.Wav;
recorder.Start(@"C:\Recordings\high_quality.wav");

推荐的录音配置:

场景

采样率

声道

位深

格式

语音笔记

16000

Mono

16

WAV 或 AAC

播客录制

44100

Mono

16

WAV

音乐录制

48000

Stereo

24

WAV

移动分享

44100

Stereo

16

M4A

语音识别

16000

Mono

16

WAV

自定义后处理

48000

Stereo

24

Raw PCM


录制到流

除了保存到文件,还可以录制到任意 Stream 对象:

using SimpleAudioPlayer;using var outputStream = new MemoryStream();
using var recorder = new AudioRecorder();// 录制到流(需要指定格式)
recorder.OutputFormat = RecordingFileFormat.Wav;
recorder.Start(outputStream, RecordingFileFormat.Wav);Console.WriteLine("正在录音到内存流... 按 Enter 停止");
Console.ReadLine();recorder.Stop();// 此时 outputStream 包含完整的 WAV 文件数据
Console.WriteLine($"录制了 {outputStream.Length} 字节");// 可以保存或进一步处理
await File.WriteAllBytesAsync(@"C:\Recordings\from_stream.wav", outputStream.ToArray());

流的优势:

  • 录制到 MemoryStream 直接用于播放或上传

  • 录制到 FileStream 实现自定义文件名逻辑

  • 录制到 NetworkStream 实时传输音频

  • 录制到 CryptoStream 加密保存

上传到服务器的示例

using SimpleAudioPlayer;// 在内存中录制
using var audioStream = new MemoryStream();
using var recorder = new AudioRecorder();recorder.OutputFormat = RecordingFileFormat.Aac;
recorder.Start(audioStream, RecordingFileFormat.Aac);Console.WriteLine("录音中... 按 Enter 停止并上传");
Console.ReadLine();recorder.Stop();// 上传到服务器
audioStream.Position = 0;
using var httpClient = new HttpClient();
using var content = new StreamContent(audioStream);
content.Headers.ContentType = new MediaTypeHeaderValue("audio/aac");var response = await httpClient.PostAsync("https://api.example.com/upload", content);
Console.WriteLine($"上传结果: {response.StatusCode}");

停止录制与统计信息

Stop() 返回一个 MaResult 对象,包含录制统计信息:

using SimpleAudioPlayer;using var recorder = new AudioRecorder();recorder.Start(@"C:\Recordings\stats.wav");
Thread.Sleep(3000);
var result = recorder.Stop();Console.WriteLine($"录制统计:");
Console.WriteLine($"  采样率: {result.SampleRate} Hz");
Console.WriteLine($"  声道数: {result.Channels}");
Console.WriteLine($"  位深: {result.BitsPerSample}");
Console.WriteLine($"  总帧数: {result.TotalFrames}");
Console.WriteLine($"  丢帧数: {result.FramesDropped}");
Console.WriteLine($"  时长: {result.Duration}");if (result.FramesDropped > 0)
{double dropRate = (double)result.FramesDropped / result.TotalFrames * 100;Console.WriteLine($"  ⚠ 注意: 丢帧率 {dropRate:F2}%,可能存在录音质量问题");
}

MaResult 属性

属性

类型

说明

SampleRate

int

录制采样率 (Hz)

Channels

int

声道数

BitsPerSample

int

位深

TotalFrames

long

采集的 PCM 帧总数

FramesDropped

int

丢帧数(因缓冲区溢出丢失的帧)

Duration

TimeSpan

录制时长

FilePath

string?

输出文件路径(如果录制到文件)


模拟录制模式

模拟录制模式不需要麦克风硬件,在测试环境中非常有用。它生成静音 PCM 数据,让你可以在没有音频输入设备的机器上测试录制流程。

using SimpleAudioPlayer;// 启用模拟录制模式(必须在 Start 之前设置)
AudioRecorder.SimulateRecording = true;using var recorder = new AudioRecorder();recorder.OutputFormat = RecordingFileFormat.Wav;
recorder.Start(@"C:\Recordings\test_simulated.wav");Thread.Sleep(3000);var result = recorder.Stop();
Console.WriteLine($"模拟录制完成,时长: {result.Duration}");// 测试完成后关闭模拟模式
AudioRecorder.SimulateRecording = false;

模拟录制适用场景:

  • CI/CD 流水线中测试录音功能

  • 没有麦克风的开发环境

  • 录制功能的单元测试

  • UI 自动化测试

在测试中使用

[Fact]
public void TestRecordingAndPlayback()
{// 启用模拟模式AudioRecorder.SimulateRecording = true;var recorder = new AudioRecorder();var player = new AudioPlayer();using var audioStream = new MemoryStream();// 模拟录制 2 秒recorder.OutputFormat = RecordingFileFormat.Wav;recorder.Start(audioStream, RecordingFileFormat.Wav);Thread.Sleep(2000);recorder.Stop();// 验证录制结果var result = recorder.Stop();Assert.True(result.TotalFrames > 0);Assert.Equal(0, result.FramesDropped);// 回放录制的音频audioStream.Position = 0;var handle = new StreamHandle(audioStream);player.Load(handle);player.Play();AudioRecorder.SimulateRecording = false;
}

完整示例:录音并回放

以下程序录制一段音频、保存到文件、然后立即回放:

using SimpleAudioPlayer;Console.WriteLine("SimpleAudioPlayer 录音与回放示例");
Console.WriteLine("====================================");
Console.WriteLine();// 选择输出格式
Console.WriteLine("选择输出格式:");
Console.WriteLine("  1. WAV (无损)");
Console.WriteLine("  2. AAC (有损,体积小)");
Console.WriteLine("  3. M4A (AAC in MP4)");
Console.Write("请选择 (1-3) [默认 1]: ");var formatChoice = Console.ReadKey(true);
AudioOutputFormat format = RecordingFileFormat.Wav;
string extension = ".wav";switch (formatChoice.KeyChar)
{case '2':format = RecordingFileFormat.Aac;extension = ".aac";Console.WriteLine("AAC 格式");break;case '3':format = RecordingFileFormat.M4A;extension = ".m4a";Console.WriteLine("M4A 格式");break;default:Console.WriteLine("WAV 格式");break;
}// 选择录音配置
Console.WriteLine();
Console.WriteLine("选择录音质量:");
Console.WriteLine("  1. 语音 (16kHz, Mono, 16-bit)");
Console.WriteLine("  2. 标准 (44.1kHz, Mono, 16-bit)");
Console.WriteLine("  3. 高保真 (48kHz, Stereo, 24-bit)");
Console.Write("请选择 (1-3) [默认 2]: ");var qualityChoice = Console.ReadKey(true);
int sampleRate = 44100;
int channels = 1;
int bitDepth = 16;switch (qualityChoice.KeyChar)
{case '1':sampleRate = 16000;Console.WriteLine("语音质量");break;case '3':sampleRate = 48000;channels = 2;bitDepth = 24;Console.WriteLine("高保真质量");break;default:Console.WriteLine("标准质量");break;
}// 生成输出文件名
string outputDir = @"C:\Recordings";
Directory.CreateDirectory(outputDir);
string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string outputPath = Path.Combine(outputDir, $"recording_{timestamp}{extension}");Console.WriteLine();
Console.WriteLine($"输出文件: {outputPath}");
Console.WriteLine($"格式: {format}, 采样率: {sampleRate}Hz, 声道: {(channels == 1 ? "Mono" : "Stereo")}, 位深: {bitDepth}");
Console.WriteLine();
Console.WriteLine("准备就绪!按 Enter 开始录音...");
Console.ReadLine();// 创建录音机并配置
using var recorder = new AudioRecorder();
recorder.SampleRate = sampleRate;
recorder.Channels = channels;
recorder.BitDepth = bitDepth;
recorder.OutputFormat = format;// 开始录音
Console.WriteLine("正在录音... 按 Enter 停止");
recorder.Start(outputPath);// 显示录音时长
var startTime = DateTime.Now;
var cts = new CancellationTokenSource();_ = Task.Run(async () =>
{while (!cts.IsCancellationRequested){var elapsed = DateTime.Now - startTime;Console.Write($"\r录音中... {elapsed:mm\\:ss\\.ff}");await Task.Delay(100);}
});Console.ReadLine();
cts.Cancel();// 停止录音
var result = recorder.Stop();
Console.WriteLine();
Console.WriteLine("=== 录音统计 ===");
Console.WriteLine($"  时长: {result.Duration}");
Console.WriteLine($"  总帧数: {result.TotalFrames:N0}");
Console.WriteLine($"  丢帧数: {result.FramesDropped:N0}");
Console.WriteLine($"  文件大小: {new FileInfo(outputPath).Length / 1024.0:F1} KB");
Console.WriteLine();// 询问是否回放
Console.Write("是否立即回放?(Y/N) [默认 Y]: ");
var playKey = Console.ReadKey(true);
Console.WriteLine();if (playKey.KeyChar != 'n' && playKey.KeyChar != 'N')
{Console.WriteLine("正在回放录音...");using var player = new AudioPlayer();player.PlaybackStateChanged += (s, e) =>{if (e.NewState == PlaybackState.Completed){Console.WriteLine("回放完成");}};player.PlayCompleted += (s, e) =>{Console.WriteLine("播放完毕");};try{player.Load(outputPath);Console.WriteLine($"音频时长: {player.Duration}");player.Play();Console.WriteLine("按 Enter 停止回放");Console.ReadLine();player.Stop();}catch (Exception ex){Console.WriteLine($"回放失败: {ex.Message}");}
}Console.WriteLine("程序结束。");

录制过程中的错误处理

常见异常及处理

using SimpleAudioPlayer;using var recorder = new AudioRecorder();try
{recorder.Start(@"C:\Recordings\test.wav");// ... 录制逻辑recorder.Stop();
}
catch (InvalidOperationException ex)
{// 已在录制中 / 未初始化Console.WriteLine($"录制状态错误: {ex.Message}");
}
catch (UnauthorizedAccessException ex)
{// 没有麦克风权限Console.WriteLine($"权限不足: {ex.Message}");Console.WriteLine("请在系统设置中授予麦克风权限。");
}
catch (NotSupportedException ex)
{// 不支持当前配置(如不支持的采样率)Console.WriteLine($"不支持的配置: {ex.Message}");
}
catch (Exception ex)
{Console.WriteLine($"录制失败: {ex.Message}");
}

权限检查

不同平台对麦克风权限的处理不同:

// 尝试录制一小段来检测权限
public static async Task<bool> CheckMicrophonePermissionAsync()
{try{AudioRecorder.SimulateRecording = false;using var recorder = new AudioRecorder();using var stream = new MemoryStream();recorder.OutputFormat = RecordingFileFormat.Wav;recorder.Start(stream, RecordingFileFormat.Wav);await Task.Delay(500);recorder.Stop();return stream.Length > 44; // 有效的 WAV 文件至少有 44 字节头}catch{return false;}
}

丢帧处理

丢帧通常表示缓冲区太小或 CPU 负载过高:

using var recorder = new AudioRecorder();// 增大缓冲区可以降低丢帧率
// (具体 API 取决于版本实现)
recorder.Start(@"C:\Recordings\test.wav");
Thread.Sleep(5000);
var result = recorder.Stop();if (result.FramesDropped > 0)
{double dropRate = (double)result.FramesDropped / result.TotalFrames * 100;if (dropRate > 5){Console.WriteLine($"警告:丢帧率 {dropRate:F1}%,建议降低采样率或升级硬件。");}
}

平台注意事项

Windows

  • 使用 WASAPI 捕获设备

  • 默认使用默认输入设备(可在系统设置中更改)

  • 需要麦克风权限(Windows 10/11 隐私设置)

  • 独占模式下可能影响其他应用的录音能力

配置默认输入设备:

// Windows 上选择特定输入设备(如果 API 支持)
// recorder.InputDeviceId = "device_id"; // 取决于具体版本实现

Linux

  • 使用 ALSA 或 PulseAudio 进行捕获

  • PulseAudio 默认提供混音和采样率转换

  • 需要 pulseaudio-utilsalsa-utils

  • 可能需要将用户加入 audio

# Linux 上可能需要的配置
sudo usermod -a -G audio $USER

macOS

  • 使用 CoreAudio 进行音频捕获

  • 需要在 Info.plist 中声明麦克风权限

  • macOS 10.14+ 要求麦克风权限,系统会弹出授权对话框

  • 权限可在「系统偏好设置 → 安全性与隐私 → 麦克风」中管理

Info.plist 配置:

<key>NSMicrophoneUsageDescription</key>
<string>此应用需要访问麦克风以录制音频。</string>

Android

  • Android 6.0+ (API 23+) 需要运行时权限

  • Android 10+ (API 29+) 使用 AAudio 提供低延迟录制

  • 部分设备可能不支持某些采样率

Android 权限请求:

// Xamarin.Android / .NET MAUI 中的权限请求
var status = await Permissions.RequestAsync<Permissions.Microphone>();
if (status != PermissionStatus.Granted)
{Console.WriteLine("未获得麦克风权限");return;
}

进阶用法

分段录制

using SimpleAudioPlayer;using var recorder = new AudioRecorder();
recorder.OutputFormat = RecordingFileFormat.Wav;for (int i = 1; i <= 3; i++)
{string fileName = $"C:\\Recordings\\segment_{i}.wav";Console.WriteLine($"录制第 {i} 段...");recorder.Start(fileName);Thread.Sleep(3000);recorder.Stop();Console.WriteLine($"第 {i} 段已保存: {fileName}");
}

检测是否正在录制

if (recorder.IsRecording)
{Console.WriteLine("正在录制中...");var result = recorder.Stop();
}

Raw PCM 后处理

录制为 Raw PCM 后可以自定义处理:

recorder.OutputFormat = RecordingFileFormat.Pcm;
recorder.Start(@"C:\Recordings\raw_output.pcm");Thread.Sleep(3000);var result = recorder.Stop();Console.WriteLine($"Raw PCM 数据: {result.TotalFrames} 帧");
Console.WriteLine($"参数: {result.SampleRate}Hz, {result.Channels}ch, {result.BitsPerSample}bit");// Raw PCM 需要知道参数才能正确播放或转换
// 可使用外部工具(如 ffmpeg)转换为 WAV
// ffmpeg -f s16le -ar 44100 -ac 1 -i raw_output.pcm output.wav

通过本文的学习,你应该能够使用 AudioRecorder 完成各种录音任务。下一章将对比 SimpleAudioPlayer 与其他音频库的优劣,帮助你在项目中做出最佳选择。

← 返回列表