Xamarin C#安卓开发:如何实现高质量音频录制与实时放大播放?
Xamarin安卓实时音频录播:音质优化与音频放大解决方案
我是一名使用Xamarin和C#进行安卓开发的新手,尝试录制高质量音频并实时放大播放。目前已通过AudioRecord和AudioTrack实现录播功能,但在音质优化和音频放大方面遇到问题,附上相关代码,恳请指导解决方案。
原代码:
private void RecordAndPlayback() { int recordingSampleRate = AudioTrack.GetNativeOutputSampleRate(Stream.Music); recordingSampleRate = System.Math.Max(recordingSampleRate, instance.SelectedSamplingRate); bufferSize = AudioRecord.GetMinBufferSize(recordingSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit); buffer = new byte[bufferSize]; audioRecord = new AudioRecord(AudioSource.Mic, recordingSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize); audioTrack = new AudioTrack(Stream.Music, recordingSampleRate, ChannelOut.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize, AudioTrackMode.Stream); audioTrack.SetPlaybackRate(recordingSampleRate); audioRecord.StartRecording(); audioTrack.Play(); while (isListening) { audioRecord.Read(buffer, 0, buffer.Length); audioTrack.Write(buffer, 0, buffer.Length); } }
一、音质优化方案
- 换用优化音频源:将
AudioSource.Mic替换为AudioSource.VoiceCommunication,该源自带降噪、回声消除等适配实时语音场景的优化,能直接提升基础音质。 - 调整缓冲大小:
GetMinBufferSize返回的是系统要求的最低缓冲值,实际使用2-3倍的大小(比如bufferSize = AudioRecord.GetMinBufferSize(...) * 2),可以减少卡顿、爆音等问题,同时保证录制与播放的缓冲尺寸一致。 - 校验采样率兼容性:不要直接取最大采样率,优先用设备支持的标准采样率(44100Hz或48000Hz),可以先检查自定义采样率是否被支持,不支持则 fallback 到系统默认值。
- 后台线程执行:直接在主线程运行录播逻辑会阻塞UI甚至触发ANR,把整个录播逻辑放到后台线程,比如用
Task.Run包裹执行。 - 处理有效数据:读取音频时判断
audioRecord.Read的返回值,只有返回正数(实际读取到的字节数)才写入AudioTrack,避免无效数据导致杂音。
二、音频放大实现
因为使用的是16位PCM编码,每个音频样本是short类型(范围-32768到32767),需要将字节数组转换为短整型数组,乘以增益系数后再转回字节数组,同时做限幅处理防止失真:
- 定义增益系数(比如
float gain = 2.0f,可根据实际需求调整) - 将字节数组转换为短整型数组(注意安卓为小端字节序)
- 对每个样本乘以增益,超出范围的截断到最大值/最小值
- 转换回字节数组后写入AudioTrack
优化后的完整代码示例
private AudioRecord audioRecord; private AudioTrack audioTrack; private byte[] buffer; private int bufferSize; private bool isListening; // 可调整的增益系数 private float gain = 2.5f; public void StartRecordAndPlayback() { // 后台线程执行,避免阻塞UI Task.Run(() => RecordAndPlaybackInternal()); } private void RecordAndPlaybackInternal() { int targetSampleRate = instance.SelectedSamplingRate; // 校验采样率是否支持,不支持则用系统默认值 if (!IsSampleRateSupported(targetSampleRate)) { targetSampleRate = AudioTrack.GetNativeOutputSampleRate(Stream.Music); } // 用2倍最小缓冲提升稳定性 bufferSize = AudioRecord.GetMinBufferSize(targetSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit) * 2; buffer = new byte[bufferSize]; // 使用带优化的语音源 audioRecord = new AudioRecord(AudioSource.VoiceCommunication, targetSampleRate, ChannelIn.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize); audioTrack = new AudioTrack(Stream.Music, targetSampleRate, ChannelOut.Stereo, Android.Media.Encoding.Pcm16bit, bufferSize, AudioTrackMode.Stream); audioRecord.StartRecording(); audioTrack.Play(); isListening = true; while (isListening) { int bytesRead = audioRecord.Read(buffer, 0, buffer.Length); if (bytesRead > 0) { // 应用音频增益 ApplyGain(buffer, bytesRead); audioTrack.Write(buffer, 0, bytesRead); } // 短暂休眠降低CPU占用 Thread.Sleep(10); } // 停止并释放资源,避免内存泄漏 audioRecord.Stop(); audioTrack.Stop(); audioRecord.Release(); audioTrack.Release(); } private void ApplyGain(byte[] audioBytes, int length) { // 16位PCM每2字节对应一个short样本 int sampleCount = length / 2; short[] samples = new short[sampleCount]; Buffer.BlockCopy(audioBytes, 0, samples, 0, length); for (int i = 0; i < sampleCount; i++) { // 应用增益 float amplified = samples[i] * gain; // 限幅处理,防止超出short范围导致失真 if (amplified > short.MaxValue) samples[i] = short.MaxValue; else if (amplified < short.MinValue) samples[i] = short.MinValue; else samples[i] = (short)amplified; } // 转换回字节数组 Buffer.BlockCopy(samples, 0, audioBytes, 0, length); } // 检查采样率是否被当前设备支持 private bool IsSampleRateSupported(int sampleRate) { try { int minBufferSize = AudioRecord.GetMinBufferSize(sampleRate, ChannelIn.Mono, Android.Media.Encoding.Pcm16bit); return minBufferSize != AudioRecord.ErrorBadValue; } catch { return false; } }
额外注意事项
- 权限申请:确保在
AndroidManifest.xml中添加RECORD_AUDIO和MODIFY_AUDIO_SETTINGS权限,并且在运行时动态申请RECORD_AUDIO权限。 - 增益测试:增益过高会导致音频失真,建议从1.5f开始逐步测试,找到适合的数值。
- 资源释放:停止录播时必须调用
Release()释放AudioRecord和AudioTrack资源,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Shrishail V H
相关产品推荐
相关产品推荐

